热门标签 | 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



推荐阅读
  • 本文详细介绍了macOS系统的核心组件,包括如何管理其安全特性——系统完整性保护(SIP),并探讨了不同版本的更新亮点。对于使用macOS系统的用户来说,了解这些信息有助于更好地管理和优化系统性能。 ... [详细]
  • 本文详细介绍了如何在Ubuntu系统中下载适用于Intel处理器的64位版本,涵盖了不同Linux发行版对64位架构的不同命名方式,并提供了具体的下载链接和步骤。 ... [详细]
  • 本文详细介绍了C语言的起源、发展及其标准化过程,涵盖了从早期的BCPL和B语言到现代C语言的演变,并探讨了其在操作系统和跨平台编程中的重要地位。 ... [详细]
  • 本文详细探讨了Netty中Future及其子类的设计与实现,包括其在并发编程中的作用和具体应用场景。我们将介绍Future的继承体系、关键方法的实现细节,并讨论如何通过监听器和回调机制来处理异步任务的结果。 ... [详细]
  • Hadoop入门与核心组件详解
    本文详细介绍了Hadoop的基础知识及其核心组件,包括HDFS、MapReduce和YARN。通过本文,读者可以全面了解Hadoop的生态系统及应用场景。 ... [详细]
  • 解决IIS无法访问映射网络驱动器的问题
    探讨IIS在尝试访问映射的网络驱动器时遇到的问题及其解决方案,包括配置和权限设置等方面的详细分析。 ... [详细]
  • 本文详细记录了在银河麒麟操作系统和龙芯架构上使用 Qt 5.15.2 进行项目打包时遇到的问题及解决方案,特别关注于 linuxdeployqt 工具的应用。 ... [详细]
  • 5G至4G空闲态移动TAU流程解析
    本文详细解析了用户从5G网络移动到4G网络时,在空闲态下触发的跟踪区更新(TAU)流程。通过N26接口实现无缝迁移,确保用户体验不受影响。 ... [详细]
  • 深入探讨CPU虚拟化与KVM内存管理
    本文详细介绍了现代服务器架构中的CPU虚拟化技术,包括SMP、NUMA和MPP三种多处理器结构,并深入探讨了KVM的内存虚拟化机制。通过对比不同架构的特点和应用场景,帮助读者理解如何选择最适合的架构以优化性能。 ... [详细]
  • 本文探讨了领域驱动设计(DDD)的核心概念、应用场景及其实现方式,详细介绍了其在企业级软件开发中的优势和挑战。通过对比事务脚本与领域模型,展示了DDD如何提升系统的可维护性和扩展性。 ... [详细]
  • PostgreSQL 10 离线安装指南
    本文详细介绍了如何在无法联网的服务器上进行 PostgreSQL 10 的离线安装,并涵盖了从下载安装包到配置远程访问的完整步骤。 ... [详细]
  • HBase运维工具全解析
    本文深入探讨了HBase常用的运维工具,详细介绍了每种工具的功能、使用场景及操作示例。对于HBase的开发人员和运维工程师来说,这些工具是日常管理和故障排查的重要手段。 ... [详细]
  • 本文介绍了如何在C#应用程序中有效隐藏SQLCMD命令行窗口,确保程序运行时不会弹出黑色命令提示符窗口。 ... [详细]
  • 在创建新的Android项目时,您可能会遇到aapt错误,提示无法打开libstdc++.so.6共享对象文件。本文将探讨该问题的原因及解决方案。 ... [详细]
  • CentOS 6.5 上安装 MySQL 5.7.23 的详细步骤
    本文详细介绍如何在 CentOS 6.5 系统上成功安装 MySQL 5.7.23,包括卸载旧版本、下载安装包、配置文件修改及启动服务等关键步骤。 ... [详细]
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社区 版权所有