1. 程式人生 > 其它 >OpenEuler中C語言中的函式呼叫測試(選做)

OpenEuler中C語言中的函式呼叫測試(選做)

作業要求

  1. 在X86_64架構下實踐2.4中的內容
  2. 通過GDB檢視暫存器的內容,把教材中的圖填入具體的值
  3. 把2.4的C程式碼在OpenEuler中重新實踐一遍,繪製出ARM64的邏輯框圖
  4. 實驗內容要經過答辯才能得到相應分數

實踐流程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 mainr開始單步執行。

函式在執行前,都會先將自己的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實現的。