程序本质是一系列的ISA(instruction set architecture)
许多在汇编中可见的东西在C中都看不到了,比如 PC(program count 用来指向下一条执行的指令)在汇编中用%eip表示,C中是看不到的。
下面是一个c和汇编机器语言的转变的例子:
int accum = 0;
int sum(int x, int y){ int t=x+y; accum += t; return t; } unix> gcc -O1 -S code.c
在调用gcc -S之后产生的汇编文件中会有很多 .file .txt之类以点开头的行,这些点的意思其实就是告诉编译器这些行可以忽略。
sum:
pushl %ebp
movl %esp, %ebp
movl 12(%ebp), %eax
addl 8(%ebp), %eax
addl %eax, accum //仍然显示变两名,因为还不确定,如果有main函数调用,则会变成一个固定地址,例如 0x804a018
popl %ebp
ret
unix> gcc -O1 -c code.c 55 89 e5 8b 45 0c 03 45 08 01 05 00 00 00 00 5d c3
unix> objdump -d code.o
机器代码和汇编的一些对照
a)单个指令在机器码中长1到15个字节
AT&T differ Intel Assemble
. The Intel code omits the size designation suffixes. We see instruction movinstead ofmovl.
char (b), short(w), float(s), long double(t) , all others are (l)
. The Intel code omits the ‘%’ character in front of register names, using espinstead of%esp.
. The Intel code has a different way of describing locations in memory, for example ‘DWORD PTR[ebp+8]’rather than ‘8(%ebp)’.
. Instructions with multiple operands list them in the reverse order. This can be very confusing whenswitching between the two formats.
AT&T 有几个比较重要的概念,理解了这些概念,然后对照具体的指令,基本就可以读懂汇编了
1. 所有的命令都有后缀, b(byte)w(word)l(quad)比如movb movw movl, 另外还有补0或者补符号的,比如movz, movs
2. 操作的数据有三类,一类临时数据,以$打头的数据,比如$123, 第二类是register,%eax,第三类是内存,都有个括号比如7(%eax,%eax,4)
3. 四个操作位, 后面好多针对不同标志位的cmp,test, jmp
4. mov lea的不同:一个是mov地址指向的内容,lea只是mov地址
CF:Carry Flag. The most recent operation generated a carry out of the mostsignificant bit. Used to detect overflow for unsigned operations.
ZF:Zero Flag. The most recent operation yielded zero.
SF:Sign Flag. The most recent operation yielded a negative value.
OF:Overflow Flag. The most recent operation caused a two’s-complementoverflow—either negative or positive.
C语言的一些特性:
1 函数的调用
c语言和汇编的转化有个重要的概念叫stack frame,通过它C实现了函数之间的调用。
对stack的操作有call,leave,ret, 寄存器在此时也被分成两种一种是caller save,另一种是callee save,分别用来保存两者的状态,当然stack还是必须的因为寄存器可能不够用,还有一些比如必须给一些local variable一些地址,所有必须放在stack里。
对于递归调用和其他调用一致。
2. 数组
C语言对数组的表示非常简单,就是一段连续的内存,所以对应到汇编要访问数组元素也很简单是movl (%edx,%ecx,4),%eax 。
多维数组(nested array)访问可以对应到一维数组的访问,C语言如果要分配动态数组就要使用malloc 或者 calloc(calloc size*number, 且都初始成0)
3. struct union,在内存中数据对齐的问题。
GDB的使用,这里列举了一些过去所不了解的GDB的命令,原来GDB如此的强大,会汇编有很深厚的解释
编译器会优化代码,这导致很难建立c和机器语言之间的map,所以我们最好要熟悉常用的优化办法。
1. break sum // Set breakpoint at entry to functionsum
2. break *0x8048394 //Set breakpoint at address 0x8048394
3. delete 1 // Delete breakpoint 1
4. stepi 4 //Execute four instructions
5. disas // Disassemble current function
6. disas sum //Disassemble functionsum
7.disas 0x8048397 //Disassemble function around address0x8048397
8. disas 0x8048394 0x80483a4 //Disassemble code within specified address range
9. print /x $eip //Print program counter in hex
10. print *(int *) 0xfff076b0 //Print integer at address0xfff076b0
11. info frame //Information about current stack frame
12. info registers //Values of all the registers
address-space layout randomization : 避免stack的地址被预测到然后被攻击。
对于浮点数的处理,汇编有一套和整数相似的指令集合,在早期的intel的CPU当中会有一个独立的芯片来做浮点运算。