`
Ydoing
  • 浏览: 105975 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

(转)详解汇编系统调用过程(以printf为例)

 
阅读更多

(转)详解汇编系统调用过程(以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>
分享到:
评论

相关推荐

    从汇编到c 调用约定 堆栈原理

    ### 汇编与C语言调用约定及堆栈原理详解 #### 一、引言 在计算机科学领域,理解程序执行过程中堆栈的操作是非常重要的。堆栈是一种数据结构,通常用于存储函数调用时的信息,如参数、局部变量以及返回地址等。本篇...

    详解linux系统调用原理

    在汇编语言层面,系统调用的执行过程如下: 1. **设置系统调用号**:将系统调用号(如`SYS_write`的编号4)放入`eax`寄存器。 2. **准备参数**:将参数按照特定顺序放入寄存器,如`ebx`(文件描述符)、`ecx`(缓冲...

    WINAVR2006 MEGA32使用printf

    ### WINAVR2006 MEGA32 使用 printf 的详解 #### 一、概述 在嵌入式开发中,`printf` 函数是调试代码非常有用的一个工具。本文将详细介绍如何在 WINAVR2006 开发环境中,利用 `printf` 函数进行调试,特别是针对 ...

    C++与操作系统等面试题40

    ### C++与操作系统等面试题40:程序构建过程详解 #### 一、程序构建的四个阶段 在软件开发过程中,一段高级语言代码(如C++)被转换为可执行的目标二进制代码,通常会经历四个关键阶段:预处理、编译、汇编和链接...

    反汇编引擎udis86

    **反汇编引擎udis86详解** 反汇编引擎是计算机科学中一个重要的工具,它能够将机器语言代码转换成人类可读的汇编语言形式。udis86是一个开源、轻量级且高效的C++实现的x86/x86-64架构反汇编库。这个库为开发者提供...

    哈工大计算机系统大作业-程序人生-Hello’s P2P

    汇编过程是将汇编语言转换为机器语言的过程。这一阶段的任务是将汇编语言文件转换为二进制格式的目标文件。在Linux环境下,可以使用`gcc -S hello.s -o hello.o`命令完成汇编过程。生成的目标文件(`.o`文件)包含了...

    编译原理课程设计之函数调用分析

    我们可以通过汇编语言的视角来看待这两种调用方式的差异: - 对于`__stdcall`: - 在进入函数之前,首先保存`ebp`寄存器的值。 - 调用结束后,通过`ret`指令返回,并清理8个字节的堆栈空间(即两个参数的大小)。...

    C语言与汇编语言混合编程_嵌入式汇编

    ### C语言与汇编语言混合编程详解:嵌入式应用视角 #### 一、引言 在嵌入式系统开发中,为了实现某些特定功能或提高性能,有时需要结合使用高级语言(如C语言)与低级语言(如汇编语言)。本文将详细介绍如何在...

    c语言开发技术详解c语言开发技术详解 戴建华

    C语言是一种广泛应用于系统编程...通过阅读《C语言开发技术详解》这本书,并结合提供的源代码实例,读者可以系统地学习C语言的各个方面,提升编程技能,并为将来进一步学习其他编程语言或深入计算机科学打下坚实基础。

    嵌入式Linux开发详解(高清PDF)

    - 配置与更新:设置主机名、用户及权限,以及定期更新系统以获取最新安全补丁和软件包。 2. **Linux基础命令** - 文件和目录操作:掌握ls、cd、pwd、mkdir、rm、mv、cp等基本命令,理解绝对路径与相对路径的区别...

    汇编masm 5.0

    **汇编语言MASM 5.0在Win7 32位系统中的使用详解** 汇编语言(Assembly)是一种低级编程语言,它与计算机硬件的指令集紧密相关,每一条汇编指令几乎对应着一个机器码。MASM(Microsoft Macro Assembler)是微软公司...

    ARM_汇编程序实现选择排序

    ### ARM汇编程序实现选择排序知识点详解 #### 一、实验背景及原理 在计算机科学领域,**排序算法**是基础且重要的数据处理方法之一,它对于数据的组织与检索至关重要。选择排序(Selection Sort)作为一种简单直观...

    HIT计算机系统大作业2024

    - **过程**:程序通过系统调用发起 I/O 请求,操作系统调度相应的驱动程序来处理这些请求。 #### 5.5 本章小结 - 总结了程序从磁盘加载到内存,再到执行的整个流程。 ### 结论 通过对“Hello World”程序从编写到...

    keil c51使用详解

    - **整体架构**:该系统由编译器(C51)、汇编器(A51)、链接器(L51)、库管理器(BL51)、调试器(DScope51 和 TScope51)、监控程序(Monitor51)以及集成开发环境(uVision)组成。 - **各组成部分的功能**: - **编译器(C51...

    汇编语言学习笔记 .doc

    ### 汇编语言学习笔记知识点详解 #### 一、编程环境的搭建 汇编语言的学习首先需要构建一个良好的编程环境。本节介绍了一个典型的汇编语言开发环境配置过程: - **MASM32**: MASM32是Microsoft提供的一个免费的...

    汇编 商品排序程序设计

    - 在排序过程中,当发现当前名称比下一个名称大时,调用此函数进行名称的交换。 7. **格式化输出** (`guang`): - 用于在输出商品名称时插入换行符和空行,提高输出的可读性。 #### 程序执行流程 1. **初始化...

    Assemblers, Linkers & Loaders

    ### 程序编译、连接与加载详解 在计算机科学领域中,程序从源代码到可执行程序的转换过程涉及多个步骤和技术,包括编译、汇编、连接以及加载等。本文将深入探讨这些技术及其工作原理。 #### 一、翻译层次结构 ...

    如何调用C标准库的exit函数详解

    在汇编语言中,exit函数通常是通过系统调用来实现的。例如,在Linux系统中,exit函数可以通过系统调用sys_exit来实现: ```assembly movl $1, %eax xorl %ebx, %ebx int $0x80 ``` 在上面的例子中,我们使用系统调用...

    C语言程序设计详解[收集].pdf

    在`main()`函数内部,我们可以定义变量,如例1中的`float a`,并使用语句(以分号结尾)来控制程序流程。此外,注释(`/*`和`*/`之间的内容)用于解释代码,不参与程序执行。 C语言没有内置的输入/输出语句,但提供...

Global site tag (gtag.js) - Google Analytics