OpenEuler中C語言中的函式呼叫測試(選做)
作業要求
- 在X86_64架構下實踐2.4中的內容
- 通過GDB檢視暫存器的內容,把教材中的圖填入具體的值
- 把2.4的C程式碼在OpenEuler中重新實踐一遍,繪製出ARM64的邏輯框圖
- 實驗內容要經過答辯才能得到相應分數
實踐流程1-在X86_64架構下
程式碼與彙編轉換
通過教材可知,64位和32位在引數傳遞上存在差異。32位在傳遞引數的時候是直接通過堆疊進行傳遞,而64位在傳遞傳輸的時候是先將前6個引數依次傳入rdi、rsi、rdx、rcx、r8、r9,然後剩餘的引數像32位一樣通過堆疊傳遞,在2.5的作業上用32位程式碼直接在64位上呼叫printf函數出現段錯誤的問題,教材這裡直接說明了,就是這個差異導致的。
示例程式碼t.c
#include <stdio.h>
int sub(int a,int b,int c,int d,int e,int f,int g,int h)
{
int u,v,w;
u = 9;
v = 10;
w = 11;
return a+g+u+v;
}
int main()
{
int a,b,c,d,e,f,g,h,i;
a=1;
b=2;
c=3;
d=4;
e=5;
f=6;
g=7;
h=8;
i=sub(a,b,c,d,e,f,g,h);
}
在x86_64的openEuler上轉為彙編程式碼並檢視:
t.s:
.file "t.c" .text .globl sub .type sub, @function sub: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl %edi, -20(%rbp) movl %esi, -24(%rbp) movl %edx, -28(%rbp) movl %ecx, -32(%rbp) movl %r8d, -36(%rbp) movl %r9d, -40(%rbp) movl $9, -4(%rbp) movl $10, -8(%rbp) movl $11, -12(%rbp) movl -20(%rbp), %edx movl 16(%rbp), %eax addl %eax, %edx movl -4(%rbp), %eax addl %eax, %edx movl -8(%rbp), %eax addl %edx, %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size sub, .-sub .globl main .type main, @function main: .LFB1: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $48, %rsp movl $1, -4(%rbp) movl $2, -8(%rbp) movl $3, -12(%rbp) movl $4, -16(%rbp) movl $5, -20(%rbp) movl $6, -24(%rbp) movl $7, -28(%rbp) movl $8, -32(%rbp) movl -24(%rbp), %r9d movl -20(%rbp), %r8d movl -16(%rbp), %ecx movl -12(%rbp), %edx movl -8(%rbp), %esi movl -4(%rbp), %eax movl -32(%rbp), %edi pushq %rdi movl -28(%rbp), %edi pushq %rdi movl %eax, %edi call sub addq $16, %rsp movl %eax, -36(%rbp) movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE1: .size main, .-main .ident "GCC: (GNU) 7.3.0" .section .note.GNU-stack,"",@progbits
gdb分析
使用gcc -g t.s -o t
將t.s進行編譯,可以通過cgdb t
直接對彙編程式碼進行除錯,檢視。
使用b main
,r
開始單步執行。
函式在執行前,都會先將自己的rbp推入堆疊。
使用s
單步執行。
首先是main函式的變數定義,首先esp減少48,為a~h提供空間。
檢視暫存器的值:用i r
,檢視對應記憶體中的值,用x 0x012345678abcdef
對應上圖ebp的值,檢視堆疊中的a~h的值:
分析出main函式存值後的堆疊狀態:
進入sub函式中後,在進行運算前,先將6個存在暫存器中的數存到了堆疊中:
然而,我看到程式碼並沒有將多出來的兩個引數(g和h)進行還原。
在運算時,需要將a、g、u、v相加。
其中,u和v是sub函式中定義的,a是通過暫存器傳進來的,而h是通過堆疊傳的。通過分析,h的值就是16(%rbp)。
下圖中的框就是相加的部分。
下圖為堆疊分析圖:
最後完成計算後,恢復rbp,返回。返回值存在rax中。
實踐流程2-在Arm64架構下
華為鯤鵬伺服器為Arm64架構。
程式碼與彙編轉換
arm64架構下的的t.s:
.cpu generic+fp+simd
.file "t.c"
.text
.align 2
.global sub
.type sub, %function
sub:
.LFB0:
.cfi_startproc
sub sp, sp, #48
.cfi_def_cfa_offset 48
str w0, [sp,28]
str w1, [sp,24]
str w2, [sp,20]
str w3, [sp,16]
str w4, [sp,12]
str w5, [sp,8]
str w6, [sp,4]
str w7, [sp]
mov w0, 9
str w0, [sp,44]
mov w0, 10
str w0, [sp,40]
mov w0, 11
str w0, [sp,36]
ldr w1, [sp,28]
ldr w0, [sp,4]
add w1, w1, w0
ldr w0, [sp,44]
add w1, w1, w0
ldr w0, [sp,40]
add w0, w1, w0
add sp, sp, 48
.cfi_def_cfa_offset 0
ret
.cfi_endproc
.LFE0:
.size sub, .-sub
.align 2
.global main
.type main, %function
main:
.LFB1:
.cfi_startproc
stp x29, x30, [sp, -64]!
.cfi_def_cfa_offset 64
.cfi_offset 29, -64
.cfi_offset 30, -56
add x29, sp, 0
.cfi_def_cfa_register 29
mov w0, 1
str w0, [x29,60]
mov w0, 2
str w0, [x29,56]
mov w0, 3
str w0, [x29,52]
mov w0, 4
str w0, [x29,48]
mov w0, 5
str w0, [x29,44]
mov w0, 6
str w0, [x29,40]
mov w0, 7
str w0, [x29,36]
mov w0, 8
str w0, [x29,32]
ldr w0, [x29,60]
ldr w1, [x29,56]
ldr w2, [x29,52]
ldr w3, [x29,48]
ldr w4, [x29,44]
ldr w5, [x29,40]
ldr w6, [x29,36]
ldr w7, [x29,32]
bl sub
str w0, [x29,28]
ldp x29, x30, [sp], 64
.cfi_restore 30
.cfi_restore 29
.cfi_def_cfa 31, 0
ret
.cfi_endproc
.LFE1:
.size main, .-main
.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-39)"
.section .note.GNU-stack,"",%progbits
引數傳遞流程分析
arm中:str
為儲存,ldr
為取。
上圖部分將a-h存到x29,60~x29,32的對應位置。
分析後的堆疊和流程圖:
可見,本例arm64中的函式引數傳遞是通過w0-w7實現的。