关于从 Assembly 调用 C 函数,反之亦然
About calling C function from Assembly and vice versa
我尝试过从 C 调用 ASM,反之亦然。至少目前它运行完美,但我有疑问。这是我的代码:
test.S 已关注:
.text
.global _start
.global _main
.type _main, @function
.global writeMe
.type writeMe, @function
_start:
#; Write hello world for 5 times.
#; Jump exit and call C function after that.
#; C function calls writeMe assembly function
#; Exit with syscall
xorl %ecx, %ecx #; ecx = 0
call _get_eip #; get eip without labels. Just for research.
pushl %eax #; push to stack
incl %ecx #; ++ecx
pushl %ecx #; push to stack
movl $len,%edx #; tell length of string
movl $msg,%ecx #; tell string position
movl ,%ebx #; fd = stdout
movl ,%eax #; syscall = write
int [=10=]x80 #; perform call
popl %ecx #; pop counter
movl %ecx, %eax #; eax = ecx
cmpl [=10=]x5, %eax #; compare 0x5 and eax
je _exit #; eax == 0x5, jump exit
_jmp:
popl %eax #; pop instruction pointer
jmpl %eax #; jmp
_exit:
call _main #; call C function
movl [=10=],%ebx #; EXIT_SUCCESS
movl ,%eax #; syscall = exit
int [=10=]x80 #; perform call
ret
_get_eip: #; function for getting eip
popl %eax #; pop eip
pushl %eax #; push again to return
ret #; return location
writeMe: #; function for writing, called from C
popl (__eip) #; pop return location
popl %ecx #; pop first argument, msg
popl %edx #; pop second argument, len
movl , %ebx #; fd = stdout
movl , %eax #; syscall = write
int [=10=]x80 #; perform call
pushl (__eip) #; push return location
ret #; return location
writeMe2: #; function for writing, called from C
popl %ecx #; pop return location
popl %ecx #; pop first argument, msg
popl %edx #; pop second argument, len
movl , %ebx #; fd = stdout
movl , %eax #; syscall = write
int [=10=]x80 #; perform call
subl [=10=]x0C, %esp #; restore stack
ret
.data
__eip: .long
msg:
.ascii "Hello, world!\n[=10=]"
len = . - msg
main.C 已关注:
extern void writeMe(const char *msg, int len);
int _strlen(const char *msg) {
int _len = 0;
while (*msg++ != 0x0)
_len++;
return _len;
}
void _main() {
const char * szmsg = "Hello, world!\n";
writeMe(szmsg, _strlen(szmsg));
}
我的输出符合我的预期。
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
我的问题如下:
1)
.type writeMe, @function
这段代码是什么意思? "GCC" 的信息?它有什么作用?我必须这样做吗?
2)
我必须写这个通知操作吗?如果函数在 C 文件中声明?
.type _main, @function
_main是在C文件中声明的,我一定要写吗?
3)
popl (__eip) #; pop return location
popl %ecx #; pop first argument, msg
popl %edx #; pop second argument, len
........
pushl (__eip) #; push return location
我在writeMe中使用过这段代码,安全吗?换句话说,我可以弹出参数,还是 GCC 会自动弹出它?
popl %ecx #; pop return location
popl %ecx #; pop first argument, msg
popl %edx #; pop second argument, len
....
subl [=15=]x0C, %esp #; restore stack
我在第二个函数中使用了这段代码。我问你,哪一个是安全正确的?
4)
从 C 调用汇编函数后是否需要恢复寄存器?
(我听说我必须恢复EDI,但其他人呢?)
感谢您的所有回复。
1) 设置函数的符号类型。不需要,除非在特殊情况下,例如共享库。
2) 不,编译器已经为 C 中定义的函数完成了。
3) 这两个都是错误的。您应该访问相对于 esp
的参数,或者在设置标准堆栈框架之后,相对于 ebp
.
4) 您应该阅读相应的 ABI 文档以获取有关调用约定的信息。通常,您可以使用 eax
、ecx
和 edx
,其余必须保留。
我尝试过从 C 调用 ASM,反之亦然。至少目前它运行完美,但我有疑问。这是我的代码:
test.S 已关注:
.text
.global _start
.global _main
.type _main, @function
.global writeMe
.type writeMe, @function
_start:
#; Write hello world for 5 times.
#; Jump exit and call C function after that.
#; C function calls writeMe assembly function
#; Exit with syscall
xorl %ecx, %ecx #; ecx = 0
call _get_eip #; get eip without labels. Just for research.
pushl %eax #; push to stack
incl %ecx #; ++ecx
pushl %ecx #; push to stack
movl $len,%edx #; tell length of string
movl $msg,%ecx #; tell string position
movl ,%ebx #; fd = stdout
movl ,%eax #; syscall = write
int [=10=]x80 #; perform call
popl %ecx #; pop counter
movl %ecx, %eax #; eax = ecx
cmpl [=10=]x5, %eax #; compare 0x5 and eax
je _exit #; eax == 0x5, jump exit
_jmp:
popl %eax #; pop instruction pointer
jmpl %eax #; jmp
_exit:
call _main #; call C function
movl [=10=],%ebx #; EXIT_SUCCESS
movl ,%eax #; syscall = exit
int [=10=]x80 #; perform call
ret
_get_eip: #; function for getting eip
popl %eax #; pop eip
pushl %eax #; push again to return
ret #; return location
writeMe: #; function for writing, called from C
popl (__eip) #; pop return location
popl %ecx #; pop first argument, msg
popl %edx #; pop second argument, len
movl , %ebx #; fd = stdout
movl , %eax #; syscall = write
int [=10=]x80 #; perform call
pushl (__eip) #; push return location
ret #; return location
writeMe2: #; function for writing, called from C
popl %ecx #; pop return location
popl %ecx #; pop first argument, msg
popl %edx #; pop second argument, len
movl , %ebx #; fd = stdout
movl , %eax #; syscall = write
int [=10=]x80 #; perform call
subl [=10=]x0C, %esp #; restore stack
ret
.data
__eip: .long
msg:
.ascii "Hello, world!\n[=10=]"
len = . - msg
main.C 已关注:
extern void writeMe(const char *msg, int len);
int _strlen(const char *msg) {
int _len = 0;
while (*msg++ != 0x0)
_len++;
return _len;
}
void _main() {
const char * szmsg = "Hello, world!\n";
writeMe(szmsg, _strlen(szmsg));
}
我的输出符合我的预期。
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
Hello, world!
我的问题如下:
1)
.type writeMe, @function
这段代码是什么意思? "GCC" 的信息?它有什么作用?我必须这样做吗?
2)
我必须写这个通知操作吗?如果函数在 C 文件中声明?
.type _main, @function
_main是在C文件中声明的,我一定要写吗?
3)
popl (__eip) #; pop return location
popl %ecx #; pop first argument, msg
popl %edx #; pop second argument, len
........
pushl (__eip) #; push return location
我在writeMe中使用过这段代码,安全吗?换句话说,我可以弹出参数,还是 GCC 会自动弹出它?
popl %ecx #; pop return location
popl %ecx #; pop first argument, msg
popl %edx #; pop second argument, len
....
subl [=15=]x0C, %esp #; restore stack
我在第二个函数中使用了这段代码。我问你,哪一个是安全正确的?
4) 从 C 调用汇编函数后是否需要恢复寄存器? (我听说我必须恢复EDI,但其他人呢?)
感谢您的所有回复。
1) 设置函数的符号类型。不需要,除非在特殊情况下,例如共享库。
2) 不,编译器已经为 C 中定义的函数完成了。
3) 这两个都是错误的。您应该访问相对于 esp
的参数,或者在设置标准堆栈框架之后,相对于 ebp
.
4) 您应该阅读相应的 ABI 文档以获取有关调用约定的信息。通常,您可以使用 eax
、ecx
和 edx
,其余必须保留。