C语言与汇编混合编程

一、GCC 扩展语法与MSVC约束

(一)GCC(GNU Compiler Collection)内联汇编语法

asm("汇编指令");#或者
__asm__("汇编指令");#使用更复杂的语法来指定输入、输出操作数和修改的寄存器:
asm volatile ("汇编指令1\n\t""汇编指令2\n\t": 输出操作数列表: 输入操作数列表: 修改的寄存器列表
);

• 输出操作数列表:指定汇编指令的输出结果,格式为[约束符] (C变量),例如"=r" (result)表示将结果存储到一个通用寄存器中,并赋值给C变量result。
• 输入操作数列表:指定汇编指令的输入数据,格式为[约束符] (变量C),例如"r" (input)表示将C变量input的值放入一个通用寄存器作为输入。
• 修改的寄存器列表:列出汇编代码中修改的寄存器,防止编译器对这些寄存器进行优化,例如"cc"表示修改了条件码寄存器。

示例代码:

#include <stdio.h>int main() {int a = 10, b = 20, sum;asm volatile ("addl %1, %2\n\t""movl %2, %0\n\t": "=r" (sum)    // 输出操作数: "r" (a), "r" (b)  // 输入操作数: "cc"          // 修改的寄存器);printf("Sum = %d\n", sum);return 0;
}

(二)Microsoft Visual C++(MSVC)内联汇编语法

__asm {汇编指令1;汇编指令2;
}#或者在函数中直接使用asm关键字:
void func() {int a = 10, b = 20, sum;asm {mov eax, aadd eax, bmov sum, eax}
}

在MSVC中,内联汇编代码使用花括号括起来,汇编指令之间用分号分隔。

二、外部汇编

外部汇编是指将汇编代码写在单独的汇编文件中,然后通过链接器将其与C语言代码链接在一起。这种方式适合编写复杂的汇编代码模块。
(一)编写汇编文件
汇编函数示例:add.asm

section .text
global add
add:mov eax, [esp + 4]  ; 获取第一个参数add eax, [esp + 8]  ; 加第二个参数ret

这个汇编函数add接收两个参数,将它们相加并将结果存储在eax寄存器中。

(二)编译和链接
1.使用汇编器将汇编文件编译为目标文件:

nasm -f elf32 add.asm -o add.o

这里使用了NASM汇编器,将add.asm编译为32位ELF格式的目标文件add.o。

2.将目标文件与C语言代码编译链接:

gcc main.c add.o -o main

假设main.c是C语言主程序文件,将main.c和add.o链接生成可执行文件main。

(三)在C语言中调用汇编函数
在C语言代码中声明并调用汇编函数:main.c

extern int add(int, int);int main() {int a = 10, b = 20, sum;sum = add(a, b);printf("Sum = %d\n", sum);return 0;
}

通过extern关键字声明汇编函数add,然后在C语言代码中正常调用它。

三、注意事项

1.寄存器约定:不同的编译器和平台对寄存器的使用有不同的约定,在编写汇编代码时需要遵循这些约定。
2.数据类型对齐:在混合编程中,要注意C语言数据类型与汇编指令操作数之间的对齐。

二、参数传递陷阱:图解 cdecl 调用约定堆栈平衡过程

(一)cdecl 调用约定概述
• 在 cdecl 调用约定下,函数的参数是从右到左压入堆栈的,调用者负责平衡堆栈。这种调用约定比较灵活,允许函数有可变参数列表,但需要调用者在调用函数后清理堆栈。

(二)堆栈平衡过程图解

初始状态:
+----------------+
|                | <- SP(堆栈指针)
+----------------+1. 压入参数 b(值为 20)
+----------------+
|       20       | <- SP(堆栈指针)
+----------------+2. 压入参数 a(值为 10)
+----------------+
|       10       | <- SP(堆栈指针)
+----------------+
|       20       |
+----------------+3. 调用 add 函数,压入返回地址
+----------------+
| 0x00401000     | <- SP(堆栈指针)
+----------------+
|       10       |
+----------------+
|       20       |
+----------------+4. 函数内部执行,返回值存储在 eax 寄存器中
+----------------+
| 0x00401000     | <- SP(堆栈指针)
+----------------+
|       10       |
+----------------+
|       20       |
+----------------+5. 函数返回,弹出返回地址
+----------------+
|       10       | <- SP(堆栈指针)
+----------------+
|       20       |
+----------------+6. 弹出参数 a(值为 10)
+----------------+
|       20       | <- SP(堆栈指针)
+----------------+7. 弹出参数 b(值为 20)
+----------------+
|                | <- SP(堆栈指针)
+----------------+

1.函数调用前堆栈状态
• 假设有一个函数 int add(int a, int b),调用代码为 int result = add(10, 20);。
• 在调用前,堆栈是空的。

2.参数压栈过程
• 首先,将参数 b(值为 20)压入堆栈,堆栈指针(SP)向下移动。
• 然后,将参数 a(值为 10)压入堆栈,堆栈指针继续向下移动。
• 此时堆栈状态为:从堆栈顶部开始,依次是 10(a 的值)、20(b 的值)。

3.函数调用与返回
• 调用 add 函数时,函数地址被压入堆栈,然后跳转到函数代码执行。
• 在函数内部,通过堆栈指针访问参数 a 和 b,执行加法操作,将结果存储在某个寄存器或内存位置。
• 函数返回时,返回值被存储在约定的位置(如 eax 寄存器),然后返回指令将控制权交还给调用者。

4.堆栈平衡
• 调用者在函数返回后,需要清理堆栈。它将堆栈指针向上移动,弹出之前压入的参数(10 和 20),恢复堆栈到调用前的状态。
• 如果调用者忘记清理堆栈,会导致堆栈指针混乱,可能引发程序崩溃等严重错误。

三、混合调试技巧

(一)使用 objdump 解析混合目标文件符号
1.objdump 工具简介
• objdump 是一个用于显示目标文件信息的工具,它可以显示符号表、反汇编代码等内容。
2.解析混合目标文件符号
• 假设有一个混合了 C 和汇编代码的目标文件 mixed.o。
• 使用命令 objdump -t mixed.o 可以查看符号表,它会列出所有的符号(包括 C 函数名、全局变量名、汇编标签等)及其地址。
• 通过符号表,可以了解 C 函数汇和编代码之间的关联,比如某个 C 函数的入口地址对应汇编代码的哪个部分,这对于调试混合代码非常有帮助。

(二)在 GDB 中同时显示 C 源码与对应汇编
1.GDB 简介
• GDB(GNU Debugger)是一个功能强大的调试工具,可以用于调试 C、C++ 等语言编写的程序。

2.同时显示 C 源码与汇编
• 在 GDB 中,可以使用 disassemble 命令查看当前函数的汇编代码。
• 同时,使用 list 命令可以查看 C 源码。
• 通过结合这两个命令,可以在调试过程中同时查看 C 源码和对应的汇编代码,方便理解程序的执行过程。例如,当程序执行到某个 C 语言函数时,先用 list 查看该函数的源码,然后用 disassemble 查看对应的汇编代码,分析汇编代码是如何实现 C 语言功能的。

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

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

相关文章

WPF中的ListBox详解

文章目录简介ListBoxItem选中项目动态列表简介 【ListBox】是列表控件&#xff0c;其内部可包含多个【ListBoxItem】&#xff0c;用户可以从列表中选择一个或多个项&#xff0c;若Item个数超过指定高度&#xff0c;则右侧会自动出现滚动条&#xff0c;非常便捷。尽管逻辑上来说…

【历史人物】【李白】生平事迹

目录 一、李白个人简历 二、个人主要经历 三、个人成就及影响 1、诗 2、词 3、书法 4、剑术 5、理想 四、历史评价 五、趣事 1、李白搁笔 2、赠汪伦 一、李白个人简历 基本信息‌ 姓名&#xff1a;李白&#xff0c;字太白&#xff0c;号青莲居士 性别&#xff1…

HALCON+PCL混合编程

HALCON与PCL的混合编程基础 HALCON和PCL(Point Cloud Library)都是处理3D数据的强大工具&#xff0c;但它们有着不同的设计目标和数据结构。HALCON专注于机器视觉应用&#xff0c;提供了丰富的图像处理和分析功能&#xff1b;而PCL则是专门为点云处理设计的开源库。 要实现两者…

JavaScript书写基础和基本数据类型

JavaScript书写基础和基本数据类型 jarringslee js书写基础和规范 js是一种在客户端&#xff08;浏览器&#xff09;运行的编程语言&#xff0c;可实现人机交互的效果。js组成&#xff1a; js由两部分组成&#xff1a; ECMAScript&#xff1a;js的语言基础&#xff0c;js遵循其…

CSS个人笔记分享【仅供学习交流】

1、调整透明度 .text{ background-color: rgba(0, 0, 0, 0.08); }解释&#xff1a;rgba&#xff08;rgb三元素&#xff0c;透明度取值从0~1&#xff09; 2、文字和图片对齐方式 长用于头像旁边的昵称居中显示<img src"img/hua" alt"">华仔</img&g…

24.找到列表中最大或最小值的索引

找到列表中最大或最小值的索引 在 Python 中,如果你想找出某个列表中最小或最大值的位置(索引),你可以通过两步快速实现: 使用 min() 或 max() 获取目标值使用 .index() 获取目标值在列表中的索引位置✅ 基础实现 def min_element_index(arr):return arr.index(min(arr)

如何解决pip安装报错ModuleNotFoundError: No module named ‘pandas’问题

【Python系列Bug修复PyCharm控制台pip install报错】如何解决pip安装报错ModuleNotFoundError: No module named ‘pandas’问题 摘要 在使用 PyCharm 的 Python 控制台或终端执行 pip install pandas 后&#xff0c;仍然出现 ModuleNotFoundError: No module named ‘pandas…

【env环境】rtthread5.1.0使用fal组件

配置 board/Kconfigconfig BSP_USING_ON_CHIP_FLASHbool "Enable On Chip Flash"default ncp rt-thread/components/fal/samples/porting/fal_cfg.h board/fal_cfg.h /** Copyright (c) 2006-2018, RT-Thread Development Team** SPDX-License-Identifier: Apache-2.…

C++20 协程参考手册详解 - 源自 cppreference.com

C20 协程参考手册详解 - 源自 cppreference.com 人话版 先说“人说”&#xff0c;简化版本&#xff0c;更易理解。 宏观概念&#xff1a;协程是一个可以暂定和恢复执行的函数。&#xff08;普通函数是线程相关的&#xff0c;函数的调用依赖于线程栈&#xff0c;而协程的运行…

AI大模型训练的云原生实践:如何用Kubernetes指挥千卡集群?

当你的团队还在手动拼装显卡集群时&#xff0c;聪明人早已教会Kubernetes自动调度千卡。就像交响乐团需要指挥家&#xff0c;万级GPU需要云原生调度艺术。深夜的机房&#xff0c;硬件工程师老张盯着监控屏上跳动的红色警报——手工组装的千卡集群再次因单点故障崩溃。而隔壁团队…

java 在k8s中的部署流程

1.写Docker文件FROM ubuntu:22.04ENV LANGC.UTF-8 LC_ALLC.UTF-8RUN apt-get update \&& DEBIAN_FRONTENDnoninteractive apt-get install -y --no-install-recommends tzdata curl ca-certificates fontconfig locales binutils \&& echo "C.UTF-8 UTF-8…

静电式 vs UV 光解:哪种油烟净化技术更适合你的餐厅?

在餐饮行业&#xff0c;油烟净化是维持厨房环境、保障周边空气质量的关键环节。静电式与 UV 光解作为两种主流净化技术&#xff0c;各有其适用范围与局限性。选择时需结合餐厅的烹饪类型、油烟特点及环保要求&#xff0c;而非盲目追求技术先进或价格高低。一、技术原理&#xf…

Java全栈工程师面试实录:从电商系统到AIGC的层层递进

场景&#xff1a;互联网大厂Java面试官 vs 搞笑程序员小曾 第一轮提问 面试官&#xff1a;小曾&#xff0c;我们公司正在重构一个高并发的电商系统&#xff0c;需要使用Spring Cloud Alibaba进行服务拆分。你能描述一下如何用Nacos进行服务注册与发现&#xff0c;并解决服务雪崩…

C++ CRTP

C CRTP&#xff08;奇异递归模板模式&#xff09;CRTP 是什么&#xff1f; 一句话总结&#xff1a;CRTP 就是让子类把自己作为模板参数传递给父类。 听起来有点绕&#xff0c;直接上代码就明白了&#xff1a; template <typename Derived> class Base {// ... };class De…

21.映射字典的值

有时候你会希望保留字典的键不变,但将每个键对应的值应用一个函数进行转换,比如提取字段、做数学运算、格式化等。 ✅ 基本用法 你可以使用 dict.items() 搭配字典推导式或生成器表达式来实现。 def map_values(obj, fn):return dict((k, fn(v)

【算法】贪心算法:摆动序列C++

文章目录前言题目解析算法原理代码示例策略证明前言 题目的链接&#xff0c;大家可以先试着去做一下再来看一下思路。376. 摆动序列 - 力扣&#xff08;LeetCode&#xff09; 题目解析 将题目有用的信息划出来&#xff0c;结合示例认真阅读&#xff0c;去理解题目。 我们的摆…

【DOCKER】-6 docker的资源限制与监控

文章目录1、docker的资源限制1.1 容器资源限制的介绍1.2 OOM1.3 容器的内存限制1.3.1 内存限制的相关选项1.4 容器的CPU限制介绍2、docker的监控插件2.1 cadvisor2.2 portainer1、docker的资源限制 1.1 容器资源限制的介绍 默认情况下&#xff0c;容器没有资源的使用限制&…

gcc 源码分析--gimple 关键数据结构

gimple 操作码&#xff0c;支持这些&#xff1a;DEFGSCODE(GIMPLE_symbol, printable name, GSS_symbol). */ DEFGSCODE(GIMPLE_ERROR_MARK, "gimple_error_mark", GSS_BASE) DEFGSCODE(GIMPLE_COND, "gimple_cond", GSS_WITH_OPS) DEFGSCODE(GIMPLE_DEBU…

TDengine GREATEST 和 LEAST 函数用户手册

TDengine GREATEST 和 LEAST 函数用户手册 1. 需求背景 1.1 问题描述 在实际生产过程中&#xff0c;客户经常需要计算三相电流、电压的最大值和最小值。传统的实现方式需要使用复杂的 CASE WHEN 语句&#xff0c;例如&#xff1a; -- 传统方式&#xff1a;计算三相电流最大…

Redis 与数据库不一致问题及解决方案

一、不一致的原因分析 1. 缓存更新策略不当 先更新数据库后删除缓存:删除缓存失败会导致不一致 先删除缓存后更新数据库:并发请求可能导致不一致 缓存穿透:大量请求直接打到数据库,绕过缓存 2. 并发操作问题 读写并发:读请求获取旧缓存时,写请求更新了数据库但未更新缓存…