参考
https://blog.51cto.com/13475106/category6.html及狄泰软件相关课程
从下图的编译过程就可以很明显的看出可执行文件的生成过程
编译小知识
1.源文件编译后得到目标文件-二进制文件
2.不同语言可编译相同格式的目标文件
3.链接器负责将目标文件组装得到可执行文件
在这里出现的一个问题-C语言中的函数调用是如何进行的?
在这里需要对函数调用时栈的变化进行解释
ebp指向关键位置上半部分-高地址位置,包含了函数调用之前的信息,下部分低地址处函数调用后所使用的信息。
汇编语言栈的变化-执行入栈出栈的操作
C语言函数调用(如上图所示)-ebp指向当前栈顶,esp指向的也是栈顶,当函数返回时,将函数所使用堆空间释放,将栈顶指针寄存器位置改变,做法是将esp移动到基准位置ebp位置,之后ebp恢复之前的值(将其弹出)
,此时esp指向的是返回地址,结束之后esp指向参数的位置,在这里需要介绍调用约定的概念
调用约定
1.参数从右到左入栈
2.函数调用者负责参数的入栈出栈
3.函数本身根据约定使用栈中参数
GCC编译器使用的栈帧布局
ebp是函数调用以及函数返回的核心寄存器
1.ebp寄存器位当前栈帧的基准
2.通过ebp能够获取返回值地址、参数、局部变量等
在接下来的会用实验来进行验证
使用汇编语言编写Linux可执行程序
a.定义_start标签作为程序执行的起点
b.通过int 0x80使用内核服务(执行系统调用)
global _start ;标签 程序入口处 [section .data] vstr db "D.T.Software",0x0A [section .text] _start: ;app entry mov edx,13 mov ecx,vstr ;"D.T.Software " mov ebx,1 mov eax,4 ;sys_write int 0x80 mov ebx,0 mov eax,1 ;sys_exit int 0x80
交互关键字
1.global-从汇编语言中导出符号(变量或函数)
2.extern-使用外部文件中定义的符号(变量或函数)
示例分析
实验运行结果-在linux下编程
混合编程注意事项
1.相同的目标文件格式--elf格式
2.相同的函数调用约定--cdecl调用约定
3.相同的活动记录(栈帧)结构-ebp基准
实验
代码-新建entry.asm、main.c、test.c
代码分别为
;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() { char* delphi = "Delphi "; print(vstr, vlen); return 0; } test.c extern void print(char*, int len); extern char vstr[]; extern int vlen; int c_func() { char* delphi = "Delphi "; print(vstr, vlen); return 0; }
运行结果-c语言与汇编
在这里需要使用gcc将其编译成目标文件,最后进行链接
小结
C语言与汇编语言在满足以下条件时可以混合编程
1.遵循相同的函数调用约定
2.遵循相同的目标文件格式