我如何在没有 @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)。
如果您使用 -static
,call 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
的替代方案。
我想在汇编中调用 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)。
如果您使用 -static
,call 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
的替代方案。