Mac下仅含函数声明的动态库构建问题:与Linux差异及解决
问题背景
尝试构建一个仅包含函数声明、实现位于主程序中的动态库(dylib),该动态库的初始化函数会调用注册函数。此代码及编译方式在Linux平台可正常构建运行,但在Mac平台报错。
相关代码
dll.c
#include <stdio.h> #include <stdlib.h> typedef struct test { int i; void (*echo_fun)(struct test *p); }Test; void my_register(Test *p); static void printf_test(Test *p) { printf("i = %d\n", p->i); } static Test config = { .i = 0, .echo_fun = printf_test, }; void __attribute__((constructor)) my_init(void) { printf("init\n"); my_register(&config); }
main.c
#include <stdlib.h> #include <dlfcn.h> #include <stdio.h> typedef struct my_test { int i; void (* echo_fun)(struct __test *p); }Test; void my_register(Test *p) { p->i = 1; p->echo_fun(p); } int main(void) { void *handle = NULL; char *myso = "./dll.dylib"; if((handle = dlopen(myso, RTLD_NOW)) == NULL) { printf("dlopen - %s\n", dlerror()); exit(-1); } return 0; }
编译命令
gcc -shared -fPIC -nostartfiles -o dll.dylib dll.c
错误信息
Apple clang version 14.0.0 (clang-1400.0.29.202) Target: x86_64-apple-darwin22.1.0 Thread model: posix InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib -dynamic -dylib -arch x86_64 -platform_version macos 13.0.0 13.0 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -o dll.dylib -L/usr/local/lib dll.o -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/14.0.0/lib/darwin/libclang_rt.osx.a Undefined symbols for architecture x86_64: "_my_register", referenced from: _my_init in dll.o ld: symbol(s) not found for architecture x86_64 clang: error: linker command failed with exit code 1 (use -v to see invocation)
提问
- 为何该方案在Linux平台可正常运行,却在Mac平台报错?
- 能否在Mac平台成功构建该动态库?具体操作方法是什么?
解答
1. 跨平台差异原因
Linux与macOS的动态链接器默认规则不同:
- Linux平台:编译动态库时,默认允许未定义符号存在,这类符号会留到运行时由加载动态库的主程序提供,因此编译阶段不会报错。
- macOS平台:默认要求动态库中所有符号在编译阶段必须找到定义,当检测到
_my_register这类未定义符号时,链接器直接抛出错误,不会将未解析符号延迟到运行时处理。
另外,-nostartfiles参数在两个平台的行为存在差异,但核心报错原因还是符号解析规则的不同。
2. Mac平台构建方法
可以通过修改编译参数,让macOS链接器允许未定义符号延迟到运行时解析,具体步骤如下:
步骤1:编译动态库
添加-undefined dynamic_lookup参数,告诉链接器允许未定义符号在运行时由主程序提供:
gcc -shared -fPIC -nostartfiles -undefined dynamic_lookup -o dll.dylib dll.c
步骤2:编译主程序
编译主程序时链接动态加载库:
gcc -o main main.c -ldl
步骤3:运行程序
执行生成的主程序即可:
./main
额外注意事项
- 主程序中
struct __test的定义需要和动态库中的struct test完全一致,否则会出现指针类型不匹配问题,建议将结构体定义抽为公共头文件,避免两边定义不一致。 - macOS动态库后缀为
.dylib,代码中已正确使用,无需修改。
内容的提问来源于stack exchange,提问作者B1NandZhuo
相关产品推荐
相关产品推荐

