2016-01-30 38 views
2

我有两个.asm文件,一个在另一个中调用函数。我的文件看起来像:将两个.o文件链接在一起

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文件?

+4

您需要使'factorial'成为全局符号。 –

回答

3

您需要global factorial汇编指令factorial.asm。没有这一点,它仍然在符号表中,但链接器不会考虑它在对象之间的链接。

factorial:这样的标签位于全局/外部符号和.loop1:这样的本地标签之间的一半(根本不存在于目标文件中)。本地标签是减少混乱拆卸的好方法,每个功能一个块,而不是在每个分支目标之后开始一个单独的块。

非全局符号仅用于反汇编和类似的东西,AFAIK。我认为他们会被除去,除了调试信息,还有strip


另外,还要注意imul rax, rdiruns faster,因为它不具有对结果的高位一半存放在%rdx,甚至计算。

另请注意,您可以通过objdump -Mintel -d获得intel语法反汇编。 Agner Fog的objconv也很不错,但它更加打字,因为默认情况下输出不会转到stdout。 (虽然外壳包装函数或脚本可以解决。)

无论如何,这将是更好:

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

+0

啊,我没有意识到标签开头的'.'使它变成了本地的;我认为这只是一些程序员使用的命名约定。谢谢你的提示! – luolimao

+1

@luolimao:我更新了一些更多的提示,包括收紧您的阶乘代码。你没有提及'main'看'rdx:rax'作为返回值。你可以通过在循环中执行'cmp rdi,1' /'jne'来保存迭代,以避免乘以1.或者甚至展开最后两次迭代以乘以2乘以一个移位。虽然可能不值得额外的指示。无论如何,是的,intel-syntax asm似乎并没有在任何地方记录在一起。指令集的东西在一个地方,汇编指令/标签的东西在YASM和NASM手册。 –

+0

啊,我以为'mul'比'imul'快,因为它比较老,但显然不是;你假设我不使用'factorial'中的'rdx'也是正确的。就'push rcx'而言,我开始用尽寄存器('rbx'包含一些其他数据),并且由于它是循环计数器(而不是'sub esp,8','call factorial“,”add esp,8“,并将计数器存储在”r12“中) – luolimao