glibc_version_header icon indicating copy to clipboard operation
glibc_version_header copied to clipboard

Followed Readme, binary still contains GLIBC_2.33 funcs

Open TheBigS opened this issue 3 years ago • 8 comments

Just messing around with trying to learn how to compile compatible binaries. I'm working with socat for now:

$ unzip socat-master.zip
$ cd socat-master
$ ./configure
$ make
$ objdump -T socat | grep GLIBC_ | grep 2\.33
0000000000000000      DF *UND*	0000000000000000 (GLIBC_2.33) lstat64
0000000000000000      DF *UND*	0000000000000000 (GLIBC_2.33) stat64
0000000000000000      DF *UND*	0000000000000000 (GLIBC_2.33) lstat
0000000000000000      DF *UND*	0000000000000000 (GLIBC_2.33) stat
0000000000000000      DF *UND*	0000000000000000 (GLIBC_2.33) mknod
0000000000000000      DF *UND*	0000000000000000 (GLIBC_2.33) fstat64
0000000000000000      DF *UND*	0000000000000000 (GLIBC_2.33) fstat

And if I take the binary from my 22.04 system to an other 18.04 I get the error:

/tmp/socat: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.33' not found (required by /tmp/socat)

So I build with the provided header:

$ make clean
$ cp ../glibc_version_header-master/version_headers/x64/force_link_glibc_2.19.h .
$ export CFLAGS="-include force_link_glibc_2.19.h -static-libgcc"
$ ./configure
$ make
...
gcc -include force_link_glibc_2.19.h -static-libgcc -D_GNU_SOURCE -Wall -Wno-parentheses  -DHAVE_CONFIG_H -I.  -I.   -c -o xio-udp.o xio-udp.c
gcc -include force_link_glibc_2.19.h -static-libgcc -D_GNU_SOURCE -Wall -Wno-parentheses  -DHAVE_CONFIG_H -I.  -I.   -c -o xio-progcall.o xio-progcall.c
gcc -include force_link_glibc_2.19.h -static-libgcc -D_GNU_SOURCE -Wall -Wno-parentheses  -DHAVE_CONFIG_H -I.  -I.   -c -o xio-exec.o xio-exec.c
gcc -include force_link_glibc_2.19.h -static-libgcc -D_GNU_SOURCE -Wall -Wno-parentheses  -DHAVE_CONFIG_H -I.  -I.   -c -o xio-system.o xio-system.c
...

But my produced binary still has 2.33 symbols in it:

$ objdump -T socat | grep GLIBC_ | grep 2\.33
0000000000000000      DF *UND*	0000000000000000 (GLIBC_2.33) lstat64
0000000000000000      DF *UND*	0000000000000000 (GLIBC_2.33) stat64
0000000000000000      DF *UND*	0000000000000000 (GLIBC_2.33) lstat
0000000000000000      DF *UND*	0000000000000000 (GLIBC_2.33) stat
0000000000000000      DF *UND*	0000000000000000 (GLIBC_2.33) mknod
0000000000000000      DF *UND*	0000000000000000 (GLIBC_2.33) fstat64
0000000000000000      DF *UND*	0000000000000000 (GLIBC_2.33) fstat

What am I doing wrong?

TheBigS avatar Nov 12 '22 14:11 TheBigS

Until glibc 2.32, stat64 etc. were redirected to __xstat64 etc. by inline functions or macros; glibc 2.33 changed them to true functions. As a result, code compiled against glibc 2.32 or older will get the old __xstat64 etc. implementation that is still present, whereas code compiled against glibc 2.33 will get the new stat64 etc. implementation.

Maybe a workaround might be:

#define _GNU_SOURCE

#include <dlfcn.h>
#include <fcntl.h>

#define REDIRECT(RET, NAME, T2) \
RET \
NAME (const char *path, T2 A2) \
{ \
    RET (*_NAME) (const char *path, T2 A2); \
    RET result; \
    _NAME = (RET (*)(const char *path, T2 A2)) dlsym (RTLD_NEXT, #NAME); \
    result = _NAME (path, A2); \
    return result; \
}

REDIRECT(int, stat, struct stat *)
REDIRECT(int, stat64, struct stat64 *)
REDIRECT(int, lstat, struct stat *)
REDIRECT(int, lstat64, struct stat64 *)
REDIRECT(int, fstat, struct stat *)
REDIRECT(int, fstat64, struct stat64 *)

katzer avatar Dec 01 '22 16:12 katzer

Hmm I see what you are saying. Sounds like an older glibc on my host might fix it, I'll have to spin up a VM to try that. I tried adding the workaround to the force_link_glibc_2.19.h

Then I reconfigured with ./configure and attempted to make

First there are a ton of errors about redefining _GNU_SOURCE

<command-line>: note: this is the location of the previous definition
gcc -include force_link_glibc_2.19.h -static-libgcc -D_GNU_SOURCE -Wall -Wno-parentheses  -DHAVE_CONFIG_H -I.  -I.   -c -o xio-readline.o xio-readline.c
In file included from <command-line>:
./force_link_glibc_2.19.h:3729: warning: "_GNU_SOURCE" redefined
 3729 | #define _GNU_SOURCE

But its just a warning, so I pressed on. Unfortunately the workaround doesn't quite work:

...
gcc -include force_link_glibc_2.19.h -static-libgcc -D_GNU_SOURCE -Wall -Wno-parentheses  -DHAVE_CONFIG_H -I.   -o socat socat.o libxio.a -lrt -lutil 
/usr/bin/ld: libxio.a(xioinitialize.o): in function `stat':
xioinitialize.c:(.text+0x0): multiple definition of `stat'; socat.o:socat.c:(.text+0x0): first defined here
/usr/bin/ld: libxio.a(xioinitialize.o): in function `stat64':
xioinitialize.c:(.text+0x4a): multiple definition of `stat64'; socat.o:socat.c:(.text+0x4a): first defined here
/usr/bin/ld: libxio.a(xioinitialize.o): in function `lstat':
xioinitialize.c:(.text+0x94): multiple definition of `lstat'; socat.o:socat.c:(.text+0x94): first defined here
/usr/bin/ld: libxio.a(xioinitialize.o): in function `lstat64':
xioinitialize.c:(.text+0xde): multiple definition of `lstat64'; socat.o:socat.c:(.text+0xde): first defined here
/usr/bin/ld: libxio.a(xioinitialize.o): in function `fstat':
xioinitialize.c:(.text+0x128): multiple definition of `fstat'; socat.o:socat.c:(.text+0x128): first defined here
/usr/bin/ld: libxio.a(xioinitialize.o): in function `fstat64':
xioinitialize.c:(.text+0x172): multiple definition of `fstat64'; socat.o:socat.c:(.text+0x172): first defined here
   ... repeat for all the .c files that use these functions ...
collect2: error: ld returned 1 exit status
make: *** [Makefile:115: socat] Error 1

TheBigS avatar Dec 01 '22 23:12 TheBigS

I tried adding the workaround to the force_link_glibc_2.19.h

You have to embed the snippet into your own source code - somewhere at the very beginning. I tried it and it seemed to work. However I am not using it because with glibc_2.34 and newer the approach in general does not work anymore because of the new __libc_start_main symbol.

I would say don't waste your time trying to get it working with glibc_2.33 ;)

katzer avatar Dec 02 '22 14:12 katzer

Any update on this? This looked among the most promising approaches.

mcourteaux avatar Nov 23 '23 20:11 mcourteaux

I was having problem with stat() not being found in the older glibc. It seems that stat() was implemented as a C preprocessor macro, redirecting to __xlstat(), which is present in the older glibc. I'll keep trying to find a way around this issue.

mcourteaux avatar Nov 24 '23 14:11 mcourteaux

Update: I gave up and am now setting up compilation in a Docker container based on ubuntu:18.04, to get an old glibc. I'm using llvm with clang-17 to get a modern compiler. The LLVM compiler suite is godsent here, thanks to their ubuntu:18.04 apt repository for the latest clang version.

mcourteaux avatar Nov 25 '23 16:11 mcourteaux

I managed to redirect the *stat functions (this example is only for stat() and fstat()):

__asm__(".symver __fxstat, __fxstat@GLIBC_2.0");
__asm__(".symver __xstat, __xstat@GLIBC_2.0");

struct stat;
extern int __xstat(int, const char *, struct stat *);
static inline int stat(const char *path, struct stat *statbuf)
{ return __xstat(3, path, statbuf); }
extern int __fxstat(int, int, struct stat *);
static inline int fstat(int fd, struct stat *statbuf)
{ return __fxstat(3, fd, statbuf); }

But I'm indeed stuck on redirecting the __libc_start_main symbol to its older version. I could link in my own startfile, but I'd really rather not do that. I wish you could influence .symver logic from the linker script.

mid-kid avatar Jul 14 '24 14:07 mid-kid

Actually, figured out that part as well. This is now a program using stat(), compiled on a glibc 2.39 system, referencing only symbols in GLIBC_2.0:

// Needs to be included in every file that uses stat()
// May also be defined only once, but in such a case "static inline" must be removed
__asm__(".symver __xstat, __xstat@GLIBC_2.0");
struct stat;
extern int __xstat(int, const char *, struct stat *);
static inline int stat(const char *path, struct stat *statbuf)
{ return __xstat(3, path, statbuf); }

// Must be defined once in the entire program
__asm__(".symver old__libc_start_main, __libc_start_main@GLIBC_2.0");
__asm__(".symver new__libc_start_main, __libc_start_main@@_NEW");
__asm__(".global new__libc_start_main");
__asm__("new__libc_start_main: jmp old__libc_start_main@PLT");

#include <stdio.h>
#include <sys/stat.h>

int main(int argc, char *argv[])
{
    struct stat buf;
    stat(argv[0], &buf);
    printf("%lu\n", buf.st_ino);
}

Compile with: gcc -m32 -Os -U_FORTIFY_SOURCE -no-pie -o test test.c

$ objdump -x test | grep GLIBC
    0x0d696910 0x00 03 GLIBC_2.0
00000000       F *UND*	00000000              printf@GLIBC_2.0
00000000       F *UND*	00000000              __xstat@GLIBC_2.0
00000000       F *UND*	00000000              __libc_start_main@GLIBC_2.0

I've successfully implemented this in a (simple, dependency-free) program: https://github.com/mid-kid/metroskrew/commit/e382cae3c78e8c492cdfd0b967203e7be0e5ed3e

mid-kid avatar Jul 14 '24 14:07 mid-kid