如何在不带@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,

查看:122
本文介绍了如何在不带@PLT的程序集中正常调用printf,而仅在带有标准库的gcc中使用-l选项调用printf,的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I want to call printf in assembly and link it using gcc -l option with standard library, But it says:

  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错误

when I replace libc.so with libc.a, It still shows Sementation fault

 .file  "mod.c"
.text
.section    .rodata
.LC0: 
   .string  "%d"
.text
.globl  main
.type   main, @function
main:
pushq   %rbp
movq    %rsp, %rbp
subq    $16, %rsp
movl    $3, -8(%rbp)
movl    $2, -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    $0, %eax
call    printf
movl    $0, %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

When I add @PLT after printf it goes right, But I just want to use -l in gcc

推荐答案

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

You don't need -llibc, gcc already links that by default.

这里的问题是,现代GCC默认使PIE可执行文件(与位置无关)是ELF共享对象".链接器将其更像是一个库,并且不会自动创建用于调用未定义符号名称的PLT存根. (我认为这种行为不是必需的,ld 可以可以让您做到这一点.)

The problem here is that modern GCC defaults to making a PIE executable (position-independent), which is an ELF "shared object". The linker treats it more like a library and doesn't automatically create PLT stubs for calls to undefined symbol names. (I don't think this behaviour is necessary, ld could let you do this.)

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

The easy solution here is gcc -no-pie -fno-pie -o mod mod.s

然后您可以编写call printf,它就可以正常工作.

Then you can write call printf and it Just Works.

使用该命令行,您将创建一个动态链接的ELF可执行文件.链接器会为您将call printf重写为call printf@plt(将其分解并查看,然后使用objdump -drwC打印重定位). libc加载地址和您的代码地址之间的偏移量不是链接时间常数. (并且可能大于2 ^ 32).

With that command line, you'll create a dynamically-linked ELF executable. The linker rewrites your call printf to call printf@plt for you (disassemble it and see, with objdump -drwC to print relocations.). The offset between the libc load address and your code's address is not a link-time constant. (And could be larger than 2^32 anyway).

如果使用-static,则call printf解析为从libc.a复制到可执行文件中的printf定义的实际地址.

If you use -static, the call printf resolves to the actual address of the printf definition copied into your executable from libc.a.

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

I'm guessing that the option of building a static or dynamic executable from the same source is why ld is willing to rewrite calls to PLT stubs for ELF executables but not ELF shared objects (like PIE executables).

请参见 32位绝对地址在x86-64 Linux中允许更长的时间?有关PIE的更多信息.

See 32-bit absolute addresses no longer allowed in x86-64 Linux? for more about PIE.

调用共享库函数的另一种方式是call *printf@GOTPCREL(%rip),就像gcc在使用-fno-plt进行编译时所做的那样.这样就完全绕开了PLT,只需要通过GOT中的函数指针进行调用即可(您可以使用RIP相对寻址模式访问该指针).

The other way to call shared library functions is call *printf@GOTPCREL(%rip), like gcc does if you compile with -fno-plt. This bypasses the PLT entirely, just doing a call through the function pointer in the GOT (which you access with a RIP-relative addressing mode).

此的NASM版本是call [rel printf wrt ..got]
替代call printf wrt ..plt. 无法调用汇编(yasm)代码在64位Linux上提供C标准库功能

The NASM version of this is call [rel printf wrt ..got]
as an alternative to call printf wrt ..plt. Can't call C standard library function on 64-bit Linux from assembly (yasm) code

这篇关于如何在不带@PLT的程序集中正常调用printf,而仅在带有标准库的gcc中使用-l选项调用printf,的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆