手动生成动态库
下面的内容叙述了如何从源码分阶段编译一个动态库。起源是需要修改函数名,但是函数名无法在源码中更改(因为函数签名问题)
首先生成 Makefile 文件
cmake -B build -G'Unix Makefiles'
然后生成相关预编译文件:
cd build/src make memalloc.i
从预编译文件生成汇编文件:
cd CMakeFiles/memalloc.dir c++ -S memalloc.cpp.i -o memalloc.s -fPIC -shared
-fPIC 用来生成位置无关代码,这意味着汇编代码中全部使用相对位置跳转。因此此参数在生成汇编文件的时候就需要加上 修改相关的函数名:
sed -i 's/mem_alloc/malloc/g' memalloc.s sed -i 's/mem_free/free/g' memalloc.s sed -i 's/mem_calloc/calloc/g' memalloc.s sed -i 's/mem_realloc/realloc/g' memalloc.s
编译成动态库
c++ memalloc.s --shared -o memalloc.so
这里使用 c++ 而不是 gcc 是因为尽管 使用 gcc 也能通过编译 但是 gcc 不会自动连接 libstdc++ 库,从而导致运行时错误:
undefined symbol: __gxx_personality_v0
另一个问题是如果在此步骤的命令行中加入了
-c
参数,就会出现错误:so: only ET_DYN and ET_EXEC can be loaded
然后可以使用 readelf 查看函数存在的符号
readelf -s memalloc.so
另一种说法是使用 objdump 读取符号,但是这种方式读取的符号是有冗余的。最好的办法就是使用 readelf |
未定义符号
未定义符号的情况以下几种:
函数定义但是无实现。
函数实现了但是编译器裁剪掉了。
第一种情况一般是忘记实现亦或是忘记将源文件加入编译文件列表。在这种情况下,编译器看到了函数声明因而编译成功,但是链接时找不到符号导致失败。
第二种情况一般发生在 MSVC 忘记添加导出宏,抑或是由静态库合成动态库时未使用符号被裁剪掉了。