将两个.o文件链接在一起

时间:2016-01-30 20:33:09

标签: gcc assembly linker 64-bit nasm

我有两个.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文件?

1 个答案:

答案 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