将两个 .o 文件链接在一起
Linking two .o files together
我有两个 .asm 文件,一个在另一个内部调用函数。我的文件看起来像:
mainProg.asm:
global main
extern factorial
section .text
main:
;---snip---
push rcx
call factorial
pop rcx
;---snip---
ret
factorial.asm:
section .text
factorial:
cmp rdi, 0
je l2
mov rax, 1
l1:
mul rdi
dec rdi
jnz l1
ret
l2:
mov rax, 1
ret
(是的,我可以通过实施改进一些地方。)
我尝试按照How to link two nasm source files:
中的步骤编译它们
$ nasm -felf64 -o factorial.o factorial.asm
$ nasm -felf64 -o mainProg.o mainProg.asm
$ gcc -o mainProg mainProg.o factorial.o
前两个命令没有问题,但最后一个失败
mainProg.o: In function `main':
mainProg.asm:(.text+0x22): undefined reference to `factorial'
collect2: error: ld returned 1 exit status
更改目标文件的顺序不会更改错误。
我尝试搜索 link 两个 .o 文件的解决方案,并找到了问题 C Makefile given two .o files。正如那里提到的,我 运行 objdump -S factorial.o
得到了
factorial.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <factorial>:
0: 48 83 ff 00 cmp [=14=]x0,%rdi
4: 74 0e je 14 <l2>
6: b8 01 00 00 00 mov [=14=]x1,%eax
000000000000000b <l1>:
b: 48 f7 e7 mul %rdi
e: 48 ff cf dec %rdi
11: 75 f8 jne b <l1>
13: c3 retq
0000000000000014 <l2>:
14: b8 01 00 00 00 mov [=14=]x1,%eax
19: c3 retq
这与源文件几乎相同。明明有factorial
功能,为什么ld
检测不到呢? link 两个 .o 文件是否有不同的方法?
您需要 factorial.asm
中的 global factorial
汇编指令。没有它,它仍然在符号 table 中,但链接器不会将其视为对象之间的链接。
像 factorial:
这样的标签介于 global/external 符号和像 .loop1:
这样的本地标签之间(根本不存在于目标文件中)。局部标签是减少反汇编混乱的好方法,每个函数一个块而不是每个分支目标之后开始一个单独的块。
非全局符号只对反汇编和类似的东西有用,AFAIK。我认为它们会连同调试信息一起被 strip
.
剥离
另外,注意 imul rax, rdi
runs faster,因为它不必将结果的高半部分存储在 %rdx
中,甚至不需要计算它。
另请注意,您可以 objdump -Mintel -d
进行英特尔语法反汇编。 Agner Fog 的 objconv
也非常好,但它需要更多的输入,因为默认情况下输出不会转到标准输出。 (虽然 shell 包装函数或脚本可以解决这个问题。)
无论如何,这样会更好:
global factorial
factorial:
mov eax, 1 ; depending on the assembler, might save a REX prefix
; early-out branch after setting rax, instead of duplicating the constant
test rdi, rdi ; test is shorter than compare-against-zero
jz .early_out
.loop: ; local label won't appear in the object file
imul rax, rdi
dec rdi
jnz .loop
.early_out:
ret
为什么 main
push/pop rcx
?如果您正在编写遵循标准 ABI 的函数(除非有很大的性能提升,否则绝对是个好主意),并且您希望某些东西在 call
中存活下来,请将其保存在调用保留寄存器中,例如 rbx
.
我有两个 .asm 文件,一个在另一个内部调用函数。我的文件看起来像:
mainProg.asm:
global main
extern factorial
section .text
main:
;---snip---
push rcx
call factorial
pop rcx
;---snip---
ret
factorial.asm:
section .text
factorial:
cmp rdi, 0
je l2
mov rax, 1
l1:
mul rdi
dec rdi
jnz l1
ret
l2:
mov rax, 1
ret
(是的,我可以通过实施改进一些地方。)
我尝试按照How to link two nasm source files:
中的步骤编译它们$ nasm -felf64 -o factorial.o factorial.asm
$ nasm -felf64 -o mainProg.o mainProg.asm
$ gcc -o mainProg mainProg.o factorial.o
前两个命令没有问题,但最后一个失败
mainProg.o: In function `main':
mainProg.asm:(.text+0x22): undefined reference to `factorial'
collect2: error: ld returned 1 exit status
更改目标文件的顺序不会更改错误。
我尝试搜索 link 两个 .o 文件的解决方案,并找到了问题 C Makefile given two .o files。正如那里提到的,我 运行 objdump -S factorial.o
得到了
factorial.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <factorial>:
0: 48 83 ff 00 cmp [=14=]x0,%rdi
4: 74 0e je 14 <l2>
6: b8 01 00 00 00 mov [=14=]x1,%eax
000000000000000b <l1>:
b: 48 f7 e7 mul %rdi
e: 48 ff cf dec %rdi
11: 75 f8 jne b <l1>
13: c3 retq
0000000000000014 <l2>:
14: b8 01 00 00 00 mov [=14=]x1,%eax
19: c3 retq
这与源文件几乎相同。明明有factorial
功能,为什么ld
检测不到呢? link 两个 .o 文件是否有不同的方法?
您需要 factorial.asm
中的 global factorial
汇编指令。没有它,它仍然在符号 table 中,但链接器不会将其视为对象之间的链接。
像 factorial:
这样的标签介于 global/external 符号和像 .loop1:
这样的本地标签之间(根本不存在于目标文件中)。局部标签是减少反汇编混乱的好方法,每个函数一个块而不是每个分支目标之后开始一个单独的块。
非全局符号只对反汇编和类似的东西有用,AFAIK。我认为它们会连同调试信息一起被 strip
.
另外,注意 imul rax, rdi
runs faster,因为它不必将结果的高半部分存储在 %rdx
中,甚至不需要计算它。
另请注意,您可以 objdump -Mintel -d
进行英特尔语法反汇编。 Agner Fog 的 objconv
也非常好,但它需要更多的输入,因为默认情况下输出不会转到标准输出。 (虽然 shell 包装函数或脚本可以解决这个问题。)
无论如何,这样会更好:
global factorial
factorial:
mov eax, 1 ; depending on the assembler, might save a REX prefix
; early-out branch after setting rax, instead of duplicating the constant
test rdi, rdi ; test is shorter than compare-against-zero
jz .early_out
.loop: ; local label won't appear in the object file
imul rax, rdi
dec rdi
jnz .loop
.early_out:
ret
为什么 main
push/pop rcx
?如果您正在编写遵循标准 ABI 的函数(除非有很大的性能提升,否则绝对是个好主意),并且您希望某些东西在 call
中存活下来,请将其保存在调用保留寄存器中,例如 rbx
.