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



推荐阅读
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社区 版权所有