我不确定这个问题的好主题是什么,但我们开始吧:
为了强制代码的关键部分的代码局部性/紧凑性,我正在寻找一种方法来通过“跳转槽”(ELFR_X86_64_JUMP_SLOT
重定位)直接在调用时调用外部(动态加载)库中的函数站点 - 链接器通常放入 PLT / GOT 的内容,但在调用站点将这些内联。
如果我模拟这样的调用:
#include <stdio.h>
int main(int argc, char **argv)
{
asm ("push $1f\n\t"
"jmp *0f\n\t"
"0: .quad %P0\n"
"1:\n\t"
: : "i"(printf), "D"("Hello, World!\n"));
return 0;
}
为了获得 64 位字的空间,调用本身可以工作(请不要评论这是幸运的巧合,因为这违反了某些 ABI 规则——所有这些都不是这个问题的主题。
对于我的情况,以其他方式解决/解决,我试图保持这个例子简短)。
它创建以下程序集:
0000000000000000 <主>: 0: bf 00 00 00 00 移动 $0x0,%edi 1:R_X86_64_32.rodata.str1.1 5:68 00 00 00 00 推 0x0 6:R_X86_64_32 .text+0x19 一个:ff 24 25 00 00 00 00 jmpq *0x0 d: R_X86_64_32S .text+0x11 ... 11:R_X86_64_64 printf 19: 31 c0 xor %eax,%eax 1b: c3 retq
但是(由于printf
用作立即数,我猜......?)这里的目标地址仍然是 PLT 钩子的地址 - 相同的R_X86_64_64
reloc。将针对 libc 的目标文件链接到实际的可执行文件会导致:
0000000000400428 <printf@plt>: 400428: ff 25 92 04 10 00 jmpq *1049746(%rip) # 5008c0 <_GLOBAL_OFFSET_TABLE_+0x20> [ ... ] 0000000000400500 <主>: 400500: bf 0c 06 40 00 移动 $0x40060c,%edi 400505:68 19 05 40 00 推 0x400519 40050a: ff 24 25 11 05 40 00 jmpq *0x400511 400511:[.quad 400428] 400519: 31 c0 xorl %eax, %eax 40051b:c3 retq [ ... ] 动态搬迁记录 偏移类型值 [ ... ] 00000000005008c0 R_X86_64_JUMP_SLOT printf
即这仍然给出了两步重定向,首先将执行转移到PLT钩子,然后跳转到库入口点。
有没有办法指示编译器/汇编器/链接器 - 在这个例子中 - “内联”地址处的跳转槽目标0x400511
?
即用“远程”(在程序加载时通过)reloc替换“本地”(在程序链接时通过)reloc (并ld
强制这部分代码进行非延迟加载)?也许链接器映射文件可能使这成为可能 - 如果是这样,如何?R_X86_64_64
ld.so
R_X86_64_JUMP_SLOT
编辑:
为了清楚起见,问题是关于如何在动态链接的可执行文件中实现这一点/对于仅在动态库中可用的外部函数。是的,真正的静态链接以更简单的方式解决了这个问题,但是:
- 在某些系统(如 Solaris)中,供应商通常不提供静态库
- 有些库既不能作为源代码也不能作为静态版本使用
因此静态链接在这里没有帮助:(
Edit2:
我发现在某些体系结构中(SPARC,值得注意的是,请参阅GNU 中的 SPARC 重定位部分作为手册),GNU 能够使用修饰符为链接器就地创建某些类型的重定位引用。引用的 SPARC 将用于%gdop(symbolname)
使汇编器向链接器发出指令,说明“在此处创建该重定位”。Intel 在 Itanium 上的汇编器知道相同类型的@fptr(symbol)
链接重定位运算符(另请参见Itanium psABI中的第 4 节)。但是对于 x86_64,是否存在等效机制——指示汇编器在代码中的特定位置发出特定的链接器重定位类型?
我还发现 GNU 汇编器有一个.reloc
指令,据说是用于此目的;不过,如果我尝试:
#include <stdio.h>
int main(int argc, char **argv)
{
asm ("push %%rax\n\t"
"lea 1f(%%rip), %%rax\n\t"
"xchg %%rax, (%rsp)\n\t"
"jmp *0f\n\t"
".reloc 0f, R_X86_64_JUMP_SLOT, printf\n\t"
"0: .quad 0\n"
"1:\n\t"
: : "D"("Hello, World!\n"));
return 0;
}
我从链接器收到一个错误(请注意7 == R_X86_64_JUMP_SLOT
):
错误:/tmp/cc6BUEZh.o:目标文件中出现意外的 reloc 7汇编器创建一个目标文件,其中
readelf
说:偏移 0x5e8 处的重定位部分“.rela.text.startup”包含 2 个条目: 偏移量信息类型符号的值符号的名称+加数 0000000000000001 000000050000000a R_X86_64_32 0000000000000000 .rodata.str1.1 + 0 0000000000000017 0000000b00000007 R_X86_64_JUMP_SLOT 0000000000000000 printf + 0
这就是我想要的——但链接器不接受它。
链接器只接受上面的使用R_X86_64_64
代替;这样做会创建与第一种情况相同的二进制文件...重定向到printf@plt
,而不是“已解决”的二进制文件。