下面进入汇编程序怎么编程的介绍。
之前我就说过汇编的段一般分为3种
·数据段 .data
·bss段 .bss
·文本段 .text
PS; 不要忘了,本质上都是二进制代码.也就是说bss和数据段都是可移植的.为了给操作系统区分,才有这个分类.估计有些病毒就是用的这种方式吧.
注意这里的段都可以多次出现.C语言中的未初始化的全局变量和静态变量都是存储于这个bss段。所以你应该猜到了吧?对于bss段而言,全部内容都会用0来初始化。并且注意bss段并不包含在可执行文件中。但是正如之前所强调的.BSS存放的是未初始化的全局变量和静态变量,数据段存放的是初始化后的全局变量和静态变量。
注意,这里的三个段顺序。除了bss段通常放在文本段之前,其余的都可以随便放。但是这里就解释不通为什么C语言中全局变量作用域范围的问题了。在C语言中,全局变量申明的顺序直接就影响了作用范围,但是bss段的作用范围是全局的。这是为什么呢?
初步估计是因为编译器的编译方法的问题。因为编译原理中说过绝大部分的编译器为了效率是用,一边扫描预处理,一边构造参数表。所以在C中一定要先定义才能再用bss段中的数据。以后有时间自己用gcc看看这个构造过程。估计这单拿出来就可以写一篇文章了。
补充:
在linux下调试过了。linux下gcc根本就没有显示.bss段。直接用的.comm来定义的全局变量。我在测试程序中main之上定义一个a,main之下定义一个b。那么b其实是包含在main之中的。应该可以证实我的推论了。
不过,这里如果3个段是不能直接使用的。为了编译器链接各个不通的段(我个人比较接受这个观点,不过没有验证,如果有错误可以联系我),那么还要用一个.section来申明段。
声明一个段就像下面一样
.section.data 注意后面只能跟一种类型。但是可以跟多个标签
补充:
加不加.section其实不一定。我在linux下调试之后发现定义全局变量的位置前就没有这个标记。
这里其实要注意,在C语言中一个函数的局部变量是通过栈(或者寄存器)来存储的。所以当函数结束的时候,栈顶指针恢复。局部变量的数据全部丢失。但是静态变量是放在bss段中的,所以不会被丢失。
一般C中的常量字符串就可以存在.data中.不过还得注意在常量字符串之前需要加一个.asciz来标识是一个结尾带’\0’的串。所以对于调用C的库函数的话一定要用这个标识。如果用.ascii来标识的话则没有’\0’。为了和C一致,就忘了.asciz吧。
声明一个.asicz的字符串常量就像这样:
.section .rodata
zifuchuan:
.asicz “The Bigbang system”
这样Zifuchuan这个标签就相当于是一个字符串常量指针了。rodata标识只读的数据。
关于.rodata可能也用于const指定的变量吧。一般const变量会被编译器直接优化掉。我估计就当作data段了吧。
还有一个关键字需要介绍,那就是.globl了。这个声明的作用是来让外部程序可以访问用此命令声明的标签。一般程序的其实标签_start就需要用这个来声明。就像下面这样
.section .text
.globl _start
_start:
一般as编译器会吧这个当作程序的起始地址标签,相当于C中的main(有些汇编器起始标签可能也用main)。
注意,如果用GDB调试的时候将断点设置为_start标签后面的一行的话,很可能程序会一直执行完,所以需要在_start 标签之后加一条nop 指令。
前面讲到可以调用C的库函数。这里需要注意一点汇编没有C一样可以有预处理先include一个.h文件。那么计算机如何直到到哪去找这个函数呢?所以在链接.o文件的时候就需要链接库文件了。就象这样:
ld -dynamic-linker /lib/ld-linux.so.2 –o 生成可执行程序名字 –lc .o文件
这是动态链接的范例。静态链接直接就ld将.o文件一个个列出来就行了。中间的可以省略。
PS:貌似ld-linux.so.2也有_start,所以可以直接运行这个库。