简单的栈操作与函数调用

news/2025/3/14 22:47:45/文章来源:https://www.cnblogs.com/menkeyi/p/18773063

汇编语言基础:编码风格、栈操作与函数调用

汇编语言是计算机底层的编程语言,直接与硬件交互。掌握汇编语言不仅有助于理解程序的运行机制,还能优化性能。本文将介绍汇编语言的编码风格、栈操作以及函数调用的实现,并通过示例代码详细解析。


汇编编码风格

汇编语言主要有以下几种编码风格:

1. Intel 风格

  • 操作数顺序目标操作数, 源操作数
  • 示例
    mov eax, ebx  ; 将 ebx 的值移动到 eax
    add eax, 5    ; 将 eax 的值加 5
    
  • 特点
    • 寄存器名称和指令助记符通常是小写。
    • 直观易读,适合初学者。

2. AT&T 风格

  • 操作数顺序源操作数, 目标操作数
  • 示例
    movl $5, %eax  ; 将立即数 5 移动到 eax
    addl %ebx, %eax ; 将 ebx 加到 eax
    
  • 特点
    • 寄存器名前加 %,立即数前加 $,内存地址用 () 表示。
    • 主要用于 Unix/Linux 系统。

3. NASM 风格

  • 类似于 Intel 风格,但语法更严格。
  • 示例
    mov eax, 5
    add eax, ebx
    
  • 特点
    • 常用于编写 Linux 内核模块或引导程序。

objdump 工具

objdump 是一个反汇编工具,用于将二进制文件(如可执行文件或目标文件)转换为汇编代码。常用选项:

  • -d:反汇编代码段。
  • -M intel:指定使用 Intel 风格的汇编语法。
  • -M att:指定使用 AT&T 风格的汇编语法。

示例

objdump -d -M intel test64 > output.asm
  • test64 文件反汇编为 Intel 风格的汇编代码,并保存到 output.asm 文件中。

示例代码解析

以下是一个完整的汇编代码示例,展示了函数调用、栈操作和平栈的过程:

0000000000001129 <func>:1129:       f3 0f 1e fa             endbr64          ; 用于Intel CET的指令,防止ROP攻击112d:       55                      push   rbp        ; 保存旧的基址指针112e:       48 89 e5                mov    rbp,rsp    ; 设置新的基址指针1131:       89 7d fc                mov    DWORD PTR [rbp-0x4],edi  ; 保存第一个参数1134:       89 75 f8                mov    DWORD PTR [rbp-0x8],esi  ; 保存第二个参数1137:       89 55 f4                mov    DWORD PTR [rbp-0xc],edx  ; 保存第三个参数113a:       89 4d f0                mov    DWORD PTR [rbp-0x10],ecx ; 保存第四个参数113d:       44 89 45 ec             mov    DWORD PTR [rbp-0x14],r8d ; 保存第五个参数1141:       44 89 4d e8             mov    DWORD PTR [rbp-0x18],r9d ; 保存第六个参数1145:       8b 55 fc                mov    edx,DWORD PTR [rbp-0x4]  ; 将第一个参数加载到edx1148:       8b 45 f8                mov    eax,DWORD PTR [rbp-0x8]  ; 将第二个参数加载到eax114b:       01 c2                   add    edx,eax                  ; 将eax加到edx114d:       8b 45 f4                mov    eax,DWORD PTR [rbp-0xc]  ; 将第三个参数加载到eax1150:       01 c2                   add    edx,eax                  ; 将eax加到edx1152:       8b 45 f0                mov    eax,DWORD PTR [rbp-0x10] ; 将第四个参数加载到eax1155:       01 c2                   add    edx,eax                  ; 将eax加到edx1157:       8b 45 e8                mov    eax,DWORD PTR [rbp-0x18] ; 将第六个参数加载到eax115a:       01 c2                   add    edx,eax                  ; 将eax加到edx115c:       8b 45 10                mov    eax,DWORD PTR [rbp+0x10] ; 将第七个参数(栈上的参数)加载到eax115f:       01 c2                   add    edx,eax                  ; 将eax加到edx1161:       8b 45 18                mov    eax,DWORD PTR [rbp+0x18] ; 将第八个参数(栈上的参数)加载到eax1164:       01 d0                   add    eax,edx                  ; 将edx加到eax1166:       5d                      pop    rbp                      ; 恢复旧的基址指针1167:       c3                      ret                            ; 返回0000000000001168 <main>:1168:       f3 0f 1e fa             endbr64          ; 用于Intel CET的指令,防止ROP攻击116c:       55                      push   rbp        ; 保存旧的基址指针116d:       48 89 e5                mov    rbp,rsp    ; 设置新的基址指针1170:       6a 08                   push   0x8        ; 将第八个参数(8)压入栈1172:       6a 07                   push   0x7        ; 将第七个参数(7)压入栈1174:       41 b9 06 00 00 00       mov    r9d,0x6    ; 将第六个参数(6)放入r9d117a:       41 b8 05 00 00 00       mov    r8d,0x5    ; 将第五个参数(5)放入r8d1180:       b9 04 00 00 00          mov    ecx,0x4    ; 将第四个参数(4)放入ecx1185:       ba 03 00 00 00          mov    edx,0x3    ; 将第三个参数(3)放入edx118a:       be 02 00 00 00          mov    esi,0x2    ; 将第二个参数(2)放入esi118f:       bf 01 00 00 00          mov    edi,0x1    ; 将第一个参数(1)放入edi1194:       e8 90 ff ff ff          call   1129 <func> ; 调用func函数1199:       48 83 c4 10             add    rsp,0x10    ; 清理栈上的参数119d:       b8 00 00 00 00          mov    eax,0x0     ; 将返回值设置为011a2:       c9                      leave             ; 恢复栈帧11a3:       c3                      ret               ; 返回

栈操作与函数调用

1. 入栈操作

在函数调用时,参数和局部变量通常会被保存到栈中。以下代码将寄存器中的参数保存到栈中:

1131:       89 7d fc                mov    DWORD PTR [rbp-0x4],edi  ; 保存第一个参数
1134:       89 75 f8                mov    DWORD PTR [rbp-0x8],esi  ; 保存第二个参数
1137:       89 55 f4                mov    DWORD PTR [rbp-0xc],edx  ; 保存第三个参数
113a:       89 4d f0                mov    DWORD PTR [rbp-0x10],ecx ; 保存第四个参数
113d:       44 89 45 ec             mov    DWORD PTR [rbp-0x14],r8d ; 保存第五个参数
1141:       44 89 4d e8             mov    DWORD PTR [rbp-0x18],r9d ; 保存第六个参数

入栈的原因

  1. 保存寄存器状态

    • 函数调用时,寄存器可能被覆盖,因此需要将参数保存到栈中,以便后续使用。
    • 例如,DWORD PTR [rbp-0x4] 表示将 edi 的值保存到栈中,地址为 rbp-0x4
    • DWORD PTR 表示操作的数据大小为 4 字节(32 位),rbp-0x4 是栈中的一个偏移地址。
  2. 支持递归调用

    • 每次函数调用都会有自己的栈帧,保存参数和局部变量,避免数据冲突。
  3. 遵循调用约定

    • 在 x86-64 架构中,前 6 个参数通过寄存器传递(rdi, rsi, rdx, rcx, r8, r9),多余的参数通过栈传递。
    • 保存到栈中是为了统一处理,确保函数能够正确访问所有参数。

2. 平栈操作

平栈(Stack Alignment)是指在函数调用结束后,恢复栈指针 rsp 到调用前的状态,以保持栈的平衡。通常通过调整 rsp 的值来实现。

示例

1199:       48 83 c4 10             add    rsp,0x10
  • 这行代码将栈指针 rsp 增加 16 字节(0x10),用于清理栈上之前压入的两个 8 字节参数(push 0x8push 0x7)。
  • 平栈后,栈指针恢复到调用函数前的状态,保持栈的平衡。

平栈的原因

  1. 保持栈的完整性:函数调用时,参数和局部变量会占用栈空间。调用结束后,需要释放这些空间,否则会导致栈指针错误。
  2. 避免栈溢出:如果不平栈,栈空间会逐渐被耗尽,最终导致栈溢出。
  3. 遵循调用约定:在 x86-64 架构中,调用者负责清理栈上的参数。

堆栈图

以下是 main 函数调用 func 时的堆栈图:

+-------------------+
| 返回地址          | <-- rsp (调用 func 前)
+-------------------+
| 旧 rbp            | <-- rbp (main 函数的栈帧)
+-------------------+
| 局部变量/参数     |
+-------------------+
| 参数 8 (0x8)      | <-- rsp+0x10
+-------------------+
| 参数 7 (0x7)      | <-- rsp+0x8
+-------------------+
| 参数 6 (0x6)      | <-- r9d
+-------------------+
| 参数 5 (0x5)      | <-- r8d
+-------------------+
| 参数 4 (0x4)      | <-- ecx
+-------------------+
| 参数 3 (0x3)      | <-- edx
+-------------------+
| 参数 2 (0x2)      | <-- esi
+-------------------+
| 参数 1 (0x1)      | <-- edi
+-------------------+

总结

  1. 汇编编码风格:主要有 Intel、AT&T 和 NASM 风格,objdump 工具用于反汇编二进制文件。
  2. 入栈操作:保存参数和局部变量,避免寄存器被覆盖,并支持递归调用。
  3. 平栈操作:恢复栈指针,保持栈的完整性,避免栈溢出,并遵循调用约定。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/898909.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

3.14日报

所花时间:8h 代码量(行):220 博客量:6 了解到的知识点: 计算机网络:早上详细讲解了链路层部分,三个基本问题:封装成帧,透明传输,差错检测。 其中着重讲解了有关计算的二进制序列,随堂练习更是一半的计算 然后就是下午的软件测试 要做一个地铁售票

使用fastapi部署网页模板

背景 如果你想快速实现一个网站的前后端的部署,那么现成的站点模板是必须的,如果我们使用nginx做反向代理可以实现部署静态网站,如果我想相对请求或者页面有一些更新,那么就需要用到后端框架,这里我们使用fastapi去代理静态资源同时将模板的请求根据我们的需求进行动态的返…

第四周第五天

所用时间:270分钟 代码量(行):174 博客量(篇):1 了解到的知识点: 1.课堂测试 编写了简单的地铁购票系统app,实现了按站计算购票费用,将项目打包为apk后再手机端运行成功 学习了如何将项目打包为apk文件,如下图所示2.计算机网络

Apache Tomcat RCE漏洞复现(CVE-2025-24813)

今日官方披露 Apache Tomcat partial PUT文件上传反序列化漏洞。在 CVE-2025-24813 中tomcat开启文件会话持久化,攻击者可利用PUT上传文件,并构造恶意请求触发session文件反序列化。漏洞描述 该漏洞的核心在于不完整PUT请求上传时 Tomcat 会使用了一个基于用户提供的文件名和…

SparkSubmit提交任务到yarn及报错解决方案

SparkSubmit提交任务到yarn及报错解决方案@目录一、提交任务代码二、Linux提交可能出现的问题及解决方案情况1:JSON解析异常情况2:java.lang.InstantiationException spark.sql.driver情况3 中kafka:java.lang.NoClassDefFoundError: org/apache/kafka/clients/producer/Cal…

线程异步处理任务

实际开发中,service层可能会执行多个步骤,那有些步骤可能和主任务没有太大关联,那我们可以采用线程去处理,这样就提高响应速度,当然也可以采用MQ,此处介绍的是用线程处理 1、controller层@GetMapping("/thread")public void thread(){demo1Service.process();}…

08. 通用定时器

一、什么是通用定时器ESP32 S3 芯片配备了两个通用定时器组,每组均包含两个通用定时器和一个主系统看门狗定时器。每个通用定时器都具备多个通道。通过明确指定定时器号和通道号,用户可以精准地选定所需的定时器和通道。每个定时器均支持独立编程,并且具备微秒级的精确时间中…

全链路赋能游戏鸿蒙化适配,鸿蒙游戏开发者服务焕新升级

3月14日,华为游戏中心在成都开展了鸿蒙游戏开发者服务日线下活动。本次活动吸引了百余位游戏厂商代表以及开发者参与。华为一线技术专家团队与众多游戏开发者进行了面对面的深入交流,聚焦游戏鸿蒙化全流程技术实践,通过专家授课、案例解析与现场互动,为开发者提供从技术适配…

多线程程序设计(三)——Guarded Suspension

本文摘要了《Java多线程设计模式》一书中提及的 Guarded Suspension 模式的适用场景,并针对书中例子(若干名称有微调)给出一份 C++ 参考实现及其 UML 逻辑图,也列出与之相关的模式。 ◆ 适用场景 当线程访问的共享数据没有准备好时,让该线程进入等待状态,直到数据被准备好…

西部数据企业级硬盘HC310开盘数据恢复,300G左右数据量耗时半年

这块西数4T企业级硬盘HC310是杭州某研究所送过来的,突发损坏不识别,通电后咯吱咯吱敲盘异响,磁头坏了。这款企业级硬盘目前开盘成功率一般,因为磁头适配很困难,需要反复更换磁头,备件成本很高。这种硬盘的开盘难度跟服务器SCSI或SAS硬盘有的一拼,没有经验甚至拆一个废一…

5分钟,构建国产数据库智能体

近期,圈里很多朋友,都尝试利用 DeepSeek 构建自己的智能体。我也利用腾讯元器,将个人公众号内容做了个智能体,可以实现简单的问答。那么延展来看,智能体除了可利用公众号内容,也可使用离线文件等方式来构建。这不禁让我考虑,是否可用这样方式构造一个数据库智能体。说干…

Ubuntu 22.04 LTS 基于 Docker 部署 WordPress

Ubuntu 22.04 LTS 基于 Docker 部署 WordPress 1. 引言 WordPress 是全球最受欢迎的内容管理系统 (CMS),使用 Docker 可以简化其部署过程。本教程将介绍如何在 Ubuntu 22.04 LTS 上使用 Docker 部署 WordPress。2. WordPress 简介 2.1 WordPress 是什么? WordPress 是全球最流…