# 程序编码

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

`````` sum:
pushl %ebp
movl %esp,%ebp
movl 12(%ebp),%eax
popl %ebp
ret
``````

``````sum:
pushl %ebp
movl %esp,%ebp
movl 0xc(%ebp),%eax
popl %ebp
ret
``````

``````  int main(){
return sum(1,3);
}
``````

``````  sum:
pushl %ebp
movl %esp,%ebp
movl 0xc(%ebp),%eax
popl %ebp
ret
``````

# 数据访问

1

• ## 访问信息

2

3

4

5

``````  subl \$4,%esp
movl %eax,(%esp)
``````

``````  movl(%esp),%edx
``````

``````  int exchange(int *xp,int y){
int x=*xp;
*xp=y;
return x;
}
``````

xp在%ebp+8,y在%ebp+12,抛开栈的建立与完成,其汇编代码为

``````    movl 8(%ebp), %edx
movl (%edx), %eax
movl 12(%ebp), %ecx
movl %ecx, (%edx)</pre>
``````

# 算术与逻辑操作

6

7

``````  int arith(int x, int y , int z){
int t1 = x+y;
int t2 = z*48;
int t3 = t1&0xFFFF;
int t4 = t2*t3;
return t4;
}
``````

x在%ebp+8,y在%ebp+12,z在%ebp+16(%ebp+4的地方储存返回值的).同样不考虑栈的建立与完成,那么汇编代码为

``````    movl 16(%ebp), %eax
leal    (%eax,%eax,2), %edx
sall    \$4, %edx
movl 12(%ebp), %eax
andl    \$65535, %eax
imull %edx, %eax</pre>
``````

# 控制

• ## 条件码

CF：进位标志寄存器，它记录无符号操作的溢出，当溢出时会被设为1
ZF：零标志寄存器，当计算结果为0时将会被设为1
SF：符号标志寄存器，当计算结果为负数时会被设为1
OF：溢出标志寄存器，当计算结果导致了补码溢出时，会被设为1
leal指令不会改变任何条件码,除他之外前面的指令都会改变条件码.还有两种指令也会改变条件码但不会影响任何其他寄存器,即CMP和TEST.

8

test指令用来检测是负数,正数还是零.

9

``````  cmpl %eax,%edx
setl %al
movzbl %al,%eax
``````

jmp是无条件跳转,可以是直接跳转`jmp L1`,也可以是间接跳转`jmp *%eax`或者`jmp *(%eax)`.

10

``````   jle L2
.L5
movl %edx,%eax
sarl %eax
subl %eax,%edx
leal (%edx,%edx,2),%edx
testl %edx,%edx
jg .L5
.L2
movl %edx,%eax
``````

``````   8:7e 0d     jle 17<silly+0x17>
a:89 d0     movl %edx,%eax
c:d1 f8     sarl %eax
e:29 c2     subl %eax,%edx
10:8d 14 52  leal (%edx,%edx,2),%edx
13:85 d2    testl %edx,%edx
15:7f f3    jg a<silly+0xa>
17:89 d0    movl %edx,%eax
``````

• ## 翻转条件分支

``````  int min(int a,int b){
if( a < b ){
return a;
}else{
return b;
}
}
``````

`````` min:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %edx  //a
movl 12(%ebp), %eax  //b
cmpl %edx, %eax
jle  .L2
movl %edx, %eax
.L2:
popl %ebp
ret
``````

• ## do-while循环

``````  int fact(int n){
int result=1;
do{
result*=n;
n=n-1;
}while(n>1);
return result;
}
``````

n在%ebp+8,对应汇编代码

`````` movl 8(%ebp),%edx
movl \$1,%eax
.L2
imull %edx,%eax
subl \$1,%edx
cmpl \$1,%edx
jg .L2
``````
• ## while循环

``````int fact(int n){
int result=1;
while(n>1){
result*=n;
n=n-1;
}
return result;
}
``````

``````  int fact(int n){
int result=1;
if(n<=1)
goto done;
loop:
result*=n;
n=n-1;
if(n>1)
goto loop;
done:
return result;
}
``````

`````` movl 8(%ebp),%edx
movl \$1,%eax
cmp \$1,%edx
jle .L7
.L10
imull %edx,%eax
subl \$1,%edx
cmpl \$1,%edx
jg .L10
.L7
``````

• ## for循环

for循环同样是转化成do-while模式的

``````  int fact(int n){
int i;
int result=1;
for(i=2;i<=n;i++)
result*=i;
return result;
}
``````

do-while循环模式

``````  int fact(int n){
int i;
int result=1;
if(!(i<=n)) //n<=1
goto done;
loop:
result*=i;
i++;
if(i<=n)
goto loop;
done:
return result;
}
``````

`````` movl 8(%ebp),%ecx
movl \$2,%edx
movl \$1,%eax
cmpl \$1,%ecx
jle .L7
.L10
imull %edx,%eax
cmpl %edx,%ecx
jge .L10
.L7
``````
• ## 条件传送指令

``````  int fact(int x,int y){
return x<y?y-x:x-y;
}
``````

``````  movl 8(%ebp),%ecx //x
movl 12(%ebp),%edx //y
movl %edx,%ebx
subl %ecx,%ebx
movl %ecx,%eax
subl %edx,%eax
cmpl %edx,%ecx
cmovl %ebx,%eax
``````

cmovl是一个替换的条件传送指令

11

`````` int cread(int *xp){
return (xp?*xp:0);
}
``````

``````  movl \$0,%eax
test1 %edx,%edx
cmovne (%edx),%eax
``````

• ## switch 语句

switch语句是使用跳跃表实现的,把要跳转的地址都存在一个数组里.

``````  int switch(int x,int n){
int result=x;
switch(n){
case 100:
result*=13;
break;
case 102:
result+=10;
case 103:
result+=11
break;
case 104:
case 106:
result*=result;
break;
default:
result=0;
}
return result;
}
``````

`````` .L7:
.long .L3 case 100:
.long .L2 case 101:
.long .L4 case 102:
.long .L5 case 103:
.long .L6 case 104:
.long .L2 case 105:
.long .L6 case 106:
``````

``````  movl 8(%ebp),%edx
movl 12(%ebp),%eax
subl \$100,%eax
cmpl \$6,%eax
ja .L2
jmp *.L7(,%eax,4)
.L2:
movl \$0,%eax
jmp .L8
.L5:
movl %edx,%eax
jmp .L9
.L3:
leal (%edx,%ed,2),%eax
leal (%edx,%ed,4),%eax
jmp .L8
.L4:
leal 10(%edx),%eax
.L9:
jmp .L8
.L6:
movl %edx,%eax
imull %edx,%eax
.L8:
``````

# 过程

12

• ## 过程的实现

1、备份原来的帧指针，调整当前的帧指针到栈指针的位置，这个过程就是我们经常看到的如下两句汇编代码做的事情。

``````  pushl    %ebp
movl    %esp, %ebp</pre>
``````

2、建立起来的栈帧就是为被调用者准备的，当被调用者使用栈帧时，需要给临时变量分配预留内存，这一步一般是经过下面这样的汇编代码处理的。

``````  subl \$16,%ebp
``````

3、备份被调用者保存的寄存器当中的值，如果有值的话，备份的方式就是压入栈顶。因此会采用如下的汇编代码处理。

``````  pushl %ebx
``````

4、使用建立好的栈帧，比如读取和写入，一般使用mov，push以及pop指令等等。
5、恢复被调用者寄存器当中的值，这一过程其实是从栈帧中将备份的值再恢复到寄存器，不过此时这些值可能已经不在栈顶了。因此在恢复时，大多数会使用pop指令，但也并非一定如此。

``````  movl %ebo,%esp
``````

7、恢复调用者的栈帧，恢复其实就是调整栈帧两端，使得当前栈帧的区域又回到了原始的位置。因为栈指针已经在第六步调整好了，因此此时只需要将备份的原帧指针弹出到%ebp即可。类似的汇编代码如下。

``````  popl %ebp
``````

8、弹出返回地址，跳出当前过程，继续执行调用者的代码。此时会将栈顶的返回地址弹出到PC，然后程序将按照弹出的返回地址继续执行。这个过程一般使用ret指令完成。

• ## 转移控制

call指令：它一共做两件事，第一件是将返回地址（也就是call指令执行时PC的值）压入栈顶，第二件是将程序跳转到当前调用的方法的起始地址。第一件事是为了为过程的返回做准备，而第二件事则是真正的指令跳转。
leave指令：它也是一共做两件事，第一件是将栈指针指向帧指针，第二件是弹出备份的原帧指针到%ebp。第一件事是为了释放当前栈帧，第二件事是为了恢复调用者的栈帧。
ret指令：它同样也是做两件事，第一件是将栈顶的返回地址弹出到PC，第二件事则是按照PC此时指示的指令地址继续执行程序。这两件事其实也可以认为是一件事，因为第二件事是系统自己保证的，系统总是按照PC的指令地址执行程序。

• ## 寄存器使用惯例

%eax、%edx、%ecx：这三个寄存器被称为调用者保存寄存器。意思就是说，这三个寄存器由调用者P来保存，而对于Q来说，Q可以随便使用，用完了就不用再管了.
%ebx、%esi、%edi：这三个寄存器被称为被调用者保存寄存器。同样的，这里是指这三个寄存器由被调用者Q来保存，换句话说，Q可以使用这三个寄存器，但是如果里面有P的变量值，Q必须保证使用完以后将这三个寄存器恢复到原来的值，这里的备份，其实就是上面那8个步骤中第3个步骤做的事情.

• ## 过程示例

``````  int swap_add(int a,int b){
register int c = a + b;
return c;
}
int main(){
int a = 100;
int b = 101;
return c;
}
``````

``````  main:
pushl %ebp
movl %esp,%ebp
subl \$24,%esp
movl \$100,-4(%ebp)
movl \$101,-8(%ebp)
movl -8(%ebp),%eax
movl %eax,4(%esp)
movl -4(%ebp),%eax
movl %eax,(%esp)
leave
ret
pushl %ebp
movl  %esp,%ebp
pushl %ebx
movl 12(%ebp),%eax
movl 8(%ebp),%edx
leal (%edx,%eax),%ebx
movl %ebx,%eax
popl %ebx
popl %ebp
ret
``````

13

``````  main:
pushl %ebp
movl %esp,%ebp
subl \$24,%esp
movl \$100,4(%esp)
movl \$101,(%esp)
leave
ret
``````

• ## 递归调用

``````  int rfact(int n){
int result;
if(n<=1){
result = 1;
}else{
result = n * rfact(n-1);
}
return result;
}
``````

``````  rfact:
pushl %ebp
movl %esp,%ebp
pushl %ebx
subl \$20,%esp
movl 8(%ebp),%ebx
movl \$1,%eax
cmpl \$1,%ebx
jle .L3
leal -1(%ebx),%eax
movl %eax,(%esp)
call rfact
imull %ebx,%eax
.L3:
popl %ebx
popl %ebp
ret
``````
14

# 数组

`````` char A[12];
char *B[8];
double C[6];
double *D[5];
``````
15

17

• ## 嵌套数组

18

``````  movl 12(%ebp),%eax
leal (%eax,%eax,2),%eax
movl 16(%ebp),%edx
sall \$2,%edx
movl (%edx,%eax,4),%eax
``````

• ## 定长和变长数组

``````  int main(){
int a[5];
int i,sum;
for(i = 0 ; i < 5; i++){
a[i] = i * 3;
}
for(i = 0 ; i < 5; i++){
sum += a[i];
}
return sum;
}
``````

``````  main:
pushl    %ebp
movl    %esp, %ebp//到此准备好栈帧
subl    \$32, %esp//分配32个字节的空间
leal    -20(%ebp), %edx//将帧指针减去20赋给%edx寄存器
movl    \$0, %eax//将%eax设置为0，这里的%eax寄存器是重点
.L2:
movl    %eax, (%edx)//将0放入帧指针减去20的位置
cmpl    \$15, %eax//比较%eax和15
jne    .L2//如果不相等的话就回到L2
movl    -20(%ebp), %eax//下面这五句指令，很明显从-20到-4。
leave
ret
``````

`````` int fact(int n,intA[n][n],int i,int j){
return A[i][j];
}
``````

``````  movl 8(%ebp),%eax
sall \$2,%eax
movl %eax,%edx
imull 16(%ebp),%edx
movl 20(%ebp),%eax
sall \$2,%eax
movl (%eax,%edx),%eax
``````

``````.L30
movl (%ecx),%eax
imull (%esi,%edx,4),%eax
cmpl %edx,8(%ebp)
jg .L30
``````
• ## 异质结构与数据对齐

``````struct {
int a;
int b;
char c;
} mystruct;
int main(){
printf("%d\n",sizeof mystruct);
}
``````

``````union {
int a;
int b;
char c;
} myunion;
int main(){
printf("%d\n",sizeof myunion);
}
``````

# 缓冲区溢出

C对于数组是不进行任何边界检查的,而且局部变量和状态信息都会放在栈中.

19

• ## 栈破坏检测

20

``````  movl %gs:20,%eax
movl %eax,-8(%ebp)
xorl eax,%eax
``````

``````  movl -8(%ebp),%eax
xorl %gs:20,%eax
je .L19
call __stack_chk_fail
``````

# X86-64

64位机器不细讲,和32位没有多大区别.

21

### 推荐阅读更多精彩内容

• 原文地址：C语言函数调用栈(一)C语言函数调用栈(二) 0 引言 程序的执行过程可看作连续的函数调用。当一个函数执...
小猪啊呜阅读 2,761评论 1 18
• 计算机系统漫游 代码从文本到可执行文件的过程（c语言示例）：预处理阶段，处理 #inlcude ， #defin...
wildimgine阅读 1,522评论 0 4
• 站在巨人的肩膀上——IDA PRO权威指南阅读笔记 一，窗口 view->open subviews 打开/关闭各...
SueLyon阅读 8,039评论 0 6
• 1.地址总线，数据总线，控制总线在哪里，它们有什么作用?答：它们都是cpu连接外部组件的线路。地址总线：地址总线A...
MagicalGuy阅读 442评论 0 0
• Return-Oriented-Programming(ROP FTW) Author: Saif El-Sher...
RealSys阅读 1,383评论 0 2