Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Followed Readme, binary still contains GLIBC_2.33 funcs #32

Open
TheBigS opened this issue Nov 12, 2022 · 8 comments
Open

Followed Readme, binary still contains GLIBC_2.33 funcs #32

TheBigS opened this issue Nov 12, 2022 · 8 comments

Comments

@TheBigS
Copy link

TheBigS commented Nov 12, 2022

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?

@katzer
Copy link

katzer commented Dec 1, 2022

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 *)

@TheBigS
Copy link
Author

TheBigS commented Dec 1, 2022

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

@katzer
Copy link

katzer commented Dec 2, 2022

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 ;)

@mcourteaux
Copy link

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

@mcourteaux
Copy link

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
Copy link

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.

@mid-kid
Copy link

mid-kid commented Jul 14, 2024

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
Copy link

mid-kid commented Jul 14, 2024

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: mid-kid/metroskrew@e382cae

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants