Skip to content

Latest commit

 

History

History
166 lines (110 loc) · 4.05 KB

3.01.md

File metadata and controls

166 lines (110 loc) · 4.05 KB

程序编码

例子

编译下面C源码 :

// 3.01.1.c
int accum = 0;

int sum(int x, int y)
{
    int t = x + y;
    accum += t;
    return t;
}

运行指令如下

gcc -O1 -S 3.01.1.c

会在目录下生成汇编文件

  .file  "3.01.1.c"
  .text
  .globl  sum
  .def  sum;  .scl  2;  .type  32;  .endef
  .seh_proc  sum
sum:
  .seh_endprologue
  leal  (%rcx,%rdx), %eax
  addl  %eax, accum(%rip)
  ret
  .seh_endproc
  .globl  accum
  .bss
  .align 4
accum:
  .space 4
  .ident  "GCC: (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0"

继续编译成 可执行文件 然后使用objdump 查看 :

gcc -O1 3.01.1.c
objdump -d a.exe >> 3.01.1.dump

可以在dump文件中找到类似下面的内容:

#...
0000000000401550 <sum>:
  401550:  8d 04 11               lea    (%rcx,%rdx,1),%eax
  401553:  01 05 d7 5a 00 00      add    %eax,0x5ad7(%rip)        # 407030 <accum>
  401559:  c3                     retq
#...

类似 8d 04 11 01 05 d7 5a 00 00 c3 这样的内容就是程序在机器中的存在形式.

汇编格式

AT&T 和 Intel 不同

寄存器命名原则

相比inter语法,AT&T语法格式要求所有的寄存器都必须加上取值符"%".

操作码命令格式

  • 源/目的操作数顺序:
Intel语法格式中命令表示格式为:"opcode dest, src"; "操作码 目标, 源"
AT&T语法格式表示为:"opcode src, dest"; "操作码 源, 目标"
  • 操作数长度标识:

在AT&T语法中,通过在指令后添加后缀来指明该指令运算对象的尺寸.

后缀 'b' 指明运算对象是一个字节(byte)

后缀 'w' 指明运算对象是一个字(word)

后缀 'l' 指明运算对象是一个双字(double word)

后缀 'q' 指明运算对象是一个四字(quad word)

Intel语法中指令'mov'在AT&T语法必须根据运算对象的实际情况写成:'movb','movw'或'movl'。

注:若在AT&T中省略这些后缀,GAS将通过使用的寄存器大小来猜测指令的操作数长度.

  • 'FAR'不是GAS的关键字,因此对far的call或jmp指令须加前缀 'l', 'far call'要写成 'lcall' , 'far jmp' 要写成 'ljmp' , 'ret far' 写成 'lret'。

常数/立即数的格式

在AT&T语法中对立即数,须在其前加前缀 $ 来指明,而Inter语法则不需要。

另外, 在常数前也必须加一个前缀字符 * ,而Inter语法则也是不需要的。

内存寻址方式

  • 在Intel语法中,使用下面格式来表示存储器寻址方式:

SECTION:[BASE + INDEX * SCALE + DISP];段:[基地址 + 变址 * 比例因子 + 偏移量]

  • AT&T语法则使用不同的格式来表示寻址方式:

SECTION:DISP(BASE, INDEX, SCALE);段:偏移量(基地址,变址,比例因子)

BASE是基地址索引寄存器(可以是任一通用寄存器),

INDEX是变址寄存器(除ESP外的任一通用寄存器),

SCALE是变址寄存器的比例常数,

DISP是基址/变址寄存器的位移量。

其他

标号 & 标识符

所有的标号必须以一个字母,点或下划线开始,标号后加一个冒号表示标号的结束。

局部标号使用数字0-9后跟一个冒号,使用局部标号时要在数字后跟一个字符'b'(向后引用)或字符'f'(向前引用)。

因为只能使用数字0-9作为局部标号名,所以最多只能定义10个局部标号.一个标识符能给它赋于一个值。(如:'TRUE=1', 或者使用 .set 或 .equ 指令)。

基本的行内汇编格式

asm("statements");

例如:asm("nop");

asm("movl %eax,%ebx");

asm 和 __asm__是完全一样的.

如果有多行汇编,则每一行都要加上 "\n\t"

扩展的行内汇编格式

asm ( "statements" : output_regs : input_regs : clobbered_regs);

冒号后的语句指明输入,输出和被改变的寄存器.

条件码寄存器(单个bit)

cf(进位标志),zf(零标志),sf(符号标志),of(溢出标志)...

访问条件码指令:cmp,test,set...

t = a + b;
cf: (unsigned) t < (unsigned) a;//无符号溢出  
zf: t == 0;//零  
sf: t < 0;//负数  
of: (a < 0 == b < 0) && (t < 0 != a < 0)//有符号溢出