、介绍
作为最基本编程语言的汇编语言虽然应用范围不算很广但重要性却勿庸置疑它能够完成许多其它语言所无法完成功能就拿 Linux 内核来讲虽然绝大部分代码是用 C 语言编写但仍然不可避免地在某些关键地方使用了汇编代码其中主要是在 Linux 启动部分由于这部分代码和硬件关系非常密切即使是 C 语言也会有些力不从心而汇编语言则能够很好扬长避短最大限度地发挥硬件性能
大多数情况下 Linux 员不需要使用汇编语言即便是硬件驱动这样底层在 Linux 操作系统中也可以用完全用 C 语言来实现再加上 GCC 这优秀编译器目前已经能够对最终生成代码进行很好优化确有足够理由让我们可以暂时将汇编语言抛在边了但实现情况是 Linux 员有时还是需要使用汇编或者不得不使用汇编理由很简单:精简、高效和 libc 无关性假设要移植 Linux 到某特定嵌入式硬件环境下首先必然面临如何减少系统大小、提高执行效率等问题此时或许只有汇编语言能帮上忙了
汇编语言直接同计算机底层软件Software甚至硬件进行交互它具有如下些优点:
2、Linux 汇编语法格式
绝大多数 Linux 员以前只接触过DOS/Windows 下汇编语言这些汇编代码都是 Intel 风格但在 Unix 和 Linux 系统中更多采用还是 AT&T 格式两者在语法格式上有着很大区别:
AT&T 格式 Intel 格式
pushl %eax push eax
AT&T 格式 Intel 格式
pushl $1 push 1
AT&T 格式 Intel 格式
addl $1, %eax add eax, 1
AT&T 格式 Intel 格式
movb val, %al mov al, ptr val
AT&T 格式 Intel 格式
ljump $section, $off jmp far section:off
lcall $section, $off call far section:off
和的相应远程返回指令则为:
AT&T 格式 Intel 格式
lret $stack_adjust ret far stack_adjust
section:disp(base, index, scale)
而在 Intel 汇编格式中内存操作数寻址方式为:
section:[base + index*scale + disp]
由于 Linux 工作在保护模式下用是 32 位线性地址所以在计算地址时不用考虑段基址和偏移量而是采用如下地址计算思路方法:
disp + base + index * scale
下面是些内存操作数例子:
AT&T 格式 Intel 格式
movl -4(%ebp), %eax mov eax, [ebp - 4]
movl .gif' />(, %eax, 4), %eax mov eax, [eax*4 + .gif' />]
movw .gif' />(%ebx, %eax, 4), %cx mov cx, [ebx + 4*eax + .gif' />]
movb $4, %fs:(%eax) mov fs:eax, 4
真不知道打破这个传统会带来什么样后果但既然所有设计语言第个例子都是在屏幕上打印个串 "Hello World!"那我们也以这种方式来开始介绍 Linux 下汇编语言设计
在 Linux 操作系统中你有很多办法可以实现在屏幕上显示个串但最简洁方式是使用 Linux 内核提供系统使用这种思路方法最大好处是可以直接和操作系统内核进行通讯不需要链接诸如 libc 这样库也不需要使用 ELF 解释器因而代码尺寸小且执行速度快
Linux 是个运行在保护模式下 32 位操作系统采用 flat memory 模式目前最常用到是 ELF 格式 2进制代码个 ELF 格式可执行通常划分为如下几个部分:.text、.data 和 .bss其中 .text 是只读代码区.data 是可读可写数据区而 .bss 则是可读可写且没有化数据区代码区和数据区在 ELF 中统称为 section根据实际需要你可以使用其它标准 section也可以添加自定义 section但个 ELF 可执行至少应该有个 .text 部分下面给出我们第个汇编用是 AT&T 汇编语言格式:
例1. AT&T 格式
#hello.s
.data # 数据段声明
msg : . "Hello, world!\n" # 要输出串
len = . - msg # 字串长度
.text # 代码段声明
.global _start # 指定入口
_start: # 在屏幕上显示个串
movl $len, %edx # 参数 3:串长度
movl $msg, %ecx # 参数 2:要显示串
movl $1, %ebx # 参数:文件描述符(stdout)
movl $4, %eax # 系统号(sys_write)
$0x80 # 内核功能
# 退出
movl $0,%ebx # 参数:退出代码
movl $1,%eax # 系统号(sys_exit)
$0x80 # 内核功能
初次接触到 AT&T 格式汇编代码时很多员都认为太晦涩难懂了没有关系在 Linux 平台上你同样可以使用 Intel 格式来编写汇编:
例2. Intel 格式
; hello.asm
section .data ; 数据段声明
msg db "Hello, world!", 0xA ; 要输出串
len equ $ - msg ; 字串长度
section .text ; 代码段声明
global _start ; 指定入口
_start: ; 在屏幕上显示个串
mov edx, len ; 参数 3:串长度
mov ecx, msg ; 参数 2:要显示串
mov ebx, 1 ; 参数:文件描述符(stdout)
mov eax, 4 ; 系统号(sys_write)
0x80 ; 内核功能
; 退出
mov ebx, 0 ; 参数:退出代码
mov eax, 1 ; 系统号(sys_exit)
0x80 ; 内核功能
上面两个汇编采用语法虽然完全区别但功能却都是 Linux 内核提供 sys_write 来显示个串然后再 sys_exit 退出在 Linux 内核源文件 /asm-i386/unistd.h 中可以找到所有系统定义
4、Linux 汇编工具
Linux 平台下汇编工具虽然种类很多但同 DOS/Windows 样最基本仍然是汇编器、连接器和调试器
1.汇编器
汇编器(assembler)作用是将用汇编语言编写源转换成 2进制形式目标代码Linux 平台标准汇编器是 GAS它是 GCC 所依赖后台汇编工具通常包含在 binutils 软件Software包中GAS 使用标准 AT&T 汇编语法可以用来汇编用 AT&T 格式编写:
[xiaowp@gary code]$ as -o hello.o hello.s
Linux 平台上另个经常用到汇编器是 NASM它提供了很好宏指令功能并能够支持相当多目标代码格式包括 bin、a.out、coff、elf、rdf 等NASM 采用是人工编写语法分析器因而执行速度要比 GAS 快很多更重要是它使用是 Intel 汇编语法可以用来编译用 Intel 语法格式编写汇编:
[xiaowp@gary code]$ nasm -f elf hello.asm
2.链接器
由汇编器产生目标代码是不能直接在计算机上运行它必须经过链接器处理才能生成可执行代码链接器通常用来将多个目标代码连接成个可执行代码这样可以先将整个分成几个模块来单独开发然后才将它们组合(链接)成个应用 Linux 使用 ld 作为标准链接它同样也包含在 binutils 软件Software包中汇编在成功通过 GAS 或 NASM 编译并生成目标代码后就可以使用 ld 将其链接成可执行了:
[xiaowp@gary code]$ ld -s -o hello hello.o
3.调试器
有人说不是编出来而是调出来足见调试在软件Software开发中重要作用在用汇编语言编写时尤其如此Linux 下调试汇编代码既可以用 GDB、DDD 这类通用调试器也可以使用专门用来调试汇编代码 ALD(Assembly Language Debugger)
从调试角度来看使用 GAS 好处是可以在生成目标代码中包含符号表(symbol table)这样就可以使用 GDB 和 DDD 来进行源码级调试了要在生成可执行中包含符号表可以采用下面方式进行编译和链接:
[xiaowp@gary code]$ as --gstabs -o hello.o hello.s
[xiaowp@gary code]$ ld -o hello hello.o
执行 as 命令时带上参数 --gstabs 可以告诉汇编器在生成目标代码中加上符号表同时需要注意是在用 ld 命令进行链接时不要加上 -s 参数否则目标代码中符号表在链接时将被删去
在 GDB 和 DDD 中调试汇编代码和调试 C 语言代码是样你可以通过设置断点来中断运行查看变量和寄存器当前值并可以对代码进行单步跟踪图1 是在 DDD 中调试汇编代码时情景:
意义
"m"、"v"、"o" 内存单元
"r" 任何寄存器
"q" 寄存器eax、ebx、ecx、edx的
"i"、"h" 直接操作数
"E"和"F" 浮点数
"g" 任意
"a"、"b"、"c"、"d" 分别表示寄存器eax、ebx、ecx和edx
"S"和"D" 寄存器esi、edi
"I" 常数(0至31)
8、小结
Linux操作系统是用C语言编写汇编只在必要时候才被人们想到但它却是减少代码尺寸和优化代码性能种非常重要手段特别是在和硬件直接交互时候汇编可以说是最佳选择Linux提供了非常优秀工具来支持汇编开发使用GCC内联汇编能够充分地发挥C语言和汇编语言各自优点
最新评论