Fra*_*kH. 6 assembly gcc x86-64 elf ld
我不确定这个问题的主题是什么,但是我们走了......
为了强制代码的关键部分的代码局部性/紧凑性,我正在寻找一种方法在调用时R_X86_64_JUMP_SLOT直接通过"跳槽"(ELF 重定位)在外部(动态加载)库中调用函数site - 链接器通常放入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;
}Run Code Online (Sandbox Code Playgroud)
为了得到一个64位字的空间,电话本身是有效的(拜托,没有评论这是幸运的巧合,因为这打破了某些ABI规则 - 所有这些都不是这个问题的主题......并且,对于我的情况,可以工作围绕/以其他方式解决,我试图保持这个例子简短).
它创建以下程序集:
0000000000000000 <main>:
0: bf 00 00 00 00 mov $0x0,%edi
1: R_X86_64_32 .rodata.str1.1
5: 68 00 00 00 00 pushq $0x0
6: R_X86_64_32 .text+0x19
a: 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_64reloc.将目标文件与libc链接到实际可执行文件中会导致:0000000000400428 <printf@plt>: 400428: ff 25 92 04 10 00 jmpq *1049746(%rip) # 5008c0 <_GLOBAL_OFFSET_TABLE_+0x20> [ ... ] 0000000000400500 <main>: 400500: bf 0c 06 40 00 mov $0x40060c,%edi 400505: 68 19 05 40 00 pushq $0x400519 40050a: ff 24 25 11 05 40 00 jmpq *0x400511 400511: [ .quad 400428 ] 400519: 31 c0 xorl %eax, %eax 40051b: c3 retq [ ... ] DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE [ ... ] 00000000005008c0 R_X86_64_JUMP_SLOT printf即,这仍然提供两步重定向,首先将执行转移到PLT钩子,然后跳转到库入口点.
有没有一种方法可以指示编译器/汇编器/链接器 - 在本例中 - "内联"地址的跳槽目标0x400511?即将"本地"(在程序链接时解析ld)R_X86_64_64reloc替换为"remote"(在程序加载时解析ld.so)R_X86_64_JUMP_SLOT1(并强制执行此部分代码的非延迟加载)?也许链接器mapfiles可能使这成为可能 - 如果是这样,怎么样?
编辑:
为了清楚说明,问题是如何在动态链接的可执行文件/外部函数中实现此功能,该函数仅在动态库中可用.是的,这是真正的静态链接以更简单的方式解决这个问题,但是:
因此静态链接在这里没有用:(
Edit2:
我发现在某些体系结构中(SPARC,显然,请参阅GNU中的SPARC重定位部分作为手册),GNU能够使用修饰符就地为链接器创建某些类型的重定位引用.引用的SPARC将用于%gdop(symbolname)使汇编程序向链接器发出指令,指出"在此处创建重定位".英特尔在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;
}Run Code Online (Sandbox Code Playgroud)
我从链接器收到错误(请注意7 == R_X86_64_JUMP_SLOT):
error: /tmp/cc6BUEZh.o: unexpected reloc 7 in object file汇编程序创建一个目标文件,其中包含
readelf:Relocation section '.rela.text.startup' at offset 0x5e8 contains 2 entries:
Offset Info Type Symbol's Value Symbol's Name + Addend
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不是"已解决" 的二进制文件...
此优化已在 GCC 中实现。可以使用-fno-plt选项和noplt功能属性启用它:
不要在与位置无关的代码中使用 PLT 进行外部函数调用。相反,从 GOT 加载调用站点的被调用者地址并分支到它。通过消除 PLT 存根并使 GOT 负载进行优化,可以产生更高效的代码。在 32 位 x86 等架构上,PLT 存根期望 GOT 指针位于特定寄存器中,这为编译器提供了更多的寄存器分配自由。惰性绑定需要使用PLT;所有外部符号都
-fno-plt在加载时解析。或者,函数属性
noplt可用于避免通过 PLT 调用特定外部函数。在位置相关代码中,一些目标还会将对标记为不使用 PLT 的函数的调用转换为使用 GOT。