热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

OpenEuler中C与汇编的混合编程(选做)

作业要求在X86_64架构下实践2.5中的内容,提交代码和实践截图把2.5的内容在OpenEuler中重新实践一遍,提交相关代码和截图实验内容要经过答辩才能得到相应分数实践过程——

作业要求


  1. 在X86_64架构下实践2.5中的内容,提交代码和实践截图

  2. 把2.5的内容在OpenEuler中重新实践一遍,提交相关代码和截图

  3. 实验内容要经过答辩才能得到相应分数



实践过程——在x86_64架构下实现

电脑为x86_64架构,首先在ubuntu中实现一遍。


1. 将C代码编译成汇编代码

a.c:

#include
extern int B();
int A(int X, int y)
{
int d,e,f;
d = 4; e = 5; f = 6;
f = B(d,e);
}

编译为a.s,用cc -m32 -S a.c -o a.s

a.s:

.file "a.c"
.text
.globl A
.type A, @function
A:
.LFB0:
.cfi_startproc
endbr32
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
pushl %ebx
subl $20, %esp
.cfi_offset 3, -12
call __x86.get_pc_thunk.ax
addl $_GLOBAL_OFFSET_TABLE_, %eax
movl $4, -20(%ebp)
movl $5, -16(%ebp)
movl $6, -12(%ebp)
subl $8, %esp
pushl -16(%ebp)
pushl -20(%ebp)
movl %eax, %ebx
call B@PLT
addl $16, %esp
movl %eax, -12(%ebp)
nop
movl -4(%ebp), %ebx
leave
.cfi_restore 5
.cfi_restore 3
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size A, .-A
.section .text.__x86.get_pc_thunk.ax,"axG",@progbits,__x86.get_pc_thunk.ax,comdat
.globl __x86.get_pc_thunk.ax
.hidden __x86.get_pc_thunk.ax
.type __x86.get_pc_thunk.ax, @function
__x86.get_pc_thunk.ax:
.LFB1:
.cfi_startproc
movl (%esp), %eax
ret
.cfi_endproc
.LFE1:
.ident "GCC: (Ubuntu 9.4.0-1ubuntu1~20.04) 9.4.0"
.section .note.GNU-stack,"",@progbits
.section .note.gnu.property,"a"
.align 4
.long 1f - 0f
.long 4f - 1f
.long 5
0:
.string "GNU"
1:
.align 4
.long 0xc0000002
.long 3f - 2f
2:
.long 0x3
3:
.align 4
4:

2. 用汇编语言实现简单函数,用C调用

s.s

.global get_esp, get_ebp
get_esp:
movl %esp, %eax
ret
get_ebp:
movl %ebp, %eax
ret

s.c:

#include
int main()
{
int ebp, esp;
ebp = get_ebp();
esp = get_esp();
printf("ebp=%8x esp=%8x\n",ebp, esp);
}

对s.s和s.c进行混合编译,将C代码和汇编代码混合编译。

使用gcc -m32 s.s s.c -o s就可。程序会打印当前eax和ebp寄存器的值。

编译、运行截图:


3. 用汇编代码实现mysum()函数,用C调用

mysum.s:

.text
.global mysum, printf #mysum被调用,调用printf

mysum:
# 首先建立栈帧
pushl %ebp
movl %esp, %ebp
# mysum函数代码
movl 8(%ebp), %eax # AX = x
addl 12(%ebp), %eax # AX += y

movl %ebp, %esp
pop %ebp
ret

mysum.c:

#include
int main()
{
int a,b,c;
a = 2019; b = 1320;
c = mysum(a,b);
printf("c=%d\n", c);
}

编译运行截图:

使用gcc -m32 mysum.s mysum.c -o mysum进行编译。


4. 使用汇编调用C函数

printf.s:

.text
.global sub, a, b, printf
sub:
pushl %ebp
movl %esp, %ebp

pushl b
pushl a
pushl $fmt
call printf
addl $12, %esp

movl %ebp, %esp
popl %ebp
ret

.data
fmt: .asciz "a=%d,b=%d\n"

printf.c:

#include
int a,b;
int main()
{
a = 13; b = 20;
sub();
return 0;
}

编译运行截图:


实践过程——在openEuler下实现和问题解决

由于openEuler中没有支持编译运行32位代码的包,所以只能在64位下实现。(需要修改汇编代码)



1. 将C代码转为汇编代码


2. 用汇编语言实现简单函数,用C调用



以上两步没有出现任何问题,ubuntu中32位和64位的代码在openEuler中兼容。




3. 用汇编代码实现mysum()函数,用C调用

在这一步,若不修改源汇编代码,编译时出现问题,如图:



发现push和pop出现了问题。修改了很久,都没有解决。尝试过修改为pushl等,都会出错,出现段错误等。

后来我查看了一些openEuler中编译出的汇编代码,如a.s中的代码。观察发现发现在64位openEuler中,使用的sp和bp寄存器不是esp和ebp,而是rsp和rbp。

如图:





我将自己的汇编代码中的所有ebp,esp修改为rbp,rsp,然后再编译,出现以下报错:



原来,rbp和rsp的位数是64位,不能用pushl和popl(l后缀为32位。)直接使用push和pop,就可以按照其大小进行入栈和出栈了。

修改后的mysum.s:

.text
.global mysum, printf #mysum被调用,调用printf

mysum:
# 首先建立栈帧
push %rbp
mov %rsp, %rbp
# mysum函数代码
movl 8(%rbp), %eax # AX = x
addl 12(%rbp), %eax # AX += y

mov %rbp, %rsp
pop %rbp
ret

最后成功编译运行,结果运行结果出现问题:



估计也是位数导致的问题。

通过cgdb,我查看了运行中对应的数据存放的位置,重新计算了a和b在内存中的位置和ebp说存放位置之间的差值,然后修改了代码。





(查看其值所在内存地址)





差值为24和28,修改代码后,在此测试,程序得到了正确结果。

最后的mysum.s代码为:

.text
.global mysum, printf #mysum被调用,调用printf

mysum:
# 首先建立栈帧
push %rbp
mov %rsp, %rbp
# mysum函数代码
mov 24(%rbp), %rax # AX = x
add 28(%rbp), %rax # AX += y

mov %rbp, %rsp
pop %rbp
ret

4. 使用汇编调用C函数

通过尝试,发现只修改寄存器为64位无法解决问题,仍然提示段错误,通过cgdb调试发现是在调用printf时出现的。

自己编写一个hello.c,调用printf函数,查看原理。

hello.c:

#include
int main()
{
int a,b;
a=13;
b=20;
printf("a=%d,b=%d\n",a,b);
return 0;
}

gcc -S hello.c -o hello.s得到hello.c的汇编代码,以下为hello.s截图



发现和ubuntu中32位不同,openEuler64位中,printf函数的参数不光是存在栈中的,而是还分别在esi,edx,edi中。

修改后的代码:

.text
.global sub, a, b, printf
sub:
push %rbp
mov %rsp, %rbp

push a
push b
movl -8(%rbp),%esi
movl -16(%rbp),%edx
movl $fmt,%edi
call printf

add $16, %rsp
mov %rbp, %rsp
pop %rbp
ret

.data
fmt: .asciz "a=%d,b=%d\n"

通过修改代码,重新实现64位中的printf参数传递,最终完成输出,结果图如下:


代码链接

https://gitee.com/Ressurection20191320/code/tree/master/IS/MixedProgramming



推荐阅读
  • 本文详细介绍如何在华为鲲鹏平台上构建和使用适配ARM架构的Redis Docker镜像,解决常见错误并提供优化建议。 ... [详细]
  • 本文详细记录了一位Java程序员在Lazada的面试经历,涵盖同步机制、JVM调优、Redis应用、线程池配置、Spring框架特性等多个技术点,以及高级面试中的设计问题和解决方案。 ... [详细]
  • RabbitMQ 核心组件解析
    本文详细介绍了RabbitMQ的核心概念,包括其基本原理、应用场景及关键组件,如消息、生产者、消费者、信道、交换机、路由键和虚拟主机等。 ... [详细]
  • 深度解析:用友云Pontus限流服务提升系统稳定性
    本文深入探讨了用友云Pontus限流服务的技术细节及其在提高系统稳定性方面的作用,特别是在面对突发流量时的保护机制。 ... [详细]
  • 本文深入探讨了领域驱动设计(DDD)中的聚合概念及其在事件溯源架构中的应用。聚合是一组紧密相关的类,这些类作为一个整体运作,形成一个有明确边界的组织。只有通过聚合根才能与聚合内的对象进行交互。 ... [详细]
  • 本文介绍了基于Java的在线办公工作流系统的毕业设计方案,涵盖了MyBatis框架的应用、源代码分析、调试与部署流程、数据库设计以及相关论文撰写指导。 ... [详细]
  • 微服务自动化.dockercompose
    目录一、docker-compose二、docker-compose安装与配置1、修改docker.service2、下载文件3、将刚才下载的docker-compose文 ... [详细]
  • 初探Hadoop:第一章概览
    本文深入探讨了《Hadoop》第一章的内容,重点介绍了Hadoop的基本概念及其如何解决大数据处理中的关键挑战。 ... [详细]
  • Java高级工程师学习路径及面试准备指南
    本文基于一位朋友的PDF面试经验整理,涵盖了Java高级工程师所需掌握的核心知识点,包括数据结构与算法、计算机网络、数据库、操作系统等多个方面,并提供了详细的参考资料和学习建议。 ... [详细]
  • 本文由公众号【数智物语】(ID: decision_engine)发布,关注获取更多干货。文章探讨了从数据收集到清洗、建模及可视化的全过程,介绍了41款实用工具,旨在帮助数据科学家和分析师提升工作效率。 ... [详细]
  • 将XML数据迁移至Oracle Autonomous Data Warehouse (ADW)
    随着Oracle ADW的推出,数据迁移至ADW成为业界关注的焦点。特别是XML和JSON这类结构化数据的迁移需求日益增长。本文将通过一个实际案例,探讨如何高效地将XML数据迁移至ADW。 ... [详细]
  • 本文详细介绍了 Node.js 中 OS 模块的 arch 方法,包括其功能、语法、参数以及返回值,并提供了具体的使用示例。 ... [详细]
  • 本文详细介绍了在Linux操作系统上安装和部署MySQL数据库的过程,包括必要的环境准备、安装步骤、配置优化及安全设置等内容。 ... [详细]
  • 乐东老乡们注意了,现在可以在普通的PC机上安装Mac OS X系统了。对于那些对图形图像和多媒体处理有需求的朋友们来说,这是一个好消息。 ... [详细]
  • 对象存储与块存储、文件存储等对比
    看到一篇文档,讲对象存储,好奇,搜索文章,摘抄,学习记录!背景:传统存储在面对海量非结构化数据时,在存储、分享与容灾上面临很大的挑战,主要表现在以下几个方面:传统存储并非为非结 ... [详细]
author-avatar
勉情于你
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有