/* dlsym-main.c */
#include <stdio.h>
void doSomething(char const *msg)
{
fprintf(stderr, "Main program is doing %s\n", msg);
}
void (*fnptr)(char const *) = doSomething;
int main() {
fnptr("some printing");
return 0;
}
/* dlsym-lib.c */
#define _GNU_SOURCE 1
#include <stdio.h>
#include <dlfcn.h>
void myLibraryFunc(char const *msg)
{
fprintf(stdout, "My library is doing %s\n", msg);
}
void hook(void)
{
// Load the current executable as a shared object
void* handle = dlopen(NULL, RTLD_LAZY);
if (!handle) {
fprintf(stderr, "Error: %s\n", dlerror());
return;
}
typedef void (*fnT)(char const *);
// Dynamically resolve the global non-static variable
fnT* fnPtr = (fnT*) dlsym(handle, "fnptr");
if (!fnPtr)
{
fprintf(stderr, "Error locating fnptr: %s\n", dlerror());
dlclose(handle);
return;
}
*fnPtr = myLibraryFunc;
// Close the handle
dlclose(handle);
}
__attribute__((constructor))
void setup() { hook(); }
## Makefile
linky: dlsym-main.c
gcc -o linky dlsym-main.c
libi.so: dlsym-lib.c
gcc -shared -fPIC -o libi.so dlsym-lib.c -ldl
run: linky libi.so
LD_PRELOAD=$$PWD/libi.so ./linky
Saída de make run
:
Error locating fnptr: Symbol not found: fnptr
Main program is doing some printing
Símbolo (fnptr) é um objeto de dados global:
> objdump -t linky | grep fnptr
0000000000020008 g O .data 0000000000000008 fnptr
Usar LD_DEBUG=symbols
(Debian) mostra que está procurando no executável:
318: symbol=fnptr; lookup in file=./linky [0]
318: symbol=fnptr; lookup in file=/CDS/libi.so [0]
318: symbol=fnptr; lookup in file=/usr/lib/aarch64-linux-gnu/libstdc++.so.6 [0]
318: symbol=fnptr; lookup in file=/lib/aarch64-linux-gnu/libc.so.6 [0]
318: symbol=fnptr; lookup in file=/lib/aarch64-linux-gnu/libdl.so.2 [0]
318: symbol=fnptr; lookup in file=/lib/aarch64-linux-gnu/libm.so.6 [0]
318: symbol=fnptr; lookup in file=/lib/aarch64-linux-gnu/libgcc_s.so.1 [0]
318: symbol=fnptr; lookup in file=/lib/ld-linux-aarch64.so.1 [0]
318: /CDS/libi.so: error: symbol lookup error: undefined symbol: fnptr (fatal)
Eu tentei isso no Debian, Alpine (musl libc) e outras variantes (todas GCC) e os mesmos resultados ocorreram. Perguntei aos bots de IA como fazer isso e eles cuspiram exatamente esse código.
O mesmo código no macOS funciona bem
▶ make run_macOS
DYLD_INSERT_LIBRARIES=$PWD/libi.so ./linky
My library is doing some printing
O que estou fazendo de errado para que isso não funcione no GCC?
A solução em sistemas GNU é usar
-rdynamic
ao compilar o programa principal (dlsym-main.c). Isso passa--export-dynamic
para o vinculador, que fazfnptr
parte da tabela dinâmica de símbolos, que por sua vez permite que dlsym() localize-o. Dos documentos GNU ld:-E
,--export-dynamic
Não estamos usando dlopen, mas LD_PRELOAD está fazendo o equivalente.