Assembly 所以在它里面有递归函数
在某个项目的工作中,我遇到了一个问题,就是我不能建立这样的库。我得到的错误如下:在创建共享对象时,无法使用针对符号“”的重新定位R_X86_64_PC32;用-fPIC重新编译 最终我找到了根本原因。它是库中的递归函数。例如,我有以下众所周知的例子:Assembly 所以在它里面有递归函数,assembly,shared-libraries,x86-64,gnu,dynamic-linking,Assembly,Shared Libraries,X86 64,Gnu,Dynamic Linking,在某个项目的工作中,我遇到了一个问题,就是我不能建立这样的库。我得到的错误如下:在创建共享对象时,无法使用针对符号“”的重新定位R_X86_64_PC32;用-fPIC重新编译 最终我找到了根本原因。它是库中的递归函数。例如,我有以下众所周知的例子: .section .text .globl factorial .type factorial,STT_FUNC factorial: push %rbp mov %rsp,%rbp mov 16(%rbp),%rax
.section .text
.globl factorial
.type factorial,STT_FUNC
factorial:
push %rbp
mov %rsp,%rbp
mov 16(%rbp),%rax
cmp $1,%rax
je end_factorial
dec %rax
push %rax #this is how we pass the argument to function
call factorial
pop %rbx
inc %rbx
imul %rbx,%rax
end_factorial:
mov %rbp, %rsp
pop %rbp
ret
现在,让我们尝试构建共享库:
as -g -o fact.o fact.s
ld -shared fact.o -o libfact.so
ld: fact.o: relocation R_X86_64_PC32 against symbol `factorial' can not be used when making a shared object; recompile with -fPIC
如果我包装阶乘函数,如下所示:
.section .text
.globl fact
.type fact,STT_FUNC
fact:
factorial:
push %rbp
mov %rsp,%rbp
mov 16(%rbp),%rax
cmp $1,%rax
je end_factorial
dec %rax
push %rax #this is how we pass the argument to function
call factorial
pop %rbx
inc %rbx
imul %rbx,%rax
end_factorial:
mov %rbp, %rsp
pop %rbp
ret
我可以构建没有错误的so库
问题是:为什么在构建包含递归函数的共享库时出错? 另外,在这种情况下,静态链接可以正常工作。
谢谢
factorial
是一个全局标签,因此它可以进行符号插入。看见(还有,还有一些)
创建共享库时,call factorial
指令的目标不假定为同一文件中定义的factorial:
标签。这是因为您使用了.globl factorial
正如Jester指出的,您应该为调用
目标定义一个单独的本地标签,这样您就可以保留全局阶乘
名称
您可以创建一个更简单的“helper”函数,该函数使用自己的自定义调用约定,并且如果需要,不会为递归部分生成带有%rbp
的堆栈帧。(但是在堆栈上使用arg对于x86-64来说已经不是标准的)
您可以通过PLT或通过GOT间接调用内存,但不要这样做;您不希望每个
调用都会产生额外的开销,也不希望符号插入用传递%rdi
中第一个整数arg的正常调用约定实现替换非标准调用约定实现
说到这里,在堆栈上传递arg很慢。您确实需要保存/恢复某些内容,除非您。但您也不需要每次都使用%rbp
创建堆栈帧
在调用之前,您不需要保持16字节堆栈对齐,但在调用自己的私有函数时,您不需要这样做,因为这些函数并不关心这一点
当然,如果您关心性能的话,首先就不会使用递归实现,因为factorial
这样做的唯一原因是作为学习练习。重写为tail recursive()允许您()将调用
/ret
转换为jmp
,这当然会变成一个循环
相关:。二叉树遍历或Ackermann函数递归实现比迭代实现更容易,但阶乘
或斐波那契更难(在斐波那契的情况下,速度要慢得多)。对于全局符号,您需要使用PLT或GOT,即调用factorial@PLT
或呼叫*factorial@GOTPCREL(%rip)
。如果您愿意,您可以按相反的顺序进行包装,这样您就可以保留publicfactorial
符号,并使用一些局部符号进行递归。作为补充说明,GCC8应该知道优化递归调用以避免使用PLT(感谢)。非常感谢,现在已经很清楚了。阶乘只是一个例子。您还提到了16字节堆栈对齐。我认为在x86-64上应该是8字节对齐,不是吗?@AndrewBolotov:如果这回答了您的问题,请单击向上/向下投票箭头下的“接受”复选标记。:)