AT&T汇编语言

本文主要关注在Windows平台下使用gcc进行AT&T汇编语法的学习,通过gcc对C代码进行反汇编学习,而不是手动编写汇编代码,目的是能读懂《深入理解计算机系统》,未完…

MinGW

MinGW是一个在Windows平台上的GNU开发工具包。
下载地址为 http://sourceforge.net/projects/mingw/files/

Cygwin

在Windows平台下模拟Linux开发环境最好的方法是安装Cygwin。Cygwin官网上对自己的解释是,一个在Windows上的一套Linux开发工具和环境,并包含一个提供Linux API的DLL(cygwin1.dll)。

下载地址为 http://cygwin.com/install.html

寄存器

学习汇编语言之首先要了解下寄存器的概念。

CPU最慢的操作是读取内存中的数据或者把数据存储到内存中。当处理器访问数据元素时,请求必须被发送到处理器外部、通过控制总线,然后进入内存存储单元。
为了解决上述问题,在处理器内部固化了一些存储部件,称为寄存器。寄存器能够存储要处理的数据元素,而无需访问内存。
IA-32平台下的主要寄存器有一下几类:
    通用寄存器
    段寄存器
    指令寄存器

1. 通用寄存器是8个32为寄存器,用于存储正在处理的数据
    EAX     用于操作数和结果的累加器
    EBX     指向数据内存段中的数据的指针
    ECX     字符串和循环操作的计数器
    EDX     IO指针
    EDI     用于字符串操作的目标数据指针
    ESI     用于字符串操作的源数据指针
    ESP     堆栈指针
    EBP     堆栈数据指针
    前4个通用寄存器可也可以通过16位或者8位名称引用,以表示寄存器的老版本。例如:EAX的低16位成为AX,AX的高8位称为AH,低8位称为AL。

      image

2. 段寄存器, 6个16位寄存器,用于处理内存访问。
    CS      代码段
    DS      数据段
    SS      堆栈段
    ES      附加段指针
    FS      附加段指针
    GS      附加段指针
    每个段寄存器都是16位的,包含指向内存特定段的起始位置的指针。CS包含指向内存代码段中的指针。代码段是内存中存储指令码的位置。处理器按照CS寄存器的值和EIP指令指针寄存器中包含的偏移值从内存获得指令码。程序不能显式地加载或者改变CS寄存器。当程序被分配一个内存空间时,处理器将为CS寄存器赋值。

3. 指令指针寄存器,EIP有时也被称为程序计数器,它指向要执行的下一条指令。

4. 标志寄存器,一个16位的标志寄存器,通常称为程序状态字(Program Status Word,PSW)PSW。
    CF:进位标志,最高位产生了进位或借位,CF=1,否则CF=0
    ZF:零标志,预算结果为0,ZF=1,否则ZF=0
    SF:符号标志,运算结果为负,SF=1,否则SF=0
    OF:溢出标志,有符号数的运算如果发生了溢出,OF=1,否则OF=0

AT&T汇编语法格式不同与Intel语法格式,主要区别如下:

AT&T使用$表示立即操作数,而Intel的立即操作数不需要有界定的。因此,使用AT&T语法引用十进制4时,使用$4,使用Intel语法时只需要4。
AT&T在寄存器前面加上前缀%,而Intel不这样做。因此使用AT&T语法引用EAX寄存器时写为%eax。
AT&T语法处理源和目标操作数时使用相反的顺序。把十进制4传送给EAX,AT&T的语法是movl $4, %eax,而Inter语法是mov eax, 4。
AT&T语法在助记符后面使用一个单独的字符来引用操作数中使用的数据长度,而Intel语法中数据长度被声明为单独的操作数。AT&T的指令movl $test, %eax等同于Intel语法的mov eax, dword ptr test。
长调用和跳转使用不同语法定义段和偏移值。AT&T语法使用ljmp $section, $offset,而Intel语法使用jmp section:offset。
 

使用gcc把C语言编译成汇编语言

C语言的源代码如下(hello.c)


    
#include < stdio.h > int main() { printf("hello, world\n"); }

AT&T格式的汇编代码是GCC的默认格式,使用命令 gcc –S hello.c 可以把hello.c编译成AT&T格式的汇编代码。


    
.file " hello.c " .def ___main ; .scl 2; .type 32; .endef .section .rdata, " dr " LC0: .ascii " hello, world\0 " .text .globl _main .def _main ; .scl 2; .type 32; .endef _main: LFB6: .cfi_startproc pushl %ebp .cfi_def_cfa_offset 8 .cfi_offset 5 , - 8 movl %esp, %ebp .cfi_def_cfa_register 5 andl $- 16 , %esp subl $ 16 , %esp call ___main movl $LC0, (%esp) call _puts leave .cfi_restore 5 .cfi_def_cfa 4 , 4 ret .cfi_endproc LFE6: .def _puts ; .scl 2; .type 32; .endef

所有以’.’开头的行都是指导汇编器和连接器的命令。

使用命令 gcc –S –masm=intel helloc可以生成Intel格式的汇编代码。


    
.file " hello.c " .intel_syntax noprefix .def ___main ; .scl 2; .type 32; .endef .section .rdata, " dr " LC0: .ascii " hello, world\0 " .text .globl _main .def _main ; .scl 2; .type 32; .endef _main: LFB6: .cfi_startproc push ebp .cfi_def_cfa_offset 8 .cfi_offset 5 , - 8 mov ebp, esp .cfi_def_cfa_register 5 and esp, - 16 sub esp, 16 call ___main mov DWORD PTR [esp], OFFSET FLAT: LC0 call _puts leave .cfi_restore 5 .cfi_def_cfa 4 , 4 ret .cfi_endproc LFE6: .def _puts ; .scl 2; .type 32; .endef

操作数

大多数指令有一个或多个操作数,指示出执行一个操作中的源数据值,以及放置结果的目标位置。有三种不同类型的操作数:

1. 立即数。以$ + 整数方式表示,例如:%4、%0x80。

2. 寄存器。可以是32位、16位或者8位的寄存器,例如%eax、%ax或%al。我们用Ea来表示寄存器a,用引用R[Ea]来表示它的值,这将寄存器看成一个数组R,用寄存器标识符作为索引。

3. 存储器引用。他会根据计算出来的地址访问某个存储器位置。因为将存储器看成一个很大的字节数组,我们用符号M[Addr]表示存储器中从地址Addr开始的字节值的引用。

Imm[Eb, Ei, s]是最常用的形式,它有四部分组成。一个立即数翩翩Imm,一个基址寄存器Eb,一个变址寄存器Ei和一个比例因子s,有效地址被计算为Imm + R[Eb] + R[Ei] * s

image

数据传送指令

最基本的传送指令是MOV。

MOV S, D 表示将S操作数中的值复制到D操作数中。下面是它的变种:

movb                传送字节

movw               传送字

movl                 传送双字

movsbw           将做了符号扩展的字节传送到字

movsbl             将做了符号扩展的字节传送到双字

movswl            将做了符号扩展的字传送到双字

movzbw           将做了零扩展的字节传送到字

movzbl             将做了零扩展的字节传送到双字

movzwl            将做了零扩展的字传送到双字

出栈入栈指令

pushl  S            将双字压栈

popl   S             将双字出栈

指令pushl  %ebp 等价于下面两条指令
       subl $4, %esp
       movl %ebp, (%esp)

指令popl %eax 等价于下面两条指令
       movl (%esp), %eax
       addl $4, %esp

算数与逻辑操作指令

image

gdb

使用gcc编译源代码时必须加入 –g 参数,才可以使用gdb进行调试。
image

使用gdb 13.exe就可以对生成的exe文件进行调试
image

使用list命令可以显示源代码
image

b main命令表示在main函数的开始出设置断点,r表示运行程序
image

disas 命令可以反汇编当前函数
image

display /i $pc 命令可以显示当前执行出的汇编代码
image

n 命令可以执行一行源代码,ni命令可以执行一行汇编代码
image

p命令表示打印,如下图可以打印变量和寄存器
image

i r 命令可以显示打印所有的寄存器
image

quit 命令退出gdb调试器
image

转载于:https://my.oschina.net/bery/blog/161927

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值