重定位 R_X86_64_32S 对符号 `stdout@@GLIBC_2.2.5' 不能在制作 PIE 对象时使用
relocation R_X86_64_32S against symbol `stdout@@GLIBC_2.2.5' can not be used when making a PIE object
问题
我目前正在阅读 this book 以及有关动态 linking 的章节,代码如下:
link_example.s
.globl main
.section .data
output:
.ascii "Yeet\n[=12=]"
.section .text
main:
enter [=12=], [=12=]
movq stdout, %rdi
movq $output, %rsi
call fprintf
movq [=12=], %rax
leave
ret
现在按照书上的要求,我需要按如下方式编译成link动态C库:
gcc -rdynamic link_example.s -o link_example
但我收到以下错误消息:
/usr/bin/ld: /tmp/cchUlvqS.o: relocation R_X86_64_32S against symbol `stdout@@GLIBC_2.2.5' can not be used when making a PIE object; recompile with -fPIE
/usr/bin/ld: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status
我做错了什么?
你试过什么?
添加 -fPIE
标志
我通过添加 -fPIE
标志尝试了编译器的建议:
gcc -rdynamic -fPIE link_example.s -o link_example
但我仍然遇到同样的错误。
正在搜索类似的帖子
我发现 similar post 说,我只需要使用 -shared
标志:
gcc -shared link_example.s -o link_example
但这给了我:
/usr/bin/ld: /tmp/ccxktZan.o: relocation R_X86_64_32S against symbol `stdout@@GLIBC_2.2.5' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status
如果我添加 -fPIC
标志:
gcc -shared -fPIC link_example.s -o link_example
然后我得到这个:
/usr/bin/ld: /tmp/ccKIQ9sl.o: relocation R_X86_64_32S against symbol `stdout@@GLIBC_2.2.5' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status
让我向您展示如何修复您书中的汇编语言,使其适用于您的编译器的默认设置。
正如对问题的评论所说,问题是您的编译器默认生成 position-independent executables。这意味着 stdout
、fprintf
和 output
的地址在 link 时未知,因此 linker 无法“重新定位”指令指的是他们。
然而, 在 link 时已知的是这些东西的地址与程序计数器之间的偏移量].这意味着,如果您编写的程序集稍有不同,它就可以工作。像这样:
.globl main
.section .data
output:
.ascii "Yeet\n[=10=]"
.section .text
main:
enter [=10=], [=10=]
movq stdout(%rip), %rdi
leaq output(%rip), %rsi
call fprintf@PLT
movq [=10=], %rax
leave
ret
请注意,这三者的变化略有不同。 mov stdout, %rdi
变为 mov stdout(%rip), %rdi
—— 只是同一指令的不同寻址 mode。从固定地址 stdout
处的内存加载变为从 RIP 寄存器(也称为程序计数器)的固定 位移 stdout
处从内存加载。另一方面,用mov $output, %rsi
加载固定地址output
,变成lea output(%rip), %rsi
。我建议您将此视为 一直是 一个 load-effective-address 操作,但是旧代码(在固定地址处执行 executable )能够用 move-immediate 而不是实际的 lea 指令来表达该操作。最后,call fprintf
变成了 call fprintf@PLT
。这告诉 linker 调用需要经过 过程 linkage table -- 你的书应该解释这是什么为什么需要它。
顺便说一句,我看到了这个汇编语言的其他几个问题,其中最重要的是:
- 字符串
"Yeet\n[=24=]"
属于 read-only 数据部分。
- x86-64 ABI 表示需要通过适当设置
eax
来告知像 fprintf
这样的可变参数函数它们正在接收的浮点参数的数量。
enter
和 leave
在 x86-64 上是不必要的。 (另外,enter
是一个非常慢的微编码指令,根本不应该使用。)
我会写这样的东西:
.section .rodata, "a", @progbits
.output:
.string "Yeet\n"
.section .text, "ax", @progbits
.globl main
.type main, @function
main:
sub , %rsp
mov stdout(%rip), %rdi
lea .output(%rip), %rsi
xor %eax, %eax
call fprintf@PLT
xor %eax, %eax
add , %rsp
ret
(你需要在函数的开头从%rsp
中减去8,然后再把它加回来,因为ABI说%rsp
必须总是16的倍数在 call
指令处 -- 这意味着它 不是 进入任何函数时的 16 的倍数,而是 %rsp
mod 16 是 8,因为 call
压入八个字节(return 地址)。作为 enter
和 [= 的副作用,你免费得到这个28=],但把那些拿出来,你必须手工完成。)
问题
我目前正在阅读 this book 以及有关动态 linking 的章节,代码如下:
link_example.s
.globl main
.section .data
output:
.ascii "Yeet\n[=12=]"
.section .text
main:
enter [=12=], [=12=]
movq stdout, %rdi
movq $output, %rsi
call fprintf
movq [=12=], %rax
leave
ret
现在按照书上的要求,我需要按如下方式编译成link动态C库:
gcc -rdynamic link_example.s -o link_example
但我收到以下错误消息:
/usr/bin/ld: /tmp/cchUlvqS.o: relocation R_X86_64_32S against symbol `stdout@@GLIBC_2.2.5' can not be used when making a PIE object; recompile with -fPIE
/usr/bin/ld: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status
我做错了什么?
你试过什么?
添加 -fPIE
标志
我通过添加 -fPIE
标志尝试了编译器的建议:
gcc -rdynamic -fPIE link_example.s -o link_example
但我仍然遇到同样的错误。
正在搜索类似的帖子
我发现 similar post 说,我只需要使用 -shared
标志:
gcc -shared link_example.s -o link_example
但这给了我:
/usr/bin/ld: /tmp/ccxktZan.o: relocation R_X86_64_32S against symbol `stdout@@GLIBC_2.2.5' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status
如果我添加 -fPIC
标志:
gcc -shared -fPIC link_example.s -o link_example
然后我得到这个:
/usr/bin/ld: /tmp/ccKIQ9sl.o: relocation R_X86_64_32S against symbol `stdout@@GLIBC_2.2.5' can not be used when making a shared object; recompile with -fPIC
/usr/bin/ld: failed to set dynamic section sizes: bad value
collect2: error: ld returned 1 exit status
让我向您展示如何修复您书中的汇编语言,使其适用于您的编译器的默认设置。
正如对问题的评论所说,问题是您的编译器默认生成 position-independent executables。这意味着 stdout
、fprintf
和 output
的地址在 link 时未知,因此 linker 无法“重新定位”指令指的是他们。
然而, 在 link 时已知的是这些东西的地址与程序计数器之间的偏移量].这意味着,如果您编写的程序集稍有不同,它就可以工作。像这样:
.globl main
.section .data
output:
.ascii "Yeet\n[=10=]"
.section .text
main:
enter [=10=], [=10=]
movq stdout(%rip), %rdi
leaq output(%rip), %rsi
call fprintf@PLT
movq [=10=], %rax
leave
ret
请注意,这三者的变化略有不同。 mov stdout, %rdi
变为 mov stdout(%rip), %rdi
—— 只是同一指令的不同寻址 mode。从固定地址 stdout
处的内存加载变为从 RIP 寄存器(也称为程序计数器)的固定 位移 stdout
处从内存加载。另一方面,用mov $output, %rsi
加载固定地址output
,变成lea output(%rip), %rsi
。我建议您将此视为 一直是 一个 load-effective-address 操作,但是旧代码(在固定地址处执行 executable )能够用 move-immediate 而不是实际的 lea 指令来表达该操作。最后,call fprintf
变成了 call fprintf@PLT
。这告诉 linker 调用需要经过 过程 linkage table -- 你的书应该解释这是什么为什么需要它。
顺便说一句,我看到了这个汇编语言的其他几个问题,其中最重要的是:
- 字符串
"Yeet\n[=24=]"
属于 read-only 数据部分。 - x86-64 ABI 表示需要通过适当设置
eax
来告知像fprintf
这样的可变参数函数它们正在接收的浮点参数的数量。 enter
和leave
在 x86-64 上是不必要的。 (另外,enter
是一个非常慢的微编码指令,根本不应该使用。)
我会写这样的东西:
.section .rodata, "a", @progbits
.output:
.string "Yeet\n"
.section .text, "ax", @progbits
.globl main
.type main, @function
main:
sub , %rsp
mov stdout(%rip), %rdi
lea .output(%rip), %rsi
xor %eax, %eax
call fprintf@PLT
xor %eax, %eax
add , %rsp
ret
(你需要在函数的开头从%rsp
中减去8,然后再把它加回来,因为ABI说%rsp
必须总是16的倍数在 call
指令处 -- 这意味着它 不是 进入任何函数时的 16 的倍数,而是 %rsp
mod 16 是 8,因为 call
压入八个字节(return 地址)。作为 enter
和 [= 的副作用,你免费得到这个28=],但把那些拿出来,你必须手工完成。)