我如何在没有 @PLT 的情况下在程序集中正常调用 printf 而只是在带有标准库的 gcc 中使用 -l 选项调用 printf,

How can I call printf normally in assembly without @PLT but just call printf with -l option in gcc with standard library,

我想在汇编中调用 printf 并 link 它使用带有标准库的 gcc -l 选项,但它说:

  Symbol `printf' causes overflow in R_X86_64_PC32 relocation
  Segmentation fault (core dumped)

这是我的编译方式:

   gcc mod.s -l:libc.so -o mod

当我用libc.a替换libc.so时,它仍然显示Sementation fault

 .file  "mod.c"
.text
.section    .rodata
.LC0: 
   .string  "%d"
.text
.globl  main
.type   main, @function
main:
pushq   %rbp
movq    %rsp, %rbp
subq    , %rsp
movl    , -8(%rbp)
movl    , -4(%rbp)
movl    -8(%rbp), %eax
cltd
idivl   -4(%rbp)
movl    %edx, -4(%rbp)
movl    -4(%rbp), %eax
movl    %eax, %esi
leaq    .LC0(%rip), %rdi
movl    [=12=], %eax
call    printf
movl    [=12=], %eax
leave
ret
.size   main, .-main
.ident  "GCC: (Ubuntu 7.4.0-1ubuntu1~18.04) 7.4.0"
.section    .note.GNU-stack,"",@progbits

当我在 printf 之后添加 @PLT 时,它是正确的,但我只想在 gcc 中使用 -l

您不需要 -llibc,默认情况下 gcc 已经链接了它。

这里的问题是现代 GCC 默认生成 PIE 可执行文件 (position-independent),这是一个 ELF "shared object"。链接器更像是一个库,不会自动为调用未定义的符号名称创建 PLT 存根。 (我不认为这种行为是必要的,ld可以让你这样做。)

这里的简单解决方案是 gcc -no-pie -fno-pie -o mod mod.s

然后你可以写 call printf 就可以了。

使用该命令行,您将创建一个 dynamically-linked ELF 可执行文件。链接器为您将 call printf 重写为 call printf@plt(反汇编并查看,使用 objdump -drwC 打印重定位。)。 libc 加载地址和代码地址之间的偏移量不是 link-time 常量。 (无论如何都可能大于 2^32)。

如果您使用 -staticcall printf 解析为从 libc.a.

复制到您的可执行文件中的 printf 定义的实际地址

我猜测从同一来源构建静态或动态可执行文件的选项是 ld 愿意为 ELF 可执行文件而不是 ELF 共享对象(如 PIE 可执行文件)重写对 PLT 存根的调用的原因).

有关 PIE 的更多信息,请参阅 32-bit absolute addresses no longer allowed in x86-64 Linux?

调用共享库函数的另一种方法是 call *printf@GOTPCREL(%rip),如果您使用 -fno-plt 进行编译,就像 gcc 一样。这完全绕过了 PLT,只是通过 GOT 中的函数指针进行调用(您使用 RIP-relative 寻址模式访问)。

这个NASM版本是call [rel printf wrt ..got]
作为 call printf wrt ..plt 的替代方案。