目录
编译过程
编译小知识
C语言中函数是如何进行调用的?
寄存器压栈过程
调用函数 func 会将函数的 返回地址 压入到栈中。此时,esp栈指针寄存器 会往下移动。
当程序执行 ret 的时候,程序恢复到 原来 调用 call的位置。在汇编代码层面,call 做的事情 和 ret做的事情,正好相反。
C语言函数调用过程
函数调用过程
函数返回过程
C语言中的调用约定
gcc编译器使用的栈帧布局
ebp是函数调用以及函数返回的核心寄存器
用汇编语言编写Linux应用程序
global _start
[section .data] ; 数据段
vstr db "D.T.Software",0X0A ; 0X0A 是换行符
[section .text] ; 代码段
_start: ;程序标签起点
mov edx,13 ;字符串长度
mov ecx,vstr ;哪些字符串
mov ebx,1 ;指定功能号,该功能号对应 sys_write
mov eax,4 ; sys_write,用于将制定长度的字符串输出到标准输出设备
int 0x80 ; 触发软中断,执行 sys_write 系统调用
mov ebx,0 ;将返回值 0 存储到 `ebx` 寄存器中,表示程序正常退出
mov eax,1 ; sys_exit,1号系统调用,该系统调用是 `sys_exit`,用于结束程序
int 0x80 ; 触发软中断,执行 `sys_exit` 系统调用,程序结束。
编译链接,运行如下所示:
delphi@delphi-vm:~$ nasm -f elf entry.asm -o entry.o
delphi@delphi-vm:~$ ld -s entry.o -o app.out
delphi@delphi-vm:~$ ./app.out
D.T.Software
delphi@delphi-vm:~$
注意Linux版本
在ubuntu22.04系统上,用上述的命令会报以下错误。
nasm -f elf entry.asm -o entry.o
ld -s entry.o -m elf_x86_64 -o app.out
ld: i386 architecture of input file `entry.o' is incompatible with i386:x86-64 output
原因是:
由于 `entry.asm` 生成的目标文件 `entry.o` 的架构与链接器 `ld` 的目标架构不匹配导致的。具体来说,`entry.o` 是 i386 架构,而链接器使用的目标架构是 x86-64。因此,链接器无法正确地将 `entry.o` 与其他目标文件一起链接成可执行文件。
修改成如下这样就可以了。
nasm -f elf entry.asm -o entry.o
ld -s entry.o -m elf_x86_64 -o app.out
ld: i386 architecture of input file `entry.o' is incompatible with i386:x86-64 output
ld -s entry.o -m elf_i386 -o app.out
./app.out
D.T.SoftWare
交互关键字
- global : 从汇编语言中导出符号(变量或函数)
- extern : 使用外部文件中定义的符号(变量或函数)
实例分析:
接着来看下一个demo:
entry.asm
global _start
global vstr
global vlen
global print
extern c_func;
[section .data]
vstr db "D.T.Software",0X0A
vlen dd $ - vstr
[section .text]
_start:
mov ebp,0
call c_func
call exit
print:
push ebp
mov ebp,esp
mov edx,[ebp + 12] ; 拿到第二个参数
mov ecx,[ebp + 8] ; 拿到第一个参数
mov ebx,1
mov eax,4
int 0x80
pop ebp
ret
exit:
mov ebx,0
mov eax,1
int 0x80
main.c
extern void print(char*,int len);
extern char vstr[];
extern int vlen;
int c_func()
{
print(vstr,vlen);
return 0;
}
编译,链接,运行:
delphi@delphi-vm:~$ nasm -f elf entry.asm -o entry.o
delphi@delphi-vm:~$ gcc -c main.c -o main.o
delphi@delphi-vm:~$ ld -s entry.o main.o -o app.out
delphi@delphi-vm:~$ ./app.out
D.T.Software
delphi@delphi-vm:~$
接下来,将 main.c 改成如下的程序:
extern void print(char*,int len);
extern char vstr[];
extern int vlen;
int c_func()
{
char* delphi = "Delphi\n";
//print(vstr,vlen);
print(delphi,7);
return 0;
}
现在只需要编译 c文件,然后链接,运行就行:
delphi@delphi-vm:~$ gcc -c main.c -o main.o
delphi@delphi-vm:~$ ld -s entry.o main.o -o app.out
delphi@delphi-vm:~$ ./app.out
Delphi
delphi@delphi-vm:~$
汇编和C混合编程注意事项
- 相同的目标文件格式(如:elf格式)
- 相同的函数调用约定(如:cdecl调用约定)
- 相同的活动记录(栈帧)结构(如:ebp基准)
小结
思考
可以把 main.c 文件中的 c_func 函数名字,改为 main函数名吗?
当然不行,编译可以编译通过,但是会链接出错。
delphi@delphi-vm:~$ nasm -f elf entry.asm -o entry.o
delphi@delphi-vm:~$ gcc -c main.c -o main.o
delphi@delphi-vm:~$ ld -s entry.o main.o -o app.out
entry.o: In function `_start':
entry.asm:(.text+0x6): undefined reference to `c_func'