您的位置:首页 > 理论基础

汇编与C之间的关系

2017-08-13 22:24 113 查看
1. 函数调用

注意函数调用和返回过程中的这些规则:
1. 函数参数压栈传递,并且是从右向左依次压栈。
2.  ebp总是指向当前栈帧的栈底 ,而esp指向栈顶,在函数执行过程中esp随着压栈和出栈操作随时变化,而ebp是不动的,函数的参数和局部变量都是通过ebp的值加上一个偏移量来访问
 
2. Main函数、启动列程和退出状态


 
为什么汇编程序的入口是_start,而C程序的入口是main函数呢?

Gcc只是一个外壳而不是真正的编译器,真正的c编译器是:/uer/lib/gcc/i486-gun/4.3.2/ccl;同样,ld的连接器是/uer/lib/gcc/i486-gun/4.3.2/collect2。编译链接具体步骤如下:
(1)main.c被ccl编译成汇编程序/tmp/ccRGDpua.s
(2)这个汇编被as汇编成目标文件/tmp/ccidnZ1d.o
(3)这个目标文件连同另外几个目标文件(crt1.o, crti.o, crtbegin.o, crtend.o, crtn.o)一起链接成可执行文件main。
即可执行文件main是由main.o和编译器提供的另外几个目标文件一起链接生成的。
 
$ nm  /usr/lib/crt1.o

00000000  R    _IO_stdin_used

00000000  D  __data_start

           U  __libc_csu_fini

           U  __libc_csu_init

           U  __libc_start_main
00000000  R   _fp_hw

00000000  T  _start

00000000  W  data_start

           U  main
U main这一行表示main这个符号在crt1.o中用到了,但是没有定义(U表示Undefined),因此需要别的目标文件提供一个定义并且和crt1.o链接在一起。

C程序的入口其实是crt1.o提供的 _start,它首先做一些初始化工作(启动例程),然后调用我们编写的main函数。所以,_start 才是真正的入口点,而main函数是被 _start调用的。
 
由于main函数是被启动例程调用的,所以mian函数return时就返回到启动例程中,main的返回值被启动例程得到,如果将启动例程表示成等价代码:
                        exit( main(argc,argv) )
即启动例程得到main的返回值后,立刻用它做参数调用 _exit函数,成为进程的退出状态。
番:如果声明一个函数的返回值类型是int ,如果缺了return则返回值不确定,编译器通常是会报警告的,但如果某个分支控制流程调用了exit或_exit而不写return,编译器是允许的,因为它都没有机会返回了,指不指定返回值也就无所谓了。
 
3.变量的存储布局 
.bass : 用于存放程序中未初始化的全局变量的一块内存区域(未手动初始化),但不分配空间,只记录所需空间 (不在可执行文件中)
.data : 用于存放程序中已初始化的全局变量的一块内存区域(手动初始化),分配空间,数据保存在目标文件中
.rodata : 存放C中字符串和#define定义的常量
.text : 存放程序执行代码的一块内存区域
一个程序本质上是由bass data text段组成的
 
3.1 存储类修饰符
static,用它修饰的变量的存储空间是静态分配的,用它修饰的文件作用域的变量或函数具有Internal Linkage。
auto,用它修饰的变量在函数调用时自动在栈上分配存储空间,函数返回时自动释放(auto可以省略不写 )
register,编译器对于用register修饰的变量会尽可能分配一个专门的寄存器来存储 (现用的少)
extern
typede
 
4. C内联汇编
gcc提供了一种扩展语法可以在C代码中使用内联汇编(Inline Assembly)。最简单的

格式是 __asm__("assembly code") 。
 
5. Volatile限定符
为什么要用volatile?
为了防止编译器把设备寄存器当成普通的内存单元而给优化掉。
番:普通的内存单元,只要程序不去改写它,它就不会变,如果对一个普通的内存单元连续做三次写操作,只有最后一次的值会保存到内存单元中,所以前两次写操作是多余的,可以优化掉 。
设备寄存器往往具有以下特性: 
(1)设备寄存器中的数据不需要改写就可以自己发生变化,每次读上来的值都可能不一样。

(2)连续多次向设备寄存器中写数据并不是在做无用功,而是有特殊意义的
在C语言中可以用volatile限定符修饰变量,就是告诉编译器,即使在编译时指定了优化选项,每次读这个变量仍然要老老实实从内存读取,每次写这个变量也仍然要老老实实写回内存,不能省略任何步骤。

 
 
 
 
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  计算机 数据