本文将通过编译器生成的汇编代码分析C程序在IA-32体系PC上的运行流程
实验环境: gcc 4.8.2
C语言程序的内存结构
C代码如下
int g(int x)
{
return x + 1;
}
int f(int x)
{
return g(x);
}
int main(void)
{
return f(2) + 3;
}
使用编译命令gcc -S -O0 -o main.s main.c -m32
编译出汇编文件,如下
g:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
addl $1, %eax
popl %ebp
ret
f:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl 8(%ebp), %eax
movl %eax, (%esp)
call g
leave
main:
pushl %ebp
movl %esp, %ebp
subl $4, %esp
movl $2, (%esp)
call f
addl $3, %eax
leave
入口点位于main,由main开始分析
- pushl %ebp
- subl $4, %esp
- movl %ebp, (%esp)
- 这是进入main函数后的第一个操作,保存了原来的ebp,即main开始执行之前的栈现场
- movl %esp, %ebp
- esp的值进入ebp,这儿将栈底设置为进入main之前的栈顶,即此处开始,ebp后面新增的栈都是main函数中的操作造成的,每次ebp的变化都意味着当前所处的函数的变化
- subl $4, %esp
- movl $2, (%esp)
- 以上两句实际上是执行了pushl $2, %esp,将立即数2入栈,
- 这句执行完成以后,使用gdb查看eip的值为0x8048418<main+13>,即下一行call f
- call f
- pushl %eip
- jmp f
- 调用f函数,上面两句入栈的立即数2就是函数f的参数,并将eip指向f的入口地址
- addl $3, %eax
- eax中存储的是函数f调用返回的结果,这条语句在eax上加上了立即数3
- leave
- movl %ebp, %esp
- popl %ebp
- 这两句将ebp还原到了调用main之前的现场,main函数调用到此为止
- ret
- popl %eip
- 将eip指回调用main之前的地址,退出函数main
进入main以后的第一个函数调用f(x)
- pushl %ebp
- movl %esp, %ebp
- 这儿的作用和main中一样,不重复说明
- subl $4, %esp
- 为参数留出栈空间
- movl 8(%ebp), %eax
- 栈地址是向下增长的,这儿ebp+8是获取了压入eip和参数前的地址,这条语句就是将参数存入eax
- movl %eax, (%esp)
- eax入栈,即参数入栈,c代码中函数g的参数就是函数f接收的参数,所以直接调用g函数
- call g
- pushl %eip
- jmp g
- leave
- 以上两行和main一致,不再重复
- ret
- 和main中作用一致
最后一个函数调用g(x)
- pushl %ebp
- movl %esp, %ebp
- 和前面描述一致,不赘述
- movl 8(%ebp), %eax
- 依然和f中的功能一致
- addl $1, %eax
- 函数g的功能,即在参数上+1
- popl %ebp
- movl (%esp), %ebp
- addl $4, %esp
- 恢复调用g之前的栈
- ret
- popl %eip
- 将eip指回调用函数g之前的地址,即回到函数f中
C语言程序在IA-32机器上的运行过程中,最重要的几条指令即push/pop,call/ret,其中push/pop实现了栈的基本操作,call/ret维护了函数的调用栈,确保了函数调用流程的连续
使用ddd抓了两张call前后的eip变化,可以很清晰的看出eip在call时的非线性改变
最后再看一下编译器优化可以做到的效果,以gcc -S -O3 -o test.s test.c -m32
编译上述代码,得到的汇编如下
g:
movl 4(%esp), %eax
addl $1, %eax
ret
f:
movl 4(%esp), %eax
addl $1, %eax
ret
main:
movl $6, %eax
ret
可以看见编译器直接把main函数的结果算出来当做立即数返回了。。还是很强大的。。f和g这种简单函数也没有重新设置栈底,而是直接采用了变址寻址,提升了运行效率
云课堂作业
吴韬,原创作品转载请注明出处,《Linux内核分析》MOOC课程
:first-child {
margin-top: 0;
}
blockquote > :last-child {
margin-bottom: 15px;
}
h1 {
text-transform: uppercase;
font-weight: bold;
border-bottom: 1px solid;
}
h2 {
border-bottom: 1px solid;
}
h3,
h4,
h5,
h6 {
border-bottom: none;
}
html * {
color: #657b83;
}
html body {
background-color: #fdf6e3;
}
html h1,
html h2,
html h3,
html h4,
html h5,
html h6 {
color: #586e75;
border-color: #657b83;
}
html a,
html a:active,
html a:visited {
color: #586e75;
}
html a:hover {
background-color: #eee8d5;
}
html pre {
color: #586e75;
background-color: #eee8d5;
}
html a,
html a:active,
html a:visited,
html code.url {
color: #b58900;
}
html h1 {
color: #b58900;
}
html h2,
html h3,
html h4,
html h5,
html h6 {
color: #b58900;
}
@media print {
body {
margin: 0;
}
* {
color: #000 !important;
}
}
-->
code[class*="language-"],
pre[class*="language-"] {
background-color: #fdfdfd;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
margin-bottom: 1em;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
position: relative;
padding: .2em;
-webkit-border-radius: 0.3em;
-moz-border-radius: 0.3em;
-ms-border-radius: 0.3em;
-o-border-radius: 0.3em;
border-radius: 0.3em;
color: #c92c2c;
border: 1px solid rgba(0, 0, 0, 0.1);
}
pre[class*="language-"]:before,
pre[class*="language-"]:after {
content: ‘‘;
z-index: -2;
display: block;
position: absolute;
bottom: 0.75em;
left: 0.18em;
width: 40%;
height: 20%;
-webkit-box-shadow: 0px 13px 8px #979797;
-moz-box-shadow: 0px 13px 8px #979797;
box-shadow: 0px 13px 8px #979797;
-webkit-transform: rotate(-2deg);
-moz-transform: rotate(-2deg);
-ms-transform: rotate(-2deg);
-o-transform: rotate(-2deg);
transform: rotate(-2deg);
}
:not(pre) > code[class*="language-"]:after,
pre[class*="language-"]:after {
right: 0.75em;
left: auto;
-webkit-transform: rotate(2deg);
-moz-transform: rotate(2deg);
-ms-transform: rotate(2deg);
-o-transform: rotate(2deg);
transform: rotate(2deg);
}
.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #7D8B99;
}
.token.punctuation {
color: #5F6364;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.function-name,
.token.constant,
.token.symbol,
.token.deleted {
color: #c92c2c;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.function,
.token.builtin,
.token.inserted {
color: #2f9c0a;
}
.token.operator,
.token.entity,
.token.url,
.token.variable {
color: #a67f59;
background: rgba(255, 255, 255, 0.5);
}
.token.atrule,
.token.attr-value,
.token.keyword,
.token.class-name {
color: #1990b8;
}
.token.regex,
.token.important {
color: #e90;
}
.language-css .token.string,
.style .token.string {
color: #a67f59;
background: rgba(255, 255, 255, 0.5);
}
.token.important {
font-weight: normal;
}
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.namespace {
opacity: .7;
}
@media screen and (max-width: 767px) {
pre[class*="language-"]:before,
pre[class*="language-"]:after {
bottom: 14px;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
}
/* Plugin styles */
.token.tab:not(:empty):before,
.token.cr:before,
.token.lf:before {
color: #e0d7d1;
}
/* Plugin styles: Line Numbers */
pre[class*="language-"].line-numbers {
padding-left: 0;
}
pre[class*="language-"].line-numbers code {
padding-left: 3.8em;
}
pre[class*="language-"].line-numbers .line-numbers-rows {
left: 0;
}
-->