(转)详解汇编系统调用过程(以printf为例)
本文以printf为例,详细解析一个简单的printf调用里头,系统究竟做了什么,各寄存器究竟如何变化。
环境:
linux + gnu as assembler + ld linker
如何在汇编调用glibc的函数?其实也很简单,根据c convention call的规则,参数反向压栈,call,然后结果保存在eax里头。注意,保存的是地址。
在汇编里头,一切皆地址。(别纠结这个,别告诉我还有立即数……主要是要有一切皆地址的思想)
例如这个printf,在C里头,我们用得很多
int printf(const char *format, …) 这里值得一提的是这个“…”是不定参数,也就是说后面有多少个参数,函数定义里头没有规定,感兴趣的可以google一下va_list相关的知识,这里就不展开了。
但是汇编怎么知道处理这个的呢?这里给个简单的解释,感兴趣的可以google一下“c convention call”了解更详细跟专业的解释。
例如当我们调用 result = printf( “%d %d”, 12, a )的时候,编译器默认是这样处理的(除非函数定义声明了pascal call)。
在栈里头,先一次push a的地址,还有12这个立即数,再push “%d %d”这个字符串的地址,内存模型如下,x86的esp是往下增长的。
(这里是buttom,往下增长的是top)
&a
12
address of “%d %d”
——————————————-(esp 指着这里 ,我们假设地址是4字节,12这个数也是4字节)
当call printf的时候,首先,push当前的eip入esp,解析esp+4所指的”%d %d”,因为%d这样的特定字符都定义了后面每个参数的大小,所以只要解析“%d %d”,我们就可以知道栈里头参数的情况,例如esp+4+4就是一个int,esp+4+4+4是另外一个int。
当返回的时候,先pop到eip,也就是把eip还原到call之后马上要执行的机器码,这时,esp就指着“%d %d”,esp+4指着12,esp+8指着a的地址。esp里头的内容怎么处理,看需要吧,你也可以pop出来,也可以不pop。但为了效率着想,如果空间够用,通常不pop,直接用mov指令把下一次要用的参数move进去。返回指储存在eax里头。
这也一定程度上解释了为什么c convention call是反向压栈,这样编译器处理起来方便,特别对于这些va_list,因为va_list后面不能继续跟参数,va_list一定出现在函数的末尾,如果是对printf这类的函数使用pascal call,也就是参数正向压栈,汇编级别处理起来就特别麻烦了。
眼见为实,下面就用汇编写一个调用printf的,并用gdb跟踪寄存器,看看是否是上述的一样。
文件:test_printf.s
.section .data
format: .asciz “%d\n”
.section .text
.global _start
_start:
pushl <nobr><span class="math" id="MathJax-Span-1" style="width: 4.014em; display: inline-block;"><span style="display: inline-block; position: relative; width: 3.255em; height: 0px; font-size: 123%;"><span style="position: absolute; clip: rect(1.412em 1000.003em 2.659em -0.377em); top: -2.274em; left: 0.003em;"><span class="mrow" id="MathJax-Span-2"><span class="mn" id="MathJax-Span-3" style="font-family: MathJax_Main;">12</span><span class="mi" id="MathJax-Span-4" style="font-family: MathJax_Math-italic;">p</span><span class="mi" id="MathJax-Span-5" style="font-family: MathJax_Math-italic;">u</span><span class="mi" id="MathJax-Span-6" style="font-family: MathJax_Math-italic;">s</span><span class="mi" id="MathJax-Span-7" style="font-family: MathJax_Math-italic;">h</span><span class="mi" id="MathJax-Span-8" style="font-family: MathJax_Math-italic;">l</span></span><span style="display: inline-block; width: 0px; height: 2.279em;"></span></span></span><span style="border-left-width: 0.003em; border-left-style: solid; display: inline-block; overflow: hidden; width: 0px; height: 1.203em; vertical-align: -0.33em;"></span></span></nobr><script type="math/tex" id="MathJax-Element-1">12
pushl </script>format
call printf
movl $0, (%esp)
call exit
使用如下命令编译,链接
$ as -g test_printf.s -o test_printf.o
$ ld -lc -I /lib/ld-linux.so.2 test_printf.o -o test_printf
as加入-g是要加入调试信息,ld的-lc是链接libc.a,-I是–dynamic-linker,/lib/ld-linux.so.2这个要看各人系统情况。链接libc跟ld库之后,生成test_printf
执行
$ ./test_printf
12
输出12,正常退出。
先用objdump看看test_printf里头的.text section
$ objdump -d test_printf
Disassembly of section .text:
080481c0 <_start>:
80481c0: 6a 0c push <nobr><span class="math" id="MathJax-Span-9" style="width: 15.287em; display: inline-block;"><span style="display: inline-block; position: relative; width: 12.415em; height: 0px; font-size: 123%;"><span style="position: absolute; clip: rect(1.412em 1000.003em 2.659em -0.431em); top: -2.274em; left: 0.003em;"><span class="mrow" id="MathJax-Span-10"><span class="mn" id="MathJax-Span-11" style="font-family: MathJax_Main;">0</span><span class="mi" id="MathJax-Span-12" style="font-family: MathJax_Math-italic;">x</span><span class="mi" id="MathJax-Span-13" style="font-family: MathJax_Math-italic;">c</span><span class="mn" id="MathJax-Span-14" style="font-family: MathJax_Main;">80481</span><span class="mi" id="MathJax-Span-15" style="font-family: MathJax_Math-italic;">c</span><span class="mn" id="MathJax-Span-16" style="font-family: MathJax_Main;">2</span><span class="mo" id="MathJax-Span-17" style="font-family: MathJax_Main; padding-left: 0.274em;">:</span><span class="mn" id="MathJax-Span-18" style="font-family: MathJax_Main; padding-left: 0.274em;">68</span><span class="mi" id="MathJax-Span-19" style="font-family: MathJax_Math-italic;">c</span><span class="mi" id="MathJax-Span-20" style="font-family: MathJax_Math-italic;">c</span><span class="mn" id="MathJax-Span-21" style="font-family: MathJax_Main;">92</span><span class="mn" id="MathJax-Span-22" style="font-family: MathJax_Main;">04</span><span class="mn" id="MathJax-Span-23" style="font-family: MathJax_Main;">08</span><span class="mi" id="MathJax-Span-24" style="font-family: MathJax_Math-italic;">p</span><span class="mi" id="MathJax-Span-25" style="font-family: MathJax_Math-italic;">u</span><span class="mi" id="MathJax-Span-26" style="font-family: MathJax_Math-italic;">s</span><span class="mi" id="MathJax-Span-27" style="font-family: MathJax_Math-italic;">h</span></span><span style="display: inline-block; width: 0px; height: 2.279em;"></span></span></span><span style="border-left-width: 0.003em; border-left-style: solid; display: inline-block; overflow: hidden; width: 0px; height: 1.203em; vertical-align: -0.33em;"></span></span></nobr><script type="math/tex" id="MathJax-Element-2">0xc
80481c2: 68 cc 92 04 08 push </script>0x80492cc
80481c7: e8 d4 ff ff ff call 80481a0
<script type="text/javascript">
$(function () {
$('pre.prettyprint code').each(function () {
var lines = $(this).text().split('\n').length;
var $numbering = $('<ul/>').addClass('pre-numbering').hide();
$(this).addClass('has-numbering').parent().append($numbering);
for (i = 1; i <= lines; i++) {
$numbering.append($('<li/>').text(i));
};
$numbering.fadeIn(1700);
});
});
</script>
分享到:
相关推荐
linux系统调用过程分析,深入分析linux系统调用过程,用例子讲解
80X86汇编DOS功能调用详解【 80X86汇编DOS功能调用详解
06.WSDL深入详解和WebService调用过程的底层本质
递归调用详解,分析递归调用的详细过程[参考].pdf
递归调用详解,代码详细讲解了如递归调用以及调用中应该注意的一些问题
汇编语言全接触 汇编语言详解 汇编语言 适合初学者
net调用存储过程详解 net调用存储过程详解 net调用存储过程详解
汇编指令汇编指令汇编指令汇编指令汇编指令汇编指令汇编指令汇编指令汇编指令汇编指令汇编指令汇编指令汇编指令汇编指令汇编指令汇编指令汇编指令汇编指令汇编指令
printf用法详解。最详细的printf
C语言Printf格式详解 C语言Printf格式详解
51单片机汇编指令详解 51单片机汇编指令详解
dos系统调用的详细解释 很好用 李振格等 编著 天方图书创作室
ARM汇编指令详解 可作为学习ARM汇编代码,查询之用,很详细
常用synopsys_dc命令详解汇编.pdf
股权转让个人所得税详解汇编.pdf
cisco最完美的ACL配置详解及配置全过程cisco最完美的ACL配置详解及配置全过程cisco最完美的ACL配置详解及配置全过程cisco最完美的ACL配置详解及配置全过程cisco最完美的ACL配置详解及配置全过程cisco最完美的ACL配置...
Java调用SQL存储过程详解.docx
本文给大家介绍了ARM汇编伪指令 宏的用法详解。
详细介绍了AVR的汇编的指令格式和应用例子
8086/8088汇编指令详解