]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
DEV: ncpu: make the wrapper work both as a lib and executable
authorWilly Tarreau <w@1wt.eu>
Wed, 8 Jan 2025 08:31:41 +0000 (09:31 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 8 Jan 2025 10:27:10 +0000 (11:27 +0100)
It's convenient to have a share lib be able to also work as a wrapper.
But recent glibc broke support for this dual-mode thing some time ago:

   https://patchwork.ozlabs.org/project/glibc/patch/20190312130235.8E82C89CE49C@oldenburg2.str.redhat.com/
   https://stackoverflow.com/questions/59074126/loading-executable-or-executing-a-library

Trying to preload such an executable indeed returns:

   ERROR: ld.so: object '/path/to/ncpu.so' from LD_PRELOAD cannot be preloaded (cannot dynamically load position-independent executable): ignored.

Note that the code still supports it since libc.so is both an executable
and a lib. The approach taken here is the same as in the nousr.so wrapper.
It consists in dropping the DF_1_PIE flag from the resulting executable
since it's what the dynamic linker is looking for. This flag is found in
FLAGS_1 in the .dynamic section. As readelf -a suggests, it's after the
tag 0x6ffffffb. The value is 0x08000000. We're using objdump to figure the
length and offset of the struct, dd to extract the 3 parts, and sed to
patch the binary.

It's likely that it will only work on 64-bit little endian, though tests
should be performed to see what to do on other platforms. At least on
x86_64, ld.so is happy and it continues to be possible to use the binary
as a .so, and that the platform where most of the development happens so
that's fine.

In any case the wrapper and the standard shared lib are still made two
distinct files so that it's possible to use the non-patched version on
unsupported OSes or architectures.

Makefile
dev/ncpu/Makefile

index f6cc556c0d34c6eb4490637227aa0529d6a0813d..9bf63a280d44a78a32cf35ba9d45bc7aa86afc0a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1022,7 +1022,7 @@ help:
 IGNORE_OPTS=help install install-man install-doc install-bin \
        uninstall clean tags cscope tar git-tar version update-version \
        opts reg-tests reg-tests-help admin/halog/halog dev/flags/flags \
-       dev/haring/haring dev/poll/poll dev/tcploop/tcploop
+       dev/haring/haring dev/ncpu/ncpu dev/poll/poll dev/tcploop/tcploop
 
 ifneq ($(TARGET),)
 ifeq ($(filter $(firstword $(MAKECMDGOALS)),$(IGNORE_OPTS)),)
@@ -1059,6 +1059,9 @@ dev/haring/haring: dev/haring/haring.o
 dev/hpack/%: dev/hpack/%.o
        $(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
 
+dev/ncpu/ncpu:
+       $(cmd_MAKE) -C dev/ncpu ncpu V='$(V)'
+
 dev/poll/poll:
        $(cmd_MAKE) -C dev/poll poll CC='$(CC)' OPTIMIZE='$(COPTS)' V='$(V)'
 
@@ -1072,7 +1075,7 @@ dev/udp/udp-perturb: dev/udp/udp-perturb.o
        $(cmd_LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@ $^ $(LDOPTS)
 
 # rebuild it every time
-.PHONY: src/version.c dev/poll/poll dev/tcploop/tcploop
+.PHONY: src/version.c dev/ncpu/ncpu dev/poll/poll dev/tcploop/tcploop
 
 src/calltrace.o: src/calltrace.c $(DEP)
        $(cmd_CC) $(TRACE_COPTS) -c -o $@ $<
@@ -1138,7 +1141,7 @@ clean:
 distclean: clean
        $(Q)rm -f admin/iprange/iprange admin/iprange/ip6range admin/halog/halog
        $(Q)rm -f admin/dyncookie/dyncookie
-       $(Q)rm -f dev/haring/haring dev/poll/poll dev/tcploop/tcploop
+       $(Q)rm -f dev/haring/haring dev/ncpu/ncpu{,.so} dev/poll/poll dev/tcploop/tcploop
        $(Q)rm -f dev/hpack/decode dev/hpack/gen-enc dev/hpack/gen-rht
        $(Q)rm -f dev/qpack/decode
 
index 2c1c42feb959f30aa367e08c2c64b32491c9a5d9..4307c99d4aaf34267c14380e4a9c1c76ce47c127 100644 (file)
@@ -4,12 +4,28 @@ CC       = cc
 OPTIMIZE = -O2 -g
 DEFINE   =
 INCLUDE  =
-OBJS     = ncpu.so
+OBJS     = ncpu.so ncpu
+OBJDUMP  = objdump
 
 all:   $(OBJS)
 
-%.so: %.c
-       $(cmd_CC) $(OPTIMIZE) $(DEFINE) $(INCLUDE) -fPIC -shared -o $@ $^
+%.o: %.c
+       $(cmd_CC) $(OPTIMIZE) $(DEFINE) $(INCLUDE) -shared -fPIC -c -o $@ $^
+
+%.so: %.o
+       $(cmd_CC) -pie -o $@ $^
+       $(Q)rm -f $^
+
+%: %.so
+       $(call qinfo, PATCHING)set -- $$($(OBJDUMP) -j .dynamic -h $^ | fgrep .dynamic); \
+         ofs=$$6; size=$$3; \
+         dd status=none bs=1 count=$$((0x$$ofs)) if=$^ of=$^-p1; \
+         dd status=none bs=1 skip=$$((0x$$ofs)) count=$$((0x$$size)) if=$^ of=$^-p2; \
+         dd status=none bs=1 skip=$$((0x$$ofs+0x$$size)) if=$^ of=$^-p3; \
+         sed -e 's,\xfb\xff\xff\x6f\x00\x00\x00\x00\x00\x00\x00\x08,\xfb\xff\xff\x6f\x00\x00\x00\x00\x00\x00\x00\x00,g' < $^-p2 > $^-p2-patched; \
+         cat $^-p1 $^-p2-patched $^-p3 > "$@"
+       $(Q)rm -f $^-p*
+       $(Q)chmod 755 "$@"
 
 clean:
-       rm -f $(OBJS) *.[oas] *~
+       rm -f $(OBJS) *.[oas] *.so-* *~