我有两个.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
更改目标文件的顺序不会更改错误。
我尝试搜索链接两个.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 $0x0,%rdi
4: 74 0e je 14 <l2>
6: b8 01 00 00 00 mov $0x1,%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 $0x1,%eax
19: c3 retq
与源文件非常相似。它显然包含factorial
函数,那么为什么ld
没有检测到它?是否有不同的方法来链接两个.o文件?
答案 0 :(得分:3)
global factorial
中需要factorial.asm
汇编程序指令。没有它,它仍然在符号表中,但链接器不会考虑它在对象之间的链接。
像factorial:
这样的标签位于全局/外部符号和.loop1:
之类的本地标签之间(根本不存在于目标文件中)。局部标签是一种不太混乱的反汇编的好方法,每个函数只有一个块,而不是在每个分支目标后开始的单独块。
非全局符号仅适用于反汇编和AFAIK之类的东西。我认为它们会随着调试信息被strip
删除。
另请注意imul rax, rdi
runs faster,因为它不必将结果的高半部分存储在%rdx
中,甚至不能计算出来。
另请注意,您可以objdump -Mintel -d
获取英特尔语法反汇编。 Agner Fog的objconv
也非常好,但它的输入更多,因为输出默认情况下不会转到stdout。 (虽然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
推/弹rcx
?如果您正在编写遵循标准ABI的函数(绝对是一个好主意,除非有很大的性能提升),并且您希望某些内容能够在call
中存活,请将其保存在调用保留的寄存器中比如rbx
。