2.1.3 GCC:AT&T 语法

我们来看一看在AT&T当中的汇编语法,这个语法在UNIX当中更普遍。

gcc -S 1_1.c

我们将得到这个:

  1. .file "1_1.c"
  2. .section .rodata
  3. .LC0:
  4. .string "hello, world"
  5. .text
  6. .globl main
  7. .type main, @function
  8. main:
  9. .LFB0:
  10. .cfi_startproc
  11. pushl %ebp
  12. .cfi_def_cfa_offset 8
  13. .cfi_offset 5, -8
  14. movl %esp, %ebp
  15. .cfi_def_cfa_register 5
  16. andl $-16, %esp
  17. subl $16, %esp
  18. movl $.LC0, (%esp)
  19. call printf
  20. movl $0, %eax
  21. leave
  22. .cfi_restore 5
  23. .cfi_def_cfa 4, 4
  24. ret
  25. .cfi_endproc
  26. .LFE0:
  27. .size main, .-main
  28. .ident "GCC: (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3"
  29. .section .note.GNU-stack, "", @progbits

有很多的宏(用点开始)。现在为了简单起见,我们先不看这些。(除了 .string ,就像一个C字符串编码一个null结尾的字符序列)。然后,我们将看到这个:

  1. .LC0:
  2. .string "hello, world"
  3. main:
  4. pushl %ebp
  5. movl %esp, %ebp
  6. andl $-16, %esp
  7. subl $16, %esp
  8. movl $.LC0, (%esp)
  9. call printf
  10. movl $0, %eax
  11. leave
  12. ret

在Intel与AT&T语法当中比较重要的区别就是:

操作数写在后面

  1. Intel语法中:<instruction> <destination operand> <source operand>
  2. AT&T语法中:<instruction> <source operand> <destination operand>

有一个理解它们的方法: 当你面对intel语法的时候,你可以想象把等号放到2个操作数中间,当面对AT&T语法的时候,你可以放一个右箭头(→)到两个操作数之间。

AT&T: 在寄存器名之前需要写一个百分号(%)并且在数字前面需要美元符($)。方括号被圆括号替代。 AT&T: 一些用来表示数据形式的特殊的符号

  1. l long(32 bits)
  2. w word(16bits)
  3. b byte(8 bits)

让我们回到上面的编译结果:它和在IDA里看到的是一样的。只有一点不同:0FFFFFFF0h 被写成了$-16,但这是一样的,10进制的16在16进制里表示为0x10。-0x10就等同于0xFFFFFFF0(这是针对于32位构架)。

外加返回值这里用的MOV来设定为0,而不是用XOR。MOV仅仅是加载(load)了变量到寄存器。指令的名称并不直观。在其他的构架上,这条指令会被称作例如”load”这样的。