我如何使用像 objdump
这样的工具来判断一个目标文件是否是用 -fPIC
编译的?
我如何使用像 objdump
这样的工具来判断一个目标文件是否是用 -fPIC
编译的?
答案取决于平台。在大多数平台上,如果从
readelf --relocs foo.o | egrep '(GOT|PLT|JU?MP_SLOT)'
如果空的话,则表示 foo.o
没有使用 -fPIC
编译或者 foo.o
中不存在 -fPIC
所需的代码。我曾经需要在 PowerPC 目标环境下执行以下操作,找出哪个共享对象(.so)没有使用 -fPIC 标志进行构建。 我运行 readelf -d libMyLib1.so 命令,并查找 TEXTREL。 如果您看到 TEXTREL,则意味着一个或多个源文件未使用 -fPIC 构建成您的 .so。 如果需要,您可以使用 elfdump 命令代替 readelf。
例如:
[user@host lib]$ readelf -d libMyLib1.so | grep TEXT # Bad, not -fPIC
0x00000016 (TEXTREL)
[user@host lib]$ readelf -d libMyLib2.so | grep TEXT # Good, -fPIC
[user@host lib]$
为了帮助寻找解决方案的人,当我运行可执行文件时遇到的错误是:
root@target:/# ./program: error while loading shared libraries: /usr/lib/libMyLi
b1.so: R_PPC_REL24 relocation at 0x0fc5987c for symbol 'memcpy' out of range
我不知道这些信息是否适用于所有架构。
-fPIC的意思是代码能够在与编译地址不同的地址上执行。
为了实现这一点,反汇编器看起来会像这样....
call get_offset_from_compilation_address
get_offset_from_compilation_address: pop ax
sub ax, ax , &get_offset_from_compilation_address
现在在ax中,我们有一个偏移量需要添加到对内存的任何访问中。
load bx, [ax + var_address}
使用readelf -a *.so命令并grep Flags 标志: 0x50001007, noreorder, pic, cpic, o32, mips32
这通常可以起作用。
来自Linux编程接口:
On Linux/x86-32, it is possible to create a shared library using modules compiled without the
–fPIC
option. However, doing so loses some of the benefits of shared libraries, since pages of program text containing position-dependent memory references are not shared across processes. On some architectures, it is impossible to build shared libraries without the–fPIC
option.In order to determine whether an existing object file has been compiled with the
–fPIC
option, we can check for the presence of the name_GLOBAL_OFFSET_TABLE_
in the object file’s symbol table, using either of the following commands:$ nm mod1.o | grep _GLOBAL_OFFSET_TABLE_ $ readelf -s mod1.o | grep _GLOBAL_OFFSET_TABLE_
Conversely, if either of the following equivalent commands yields any output, then the specified shared library includes at least one object module that was not compiled with
–fPIC
:$ objdump --all-headers libfoo.so | grep TEXTREL $ readelf -d libfoo.so | grep TEXTREL
然而,以上引用和这个问题的任何答案都不适用于x86_64。
我在我的x86_64 Ubuntu机器上观察到的是,无论是否指定-fPIC
,它都会生成fPIC .o
。也就是说
gcc -g -Wall -c -o my_so.o my_so.c // has _GLOBAL_OFFSET_TABLE_
gcc -g -Wall -fPIC -c -o my_so_fpic.o my_so.c // has _GLOBAL_OFFSET_TABLE_
readelf -s my_so.o > 1.txt && readelf -s my_so_fpic > 2.txt && diff 1.txt 2.txt
没有区别,my_so.o
和my_so_fpic.o
都可以用来创建共享库。
为了生成非 fpic 对象文件,我在如何测试 Linux 二进制文件是否编译为位置无关代码?的第一条评论中找到了一个名为-fno-pic
的 gcc 标志。
这很有效,
gcc -g —Wall -fno-pic -c -o my_so_fnopic.o my_so.c // no _GLOBAL_OFFSET_TABLE_
并且
gcc -g -Wall -shared -o libdemo.so my_so_fnopic.o
出现错误:
/usr/bin/ld: my_so_fnopic.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
collect2: error: ld returned 1 exit status
.o
创建共享库。区分您的程序是否使用了 -fPIC 选项的另一种选择是:
在编译时启用 -g3 -gdwarf-2 选项。
其他 GCC 调试格式也可能包含宏信息:
请注意,下面的 $'..' 语法假定使用了 bash。
echo $' main() { printf("%d\\n", \n#ifdef __PIC__\n__PIC__\n#else\n0\n#endif\n); }' | gcc -fPIC -g3
-gdwarf-2 -o test -x c -
readelf --debug-dump=macro ./test | grep __PIC__
这种方法有效是因为gcc手册声明,如果使用-fpic,则PIC定义为1,如果使用-fPIC,则PIC为2。
通过检查GOT来获取答案是更好的方法。因为我猜测很少使用-g3 -gdwarf-2的先决条件。