【发布时间】:2016-10-02 17:16:06
【问题描述】:
我正在为类 C 语言构建编译器,并且我正在尝试将在程序集中实现的基本“void readString(int, char*)”函数与我的编译器生成的程序集链接起来。
编译后的类c文件是
void main () {
char t[20];
readString(7,t); // Read 7 bytes and place them in t buffer
}
编译后,生成的文件是:out.s文件(注意调用约定是:通过堆栈传递参数。在这种语言中,整数也有2字节大小):
.$0:
.globl main
main:
pushq %rbp
movq %rsp,%rbp
subq $20,%rsp
.$1:
movw $7,%ax # Push first argument in the stack
pushw %ax
.$2:
leaq -20(%rbp),%rax # Push address of the second arg in the stack
pushq %rax
.$3:
subq $8,%rsp # this is not important, needed for the convention being followed
pushq 16(%rbp) # pushing "access link",
call _readString
addq $26,%rsp # caller clears the "leftovers"
.$4:
.$main_0_11:
movq %rbp,%rsp
popq %rbp
ret
库函数的 reads.asm 中的代码:
.intel_syntax noprefix
.global _readString
_readString push rbp
mov rbp, rsp
push rdi
push rsi
mov rdi, [rbp+32] # First argument
mov rsi, [rbp+34] # Second Argument
mov rdx, rdi
doRead:
mov byte ptr [rsi], 0x00
xor rax, rax
mov rdi, rax
syscall # read syscall, reads up to $rdx bytes
or rax, rax # nothing read
jz finish #
add rsi, rax #
cmp byte ptr [rsi-1], 0x0a # check if last character read was '\n'
jne addZero #
sub rsi, 1 # if so, replace with '\0'
addZero:
mov byte ptr [rsi], 0x00
finish:
pop rsi
pop rdi
pop rbp
ret
链接/运行如下
$ gcc -c out.s
$ gcc -c reads.s
$ gcc out.o reads.o
$ ./a.out
[2] Segmentation fault ./a.out
描述调用约定的图片
【问题讨论】:
-
您几乎肯定在处理堆栈对齐问题。查找调用约定并确保您的堆栈在调用之前正确对齐。
-
你完全正确!谢谢 :-) 我会发布修复程序
-
IDK 如果您意识到,但您在这里发明了自己的非标准 64 位调用约定。 x86-64 System V 和 Windows 在寄存器中传递函数参数。 (当它们用完寄存器时,窄 args 仍然占用 8B 堆栈槽;它们从不使用 PUSHW)。请参阅 x86 tag wiki 了解 ABI 链接,并查看 gcc/clang 输出以了解它们是如何做到的。
-
抱歉,回答延迟。我确实意识到这一点,但这是一个深奥的编译器,所以它不需要符合任何标准约定。虽然我不知道 x86 标签 wiki。感谢您发布它。
标签: assembly compiler-construction x86 system-calls static-linking