Sujet : Re: (interposers): How to workaround the "strong symbols" problem?
De : invalid (at) *nospam* invalid.invalid (Richard Kettlewell)
Groupes : comp.unix.programmerDate : 18. Jan 2025, 00:10:10
Autres entêtes
Organisation : terraraq NNTP server
Message-ID : <wwved112g19.fsf@LkoBDZeT.terraraq.uk>
References : 1
User-Agent : Gnus/5.13 (Gnus v5.13) Emacs/28.2 (gnu/linux)
gazelle@shell.xmission.com (Kenny McCormack) writes:
Context is Linux (and only Linux).
Glibc? If so what distribution and what version?
Over the years, I have written many "interposers" - that is, a shared
library loaded with LD_PRELOAD that hooks some system or library call
(e.g., "read"). The interposer usually ends up calling the "real"
function, then doing something special either before or after the
call.
>
Generally, it all works fine - or at least, it did - until they
started having "strong symbols" (I think that's the right term).
I think you mean STB_WEAK vs STB_GLOBAL, in the naming used in Glibc and
the ELF spec.
Anyway, some number of years back, I noticed that it became kind of
hit and miss as to whether or not you could get your hooked version of
the function to be called. Generally, it seemed, the more "low level"
the function, the less likely it was that the interposer would work.
>
So, I am wondering, is there a fix for this? I'm assuming that
somebody decided that interposers were evil and thus, they came up
with this as a way to foil us, but there should be fix to the fix, so
to speak. Is there?
I can interpose both weak and global symbols from Debian’s Glibc 2.39
without doing anything unusual.
Note: I am not showing code at the moment, because I'd like (if
possible) a simple "Yes, it can be done" or "No, they got you" type
answer. If there is sufficient interest, I can post code in a
followup.
Yes, it can be done.
$ make check
gcc -Wall -Wextra -Wno-unused -Werror -o t.so -shared t.c -ldl -lc
gcc -Wall -Wextra -Wno-unused -Werror -o u u.c
readelf -Ts /lib/x86_64-linux-gnu/libc.so.6 | grep -wE 'open|openat|memfrob'
1701: 00000000000f7f30 296 FUNC WEAK DEFAULT 16 open@@GLIBC_2.2.5
2274: 00000000000f80c0 280 FUNC WEAK DEFAULT 16 openat@@GLIBC_2.4
3039: 000000000009bf40 30 FUNC GLOBAL DEFAULT 16 memfrob@@GLIBC_2.2.5
LD_PRELOAD=./t.so ./u memfrob
interposed memfrob
0x7ffc04c4560f
LD_PRELOAD=./t.so ./u open /dev/null
interposed open
3
LD_PRELOAD=./t.so ./u openat /dev/null
interposed openat
3
My interposing versions are not doing anything special. Representative
example:
void *memfrob(void *s, size_t n) {
void *(*real_memfrob)(void *s, size_t n);
real_memfrob = dlsym(RTLD_NEXT, "memfrob");
write(2, "interposed memfrob\n", 19);
return real_memfrob(s, n);
}
Something tha tripped me up: a program shows a call to openat() in
strace output, so one tries to interpose openat(). But in fact the
program is calling the Glibc open() function, which calls the openat()
syscall.
$ strace -etrace=open,openat cat /dev/null
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
openat(AT_FDCWD, "/dev/null", O_RDONLY) = 3
+++ exited with 0 +++
$ LD_PRELOAD=./t.so cat /dev/null
interposed open
However I think from your other post that you’ve already considered this
possibility.
-- https://www.greenend.org.uk/rjk/