我写了一个示例C代码如下

#include<stdio.h>

int foo(void);
int main()
{
    puts("Hello World");
    int x = 10;
    fprintf(stdout,"Hello World 22");
    return foo();
}

我通过

gcc -shared -fpic hello_world.c -o hello_world.so

在海湾合作委员会指挥部以上

put调用的程序集如下所示

0000000000001070 <puts@plt>:
    1070:   f3 0f 1e fa             endbr64 
    1074:   f2 ff 25 9d 2f 00 00    bnd jmpq *0x2f9d(%rip)        # 4018 <puts@GLIBC_2.2.5>
    107b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1) 

foo()


0000000000001080 <foo@plt>:
    1080:   f3 0f 1e fa             endbr64 
    1084:   f2 ff 25 95 2f 00 00    bnd jmpq *0x2f95(%rip)        # 4020 <foo>
    108b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

但组装件看起来是一样的

我想,说明书通常是这样的

  1. 跳转至GOT表
  2. 推送重定位标识符
  3. 跳转到动态链接器

但在这里,它似乎直接跳转到GLIBC.我是不是理解错了什么?还是我编错了?

推荐答案

这与-fcf-protection有关.Ubuntu的GCC配置为--enable-cet,默认情况下启用-fcf-protection检测.如果你用-fcf-protection=none编译,你会看到做JMP [<got-entry>]; PUSH <idx>; JMP <pltstub>的"正常"PLT条目.

对于-fcf-protection(也包括=branch=full),添加了一个额外的部分,称为.plt.sec,实际的函数调用存根位于那里.每个存根以ENDBR64(见What does the endbr64 instruction actually do?)开始,将其标记为有效的分支目标,所有JMP条指令都变成BND JMP(见Meaning of BND RET in x86).推送重定位索引的PUSH <idx>个指令都在.plt中,现在它保存了一个序列ENDBR64; PUSH <idx>; BND JUMP <pltstub>(如下面所示).

在"正常"情况下,每个GOT条目最初填充有相应PLT条目的下一个指令的地址.

.plt.sec的情况下,GOT条目初始填充为.plt + <offset>,其中<offset>指向.plt中的相应序列ENDBR64; PUSH <idx>; BND JMP <pltstub>.

至于为什么会这样设计,我不清楚.我找到了this perf patch,这似乎给了一些关于.plt.sec的见解.


以下是PLT -fcf-protection=none的外观,这是您所期望的:

Disassembly of section .plt:

0000000000001020 <puts@plt-0x10>:
    1020:       ff 35 e2 2f 00 00       push   0x2fe2(%rip)        # 4008 <_GLOBAL_OFFSET_TABLE_+0x8>
    1026:       ff 25 e4 2f 00 00       jmp    *0x2fe4(%rip)        # 4010 <_GLOBAL_OFFSET_TABLE_+0x10>
    102c:       0f 1f 40 00             nopl   0x0(%rax)

0000000000001030 <puts@plt>:
    1030:       ff 25 e2 2f 00 00       jmp    *0x2fe2(%rip)        # 4018 <puts@GLIBC_2.2.5>
    1036:       68 00 00 00 00          push   $0x0
    103b:       e9 e0 ff ff ff          jmp    1020 <_init+0x20>

下面是-fcf-protection的样子:

Disassembly of section .plt:

0000000000001020 <.plt>:
    1020:       ff 35 e2 2f 00 00       push   0x2fe2(%rip)        # 4008 <_GLOBAL_OFFSET_TABLE_+0x8>
    1026:       f2 ff 25 e3 2f 00 00    bnd jmp *0x2fe3(%rip)        # 4010 <_GLOBAL_OFFSET_TABLE_+0x10>
    102d:       0f 1f 00                nopl   (%rax)
    1030:       f3 0f 1e fa             endbr64
    1034:       68 00 00 00 00          push   $0x0
    1039:       f2 e9 e1 ff ff ff       bnd jmp 1020 <_init+0x20>
    103f:       90                      nop
    1040:       f3 0f 1e fa             endbr64
    1044:       68 01 00 00 00          push   $0x1
    1049:       f2 e9 d1 ff ff ff       bnd jmp 1020 <_init+0x20>
    104f:       90                      nop
    1050:       f3 0f 1e fa             endbr64
    1054:       68 02 00 00 00          push   $0x2
    1059:       f2 e9 c1 ff ff ff       bnd jmp 1020 <_init+0x20>
    105f:       90                      nop

Disassembly of section .plt.got:

0000000000001060 <__cxa_finalize@plt>:
    1060:       f3 0f 1e fa             endbr64
    1064:       f2 ff 25 8d 2f 00 00    bnd jmp *0x2f8d(%rip)        # 3ff8 <__cxa_finalize@GLIBC_2.2.5>
    106b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

Disassembly of section .plt.sec:

0000000000001070 <puts@plt>:
    1070:       f3 0f 1e fa             endbr64
    1074:       f2 ff 25 9d 2f 00 00    bnd jmp *0x2f9d(%rip)        # 4018 <puts@GLIBC_2.2.5>
    107b:       0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)

C++相关问答推荐

理解没有返回语句的递归C函数的行为

与unions 的未定义行为

在C中使用强制转换将uint16_t转换为uint8_t [2]是否有效?

Ebpf内核代码:permission denied:invalid access to map value

如何使fputs功能提示错误输入并要求用户重新输入.程序停止而不是请求新的输入

Clang:如何强制运行时错误的崩溃/异常由于-fsanitize=undefined

如何使解释器存储变量

使用TCL C API导航到列表中的元素

在libwget中启用Cookie会导致分段故障

MacOS下C++的无阻塞键盘阅读

使用mmap为N整数分配内存

';malloc():损坏的顶部大小';分配超过20万整数后

在C中,为什么这个带有递增整数的main函数从不因溢出而崩溃?

struct 中的qsort,但排序后的 struct 很乱

使用 c 中的 write() 函数将非 ASCII 字符写入标准输出

(GNU+Linux) 多个线程同时调用malloc()

从管道读取数据时丢失

使用邻接表创建图

如何使用 raylib 显示数组中的图像

int 与 size_t 与 long