39 :C语言与汇编语言混合编程

博客围绕C语言函数调用和汇编语言编程展开。介绍了C语言函数调用过程,包括寄存器压栈、函数调用与返回过程等,还提及gcc编译器栈帧布局。同时阐述用汇编语言编写Linux应用程序的注意事项,如版本问题,以及汇编和C混合编程的要点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

编译过程

编译小知识

C语言中函数是如何进行调用的?

寄存器压栈过程

C语言函数调用过程

函数调用过程

函数返回过程

C语言中的调用约定

gcc编译器使用的栈帧布局

ebp是函数调用以及函数返回的核心寄存器 

用汇编语言编写Linux应用程序

交互关键字

汇编和C混合编程注意事项

小结

思考


编译过程

编译小知识

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

交互关键字

  1. global : 从汇编语言中导出符号(变量或函数)
  2. 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混合编程注意事项

  1. 相同的目标文件格式(如:elf格式)
  2. 相同的函数调用约定(如:cdecl调用约定)
  3. 相同的活动记录(栈帧)结构(如: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'

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

repinkply

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值