linux平台学x86汇编(四):从“hello world!”开始
2015-05-06 23:56
267 查看
【版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途】
汇编语言程序由定义好的段构成,每个段有各自的目的。三个最常用的的段如下:数据段、bss段、文本段。文本段是可执行程序内声明指令码的地方,所有汇编程序都必须有文本段,数据段和bss段是可选的,但是在程序中经常使用。数据段声明带有初始值的变量,bss段声明使用0值初始化的数据元素,这些元素常用作汇编程序的缓冲区。下图为汇编语言程序的布局。
GNU汇编器使用.section命令语句声明段。.section语句只有一个参数——段类型。上图的布局是汇编程序安排段的一般方式。bss段总是在文本段之前,数据段可以在文本段之后,但将其放在前面更容易阅读和理解。
如其它高级语言一样,汇编语言程序在链接为可执行程序时,链接器必须要知道程序中的起点是什么,就像c语言中的main函数一样。GNU汇编器使用一个默认标签_start作为应用程序的入口点,如果链接器找不到这个标签就会生成错误消息。如果编写被外部汇编语言或C语言程序使用的一组工具,需要使用.globl命令声明每个函数段标签,.globl命令是声明外部程序可以访问的程序标签。所以,一般编写汇编语言的基础模板是这样的:
有了模板之后就可以开始创建汇编语言程序,我们也像学习高级语言一样,从最简单的程序开始。
编写汇编语言主要工作在编写.text部分,该部分主要编写要实现应用程序的指令码。汇编语言允许程序员使用助记符表示指令码,助记符使程序员可以使用英语样式的词表示各个指令码,汇编器可以很容易地把汇编语言助记符转换为原始指令码。这样使得汇编程序员不必了解指令码每个字节表示什么,子需要使用更加容易记忆的助记符(如push、mov、sub、call)来表示指令码。比如下面这个指令码例子:
可以写为如下的汇编代码:
数据段
和高级语言一样,编写汇编语言程序都需要管理某种类型的变量,在汇编语言中数据段和bss段都提供了定义变量的方法。数据段是最常见的定义变量的位置。
在数据段中定义变量需要两个语句:一个符号、一个命令。
符号类似于C语言程序中变量的名称,它只是汇编器试图访问内存位置时用作引用指针的一个位置。
命令实现为符号引用的数据元素保留多少字节,类似于高级语言指定数据类型。汇编语言使用如下的命令:.ascii、.asciz、.byte、.double、.float、.int、.long、.octa、.quad、.short、.single。例如定义变量如下:
数据段主要用于定义变量数据,不过也可以使用命令.equ定义静态数据符号,类似于高级语言的定义常量。例如:
引用静态数据时,需要在变量名称前加$符号,比如把var的值传送到EAX寄存器:
bss段
在bss段中定义数据元素和在数据段中定义有些不同,不需要指定特定数据类型。
GNU汇编器使用两个命令声明缓冲区,.comm命令声明未初始化的数据的通用内存区域;.lcomm命令声明未初始化的数据本地通用内存区域,该区域不允许从本地汇编代码之外进行访问。其使用格式为:
symbol是赋给内存区域的符号,length是内存区域中包含的字节数量。
在bss段中声明数据的一个好处是数据不包含在可执行程序中,在数据段中定义数据必须包含在可执行程序中。
下面来看看汇编语言的hello world 程序:
编译执行结果如下:
Linux 下的系统调用是通过中断(int 0x80)来实现的。在执行 int 0X80 指令时,寄存器 eax 中存放的是系统调用号,而传给系统调用的参数则必须按顺序放到寄存器 ebx,ecx,edx,esi,edi 中,当系统调用完成之后,返回值可以在寄存器 eax 中获得。系统调用号4对应的函数调用是
sys_write,在应用上其函数定义如下:
ssize_t write(int fd, const void *buf, size_t count);
参数 fd、buf 和 count 分别存在寄存器 ebx、ecx 和 edx 中,而系统调用号 SYS_write 则放在寄存器 eax 中,当 int 0x80 指令执行完毕后,返回值可以从寄存器 eax 中获得。
注意,如果使用gcc编译的话有一个问题,gcc查找main标签而不是_start标签,所以把程序中的_start改为main直接使用gcc编译链接就没有问题了。
汇编语言程序由定义好的段构成,每个段有各自的目的。三个最常用的的段如下:数据段、bss段、文本段。文本段是可执行程序内声明指令码的地方,所有汇编程序都必须有文本段,数据段和bss段是可选的,但是在程序中经常使用。数据段声明带有初始值的变量,bss段声明使用0值初始化的数据元素,这些元素常用作汇编程序的缓冲区。下图为汇编语言程序的布局。
GNU汇编器使用.section命令语句声明段。.section语句只有一个参数——段类型。上图的布局是汇编程序安排段的一般方式。bss段总是在文本段之前,数据段可以在文本段之后,但将其放在前面更容易阅读和理解。
如其它高级语言一样,汇编语言程序在链接为可执行程序时,链接器必须要知道程序中的起点是什么,就像c语言中的main函数一样。GNU汇编器使用一个默认标签_start作为应用程序的入口点,如果链接器找不到这个标签就会生成错误消息。如果编写被外部汇编语言或C语言程序使用的一组工具,需要使用.globl命令声明每个函数段标签,.globl命令是声明外部程序可以访问的程序标签。所以,一般编写汇编语言的基础模板是这样的:
.section.data <此处为初始化变量> .section.bss <此处为未初始化的变量> .section.text .globl _start _start: <此处为指令码>
有了模板之后就可以开始创建汇编语言程序,我们也像学习高级语言一样,从最简单的程序开始。
编写汇编语言主要工作在编写.text部分,该部分主要编写要实现应用程序的指令码。汇编语言允许程序员使用助记符表示指令码,助记符使程序员可以使用英语样式的词表示各个指令码,汇编器可以很容易地把汇编语言助记符转换为原始指令码。这样使得汇编程序员不必了解指令码每个字节表示什么,子需要使用更加容易记忆的助记符(如push、mov、sub、call)来表示指令码。比如下面这个指令码例子:
55 89 E5 83 EC 08 C7 45 FC 01 00 00 00 83 EC 0C 6A 00 E8 D1 FE FF FF
可以写为如下的汇编代码:
push %ebp mov %esp, %ebp sub $0x8, %esp movl $0x1, -4(%ebp) sub $0xc, %esp push $0x0 call 8048348
数据段
和高级语言一样,编写汇编语言程序都需要管理某种类型的变量,在汇编语言中数据段和bss段都提供了定义变量的方法。数据段是最常见的定义变量的位置。
在数据段中定义变量需要两个语句:一个符号、一个命令。
符号类似于C语言程序中变量的名称,它只是汇编器试图访问内存位置时用作引用指针的一个位置。
命令实现为符号引用的数据元素保留多少字节,类似于高级语言指定数据类型。汇编语言使用如下的命令:.ascii、.asciz、.byte、.double、.float、.int、.long、.octa、.quad、.short、.single。例如定义变量如下:
.section .data msg: .ascii “This is a test message”
数据段主要用于定义变量数据,不过也可以使用命令.equ定义静态数据符号,类似于高级语言的定义常量。例如:
.equ var 3</span>
引用静态数据时,需要在变量名称前加$符号,比如把var的值传送到EAX寄存器:
movl $var, %eax
bss段
在bss段中定义数据元素和在数据段中定义有些不同,不需要指定特定数据类型。
GNU汇编器使用两个命令声明缓冲区,.comm命令声明未初始化的数据的通用内存区域;.lcomm命令声明未初始化的数据本地通用内存区域,该区域不允许从本地汇编代码之外进行访问。其使用格式为:
.comm symbol, length
symbol是赋给内存区域的符号,length是内存区域中包含的字节数量。
在bss段中声明数据的一个好处是数据不包含在可执行程序中,在数据段中定义数据必须包含在可执行程序中。
下面来看看汇编语言的hello world 程序:
#hello.s sample program to print hello world information .section .data #数据段声明 msg: .ascii "hello world!\n" #要输出的字符串 len=.-msg #字符串长度 .section .text #代码段声明 # .global main # main: .global _start #指定入口函数 _start: #函数在屏幕上输出hello world! movl $len, %edx #第三个参数: 字符串长度 movl $msg, %ecx #第二个参数: hello world!字符串 movl $1, %ebx #第一个参数: 输出文件描述符 movl $4, %eax #系统调用号sys_write int $0x80 #调用内核功能 #下面为退出程序代码 movl $0, %ebx #第一个参数: 退出返回码 movl $1, %eax #系统调用sys_exit int $0x80 #调用内核功能
编译执行结果如下:
$ as -o hello.o hello.s $ ld -o hello hello.o $ ./hello hello world!$
Linux 下的系统调用是通过中断(int 0x80)来实现的。在执行 int 0X80 指令时,寄存器 eax 中存放的是系统调用号,而传给系统调用的参数则必须按顺序放到寄存器 ebx,ecx,edx,esi,edi 中,当系统调用完成之后,返回值可以在寄存器 eax 中获得。系统调用号4对应的函数调用是
sys_write,在应用上其函数定义如下:
ssize_t write(int fd, const void *buf, size_t count);
参数 fd、buf 和 count 分别存在寄存器 ebx、ecx 和 edx 中,而系统调用号 SYS_write 则放在寄存器 eax 中,当 int 0x80 指令执行完毕后,返回值可以从寄存器 eax 中获得。
注意,如果使用gcc编译的话有一个问题,gcc查找main标签而不是_start标签,所以把程序中的_start改为main直接使用gcc编译链接就没有问题了。
相关文章推荐
- linux平台学x86汇编(九):循环指令
- linux平台学x86汇编(十三 ):字符串的比较与搜索
- linux平台学x86汇编(十):整数运算
- linux平台学x86汇编(十二):字符串的存储与加载
- linux平台学x86汇编(十五):使用命令行参数
- 在linux平台上安装好的基础上,开始配置arm-linux-gcc
- linux平台学x86汇编(十六):在汇编语言中调用C库函数
- linux平台学x86汇编(十七):在汇编中使用linux系统调用
- linux平台学x86汇编(十八):内联汇编
- linux平台学x86汇编(一):现代计算机结构组成与工作过程
- linux平台学x86汇编(五):使用gdb调试汇编程序
- wchar_t是内置还是别名(亲测有效:wchar_t在windows下是16位整数的别名,在linux等平台下是32位整数的别名。MSVC2008开始默认是/Zc:wchar_t)
- linux平台学x86汇编(六):数据的传送
- linux平台学x86汇编(十九):C语言中调用汇编函数
- hello world开始linux驱动
- 单位的编程平台是linux+gcc,为了适应这个潮流,我只能开始了漫长的linux学习之旅!
- linux平台学x86汇编(二):处理器指令码及IA-32平台了解
- linux平台学x86汇编(七):堆栈的使用
- linux平台学x86汇编(三):相关开发工具
- 【Linux 移植 】——1、软硬件平台和目标