/* 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
输出make run
:
Error locating fnptr: Symbol not found: fnptr
Main program is doing some printing
符号(fnptr)是一个全局数据对象:
> objdump -t linky | grep fnptr
0000000000020008 g O .data 0000000000000008 fnptr
使用LD_DEBUG=symbols
(Debian)表明它正在可执行文件中查找:
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)
我在 Debian、Alpine(musl libc)和其他变体(所有 GCC)上尝试过此操作,结果相同。我问过 AI 机器人如何做到这一点,它们吐出了这段代码。
相同的代码在 macOS 上运行良好
▶ make run_macOS
DYLD_INSERT_LIBRARIES=$PWD/libi.so ./linky
My library is doing some printing
我做错了什么,导致这在 GCC 上不起作用?
-rdynamic
GNU 系统上的解决方案是在编译主程序 (dlsym-main.c) 时使用。这会传递--export-dynamic
给链接器,链接器会生成fnptr
动态符号表的一部分,进而允许 dlsym() 找到它。来自 GNU ld 文档:-E
,--export-dynamic
我们没有使用 dlopen,但 LD_PRELOAD 可以执行等效操作。