我正在尝试在C程序中调用程序集中使用函数。这个函数应该调用一个libc函数,比方说printf()
,但是我一直遇到分段错误。
在.c文件中,我有函数的声明,例如
int do_shit_in_asm()
在.asm文件中我有
.extern printf
.section .data
printtext:
.ascii "test"
.section .text
.global do_shit_in_asm
.type do_shit_in_asm, @function
do_shit_in_asm:
pushl %ebp
movl %esp, %ebp
push printtext
call printf
movl %ebp, %esp
pop %ebp
ret
任何指针评论都会受到赞赏。
as func.asm -o func.o
gcc prog.c func.o -o prog
答案 0 :(得分:16)
将push printtext
更改为push $printtext
。
实际上,您正在从地址printtext
加载一个值并推送它,而不是推送地址。因此,您将'test'
作为32位数而不是指针传递,而printf
正试图将其解释为地址并崩溃。
答案 1 :(得分:9)
开始使用汇编语言函数的最佳方法之一是在C中编写类似的函数,然后使用生成汇编列表(gcc上的-S
)的编译器开关构建它。然后,您可以研究编译器所做的输出,并根据需要进行修改。
如果您正在调用使用不同调用约定的printf
等函数(因为参数数量可变),这将非常有用。调用这些函数可能与调用非varargs函数完全不同。
答案 2 :(得分:6)
问题在于我正在使用
pushl printtext
而不是
pushl $printtext
感谢大家的帮助,抱歉浪费你的时间:P
答案 3 :(得分:2)
之后:
push printtext
call printf
你想:
addl $4, %esp
进一步说明:
因为您正在使用x86 Linux,所以我假设调用约定要求被调用者清理参数。因为你在调用printf
之前按下了一个指针,所以在该函数的ret
指令发生后,你的堆栈会被4点关闭。
更新
是的,好的,我已经习惯了英特尔语法,所以我在脑子里得到了倒退的顺序。实际上,addl
缺少回esp
并不重要,因为您正在esp
附近正确恢复ret
。我的下一个猜测是,你传递给printf
的字符串缺少一个空终止符...让我看看gas
做了什么......
更新2:
好的,gas
null为你终止字符串,所以我想我的第二次预感错了。看起来你发现了这个问题,所以重点是没有意义。