【发布时间】:2019-08-09 03:54:47
【问题描述】:
我的问题
我正在尝试编写一个共享库(不是可执行文件,所以请不要告诉我使用-no-pie)与单独的文件(不是内联汇编)中的程序集和 C。
我想通过汇编代码中的全局偏移表访问 C 全局变量,因为调用的函数可能在任何其他共享库中定义。
我知道 PLT/GOT 的东西,但我不确定如何告诉编译器正确地为链接器生成重定位信息(语法是什么),以及如何告诉链接器用它来实际重定位我的代码信息(什么是链接器选项)。
我的代码编译时出现链接错误
/bin/ld: tracer.o: relocation R_X86_64_PC32 against
/bin/ld: final link failed: bad value
此外,如果有人能分享一些关于 GAS 组装的详细文档,那就更好了。例如,关于如何使用 GNU 汇编器在 C 和汇编之间进行插值的详尽列表。
源代码
编译 C 和汇编代码并将其链接到 ONE 共享库中。
# Makefile
liba.so: tracer2.S target2.c
gcc -shared -g -o liba.so tracer2.S target2.c
// target2.c
// NOTE: This is a variable, not a function.
int (*read_original)(int fd, void *data, unsigned long size) = 0;
// tracer2.S
.text
// external symbol declarition
.global read_original
read:
lea read_original(%rip), %rax
mov (%rax), %rax
jmp *%rax
期望与结果
我希望链接器能够愉快地链接我的目标文件,但它说
g++ -shared -g -o liba.so tracer2.o target2.c -ldl
/bin/ld: tracer.o: relocation R_X86_64_PC32 against
/bin/ld: final link failed: bad value
collect2: error: ld returned 1 exit status
make: *** [Makefile:2: liba.so] Error 1
并注释掉这一行
// lea read_original(%rip), %rax
使错误消失。
解决方案。
lea read_original@GOTPCREL(%rip), %rax
关键字GOTPCREL 会告诉编译器这是一个PC 相对重定位到GOT 表。链接器将计算从当前rip 到目标 GOT 表条目的偏移量。
你可以验证
$ objdump -d liba.so
10e9: 48 8d 05 f8 2e 00 00 lea 0x2ef8(%rip),%rax # 3fe8 <read_original@@Base-0x40>
10f0: 48 8b 00 mov (%rax),%rax
10f3: ff e0 jmpq *%rax
感谢彼得。
一些可能相关或不相关的信息
1.我可以调用一个C函数 call read@plt
objdump 显示它调用了正确的 PLT 条目。
$ objdump -d liba.so
...
0000000000001109 <read1>:
1109: e8 22 ff ff ff callq 1030 <read@plt>
110e: ff e0 jmpq *%rax
2.我可以正确lea一个PLT入口地址
0xffffff23 是 -0xdd,0x1109 - 0xdd = 102c
0000000000001020 <.plt>:
1020: ff 35 e2 2f 00 00 pushq 0x2fe2(%rip) # 4008 <_GLOBAL_OFFSET_TABLE_+0x8>
1026: ff 25 e4 2f 00 00 jmpq *0x2fe4(%rip) # 4010 <_GLOBAL_OFFSET_TABLE_+0x10>
102c: 0f 1f 40 00 nopl 0x0(%rax)
0000000000001030 <read@plt>:
1030: ff 25 e2 2f 00 00 jmpq *0x2fe2(%rip) # 4018 <read@GLIBC_2.2.5>
1036: 68 00 00 00 00 pushq $0x0
103b: e9 e0 ff ff ff jmpq 1020 <.plt>
0000000000001109 <read1>:
1109: 48 8d 04 25 23 ff ff lea 0xffffffffffffff23,%rax
1110: ff
1111: ff e0 jmpq *%rax
环境
- Arch Linux 20190809
$ uname -a
Linux alex-arch 5.2.6-arch1-1-ARCH #1 SMP PREEMPT Sun Aug 4 14:58:49 UTC 2019 x86_64 GNU/Linux
$ gcc -v
Using built-in specs.
COLLECT_GCC=/bin/gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/9.1.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: /build/gcc/src/gcc/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --with-system-zlib --with-isl --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-gnu-indirect-function --enable-multilib --disable-werror --enable-checking=release --enable-default-pie --enable-default-ssp --enable-cet=auto
Thread model: posix
gcc version 9.1.0 (GCC)
$ ld --version
GNU ld (GNU Binutils) 2.32
Copyright (C) 2019 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.
【问题讨论】:
-
.global read_original没有定义符号,它只标记它以导出 from 这个对象,如果 / 当它用read_original:标签定义时。共享库必须通过 GOT 才能访问主可执行文件或另一个共享库中的静态数据,因为它们可以加载超过 2GB,并且偏移量不是编译时常量。 (查看 C 编译器输出以访问extern int read_original。)此外,您的 LEA/JMP 等效于jmp read_original,但更糟。您的意思是mov/jmp加载函数指针吗? -
这看起来很像另一个启发性@PeterCordes回答
:)的开始 -
您真的需要将您的 asm 与定义全局的 C 语言分开放在一个共享库中吗?通常你只需将
.c和.s链接到一个共享库中。在 asm 中,您可以使用正常的 RIP 相对寻址访问您自己的共享对象中的全局变量。 -
嗨@PeterCordes,1。我不认为
.global read_original定义了符号以太。符号在target2.c中定义; 2. 我知道我们需要通过 GOT,我在问具体怎么做; 3.lea/jmp只是问题的演示,所以请不要介意时间成本:) -
@PeterCordes 我的 asm 和 C 在同一个共享库
liba.so中,不知道您是否正确理解我的问题。我将它们链接到一个共享库中。而 RIP 相对寻址正是我想要做的。如果我没有说清楚,请告诉我。
标签: linux shared-libraries x86-64 gnu-assembler got