Mixed programming of C and assembly in OpenEuler (optional)

Operation requirements

  1. On X86_64 architecture, submit code and practice screenshots
  2. Practice the content of 2.5 in OpenEuler again, and submit relevant codes and screenshots
  3. The experimental content can only get the corresponding score after oral defense

Practice process - in x86_ Implementation under 64 architecture

The computer is x86_64 architecture, first implemented in ubuntu.

1. Compile C code into assembly code

a.c:

#include <stdio.h>

extern int B();

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

Compile as a.s and use 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. Implement simple functions in assembly language and call them in C

s.s:

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

s.c:

#include <stdio.h>

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

Mixed compilation of s.s and s.c, mixed compilation of C code and assembly code.
Use GCC - M32 s.s.c - O s. The program prints the values of the current eax and ebp registers.
Screenshot of compilation and operation:

3. Implement mysum() function with assembly code and call it with C

mysum.s:

	.text
	.global mysum, printf	#mysum is called and printf is called
	
mysum:
#	First, the stack frame is established
	pushl	%ebp
	movl	%esp, %ebp
#	mysum function code
	movl	8(%ebp), %eax	# AX = x
	addl	12(%ebp), %eax	# AX += y
	
	movl	%ebp, %esp
	pop	%ebp
	ret

mysum.c:

#include <stdio.h>

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

Screenshot of compilation and operation:
Compile using gcc -m32 mysum.s mysum.c -o mysum.

4. Use assembly to call C function

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 <stdio.h>

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

Screenshot of compilation and operation:

Practice process -- implementation and problem solving under openEuler

Because there is no package in openEuler that supports compiling and running 32-bit code, it can only be implemented under 64 bit. (assembly code needs to be modified)

1. Convert C code into assembly code

2. Implement simple functions in assembly language and call them in C

There are no problems in the above two steps. The 32-bit and 64 bit codes in ubuntu are compatible in openEuler.

3. Implement mysum() function with assembly code and call it with C

In this step, if the source assembly code is not modified, there will be problems during compilation, as shown in the figure:

It was found that there was a problem with push and pop. It's been revised for a long time, but it hasn't been solved. If you try to change it to pushl, there will be errors, segment errors, etc.
Later, I looked at some assembly code compiled in openEuler, such as the code in a.s. It is observed that in 64 bit openEuler, the sp and bp registers used are not esp and ebp, but rsp and rbp.
As shown in the figure:


I modify all ebp and esp in my assembly code to rbp and rsp, and then compile it. The following error is reported:

Originally, the number of bits of rbp and rsp is 64 bits. push and pop (the l suffix is 32 bits) can not be used. push and pop can be put on and out of the stack according to their size.
Modified mysum.s:

	.text
	.global mysum, printf	#mysum is called and printf is called
	
mysum:
#	First, the stack frame is established
	push	%rbp
	mov	%rsp, %rbp
#	mysum function code
	movl	8(%rbp), %eax	# AX = x
	addl	12(%rbp), %eax	# AX += y
	
	mov	%rbp, %rsp
	pop	%rbp
	ret

Finally, it was compiled and run successfully, and there was a problem in the result:

Estimation is also a problem caused by the number of digits.
Through cgdb, I checked the corresponding data storage location in the run, recalculated the difference between the location of a and b in memory and the location of ebp, and then modified the code.


(view the memory address of its value)


The difference is 24 and 28. After modifying the code, the program gets the correct results.

The final mysum.s code is:

	.text
	.global mysum, printf	#mysum is called and printf is called
	
mysum:
#	First, the stack frame is established
	push	%rbp
	mov	%rsp, %rbp
#	mysum function code
	mov	24(%rbp), %rax	# AX = x
	add	28(%rbp), %rax	# AX += y
	
	mov	%rbp, %rsp
	pop	%rbp
	ret

4. Use assembly to call C function

Through the attempt, it is found that only modifying the register to 64 bits can not solve the problem, and the segment error is still prompted. Through cgdb debugging, it is found that it occurs when calling printf.
Write a hello.c and call the printf function to see the principle.
hello.c:

#include <stdio.h>

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

Use gcc -S hello.c -o hello.s to get the assembly code of hello.c. The following is a screenshot of hello.s

It is found that different from the 32-bit in ubuntu, in the 64 bit of openeuler, the parameters of printf function not only exist in the stack, but also in ESI, EDX and EDI respectively.
Modified code:

	.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"

By modifying the code, the printf parameter in 64 bit is transferred again, and the final output is completed. The results are shown as follows:

Posted on Sun, 05 Dec 2021 14:06:48 -0500 by ryanyoungsma