本篇介绍下汇编如何支持命令行函数,以及C如何调用汇编。
看一个访问命令行参数的例子:
; cmdline.asm
section .data
NL db 10,0
msg db "The command and arguments: ",10,0
section .bss
section .text
global main
main:
push rbp
mov rbp,rsp
mov r12, rdi ;rdi contains number of arguments
mov r13, rsi ;rsi contains the address to the array of arguments
printArguments:
mov rdi, msg
call printString
mov rbx, 0
printLoop:
mov rdi, qword [r13+rbx*8]
call printString
mov rdi, NL
call printString
inc rbx
cmp rbx, r12
jl printLoop
leave
ret
global printString
printString:
push rbx
push rax
push r12
; Count characters
mov r12, rdi
mov rdx, 0
strLoop:
cmp byte [r12], 0
je strDone
inc rdx ;length in rdx
inc r12
jmp strLoop
strDone:
cmp rdx, 0 ; no string (0 length)
je prtDone
mov rsi,rdi
mov rax, 1
mov rdi, 1
syscall
prtDone:
pop r12
pop rax
pop rbx
ret
输出结果;
./cmdline 1 2 3
The command and arguments:
./cmdline
1
2
3
命令行参数个数会保存到rdi中,rsi会记录包含参数地址的数组地址。 接下来也可以debug确认下:
gdb --args ./cmdline 1 2 hello
b main
(gdb) info registers rsi
rsi 0x7fffffffde88 140737488346760
(gdb) info registers rdi
rdi 0x4 4
(gdb) x /4xg 0x7fffffffde88
0x7fffffffde88: 0x00007fffffffe1f3 0x00007fffffffe252
0x7fffffffde98: 0x00007fffffffe254 0x00007fffffffe256
(gdb) x /s 0x00007fffffffe252
0x7fffffffe252: "1"
(gdb) x s 0x00007fffffffe254
No symbol "s" in current context.
(gdb) x /s 0x00007fffffffe254
0x7fffffffe254: "2"
(gdb) x /s 0x00007fffffffe256
0x7fffffffe256: "hello"
接下来写一个例子,用C访问汇编实现的函数,extern 声明的函数由汇编实现,C代码如下:
#include <stdio.h>
#include <string.h>
extern int rsurface(int, int);
extern int rcircum(int, int);
extern double csurface( double);
extern double ccircum( double);
extern void sreverse(char *, int );
extern void adouble(double [], int );
extern double asum(double [], int );
int main()
{
char rstring[64];
int side1, side2, r_area, r_circum;
double radius,c_area, c_circum;
double darray[] = {70.0, 83.2, 91.5, 72.1, 55.5};
long int len;
double sum;
// call an assembly function with int arguments
printf("Compute area and circumference of a rectangle\n");
printf("Enter the length of one side : \n");
scanf("%d", &side1 );
printf("Enter the length of the other side : \n");
scanf("%d", &side2 );
r_area = rsurface(side1, side2);
r_circum = rcircum(side1, side2);
printf("The area of the rectangle = %d\n", r_area);
printf("The circumference of the rectangle = %d\n\n",
r_circum);
// call an assembly function with double (float) argument
printf("Compute area and circumference of a circle\n");
printf("Enter the radius : \n");
scanf("%lf", &radius);
c_area = csurface(radius);
c_circum = ccircum(radius);
printf("The area of the circle = %lf\n", c_area);
printf("The circumference of the circle = %lf\n\n", c_circum);
// call an assembly function with string argument
printf("Reverse a string\n");
printf("Enter the string : \n");
scanf("%s", rstring);
printf("The string is = %s\n", rstring);
sreverse(rstring,strlen(rstring));
printf("The reversed string is = %s\n\n", rstring);
// call an assembly function with array argument
printf("Some array manipulations\n");
len = sizeof (darray) / sizeof (double);
printf("The array has %lu elements\n",len);
printf("The elements of the array are: \n");
for (int i=0;i<len;i++){
printf("Element %d = %lf\n",i, darray[i]);
}
sum = asum(darray,len);
printf("The sum of the elements of this array = %lf\n", sum);
adouble(darray,len);
printf("The elements of the doubled array are: \n");
for (int i=0;i<len;i++){
printf("Element %d = %lf\n",i, darray[i]);
}
sum = asum(darray,len);
printf("The sum of the elements of this doubled array = %lf\n", sum);
return 0;
}
对应的汇编如下:
; rect.asm
section .data
section .bss
section .text
global rsurface
rsurface:
section .text
mov rax, rdi
imul rsi
ret
global rcircum
rcircum:
section .text
mov rax, rdi
add rax, rsi
imul rax, 2
ret
; circle.asm
section .data
pi dq 3.141592654
section .bss
section .text
global csurface
csurface:
section .text
movsd xmm1, qword [pi]
mulsd xmm0,xmm0 ;radius in xmm0
mulsd xmm0, xmm1
ret
global ccircum
ccircum:
section .text
movsd xmm1, qword [pi]
addsd xmm0,xmm0 ;radius in xmm0
mulsd xmm0, xmm1
ret
; sreverse.asm
section .data
section .bss
section .text
global sreverse
sreverse:
pushing:
mov rcx, rsi
mov rbx, rdi
mov r12, 0
pushLoop:
mov rax, qword [rbx+r12]
push rax
inc r12
loop pushLoop
popping:
mov rcx, rsi
mov rbx, rdi
mov r12, 0
popLoop:
pop rax
mov byte [rbx+r12], al
inc r12
loop popLoop
exit: mov rax, rdi
ret
; asum.asm
section .data
section .bss
section .text
global asum
asum:
section .text
;calculate the sum
mov rcx, rsi ;array length
mov rbx, rdi ;address of array
mov r12, 0
movsd xmm0, qword [rbx+r12*8]
dec rcx ; one loop less, first element already in xmm0
sloop:
inc r12
addsd xmm0, qword [rbx+r12*8]
loop sloop
ret ; return sum in xmm0
; adouble.asm
section .data
section .bss
section .text
global adouble
adouble:
section .text
;double the elements
mov rcx, rsi ;array length
mov rbx, rdi ;address of array
mov r12, 0
aloop:
movsd xmm0, qword [rbx+r12*8] ;take an element from array
addsd xmm0,xmm0 ; double it
movsd qword [rbx+r12*8], xmm0 ;move it to array
inc r12
loop aloop
ret
输出如下:
Compute area and circumference of a rectangle
Enter the length of one side :
10
Enter the length of the other side :
20
The area of the rectangle = 200
The circumference of the rectangle = 60
Compute area and circumference of a circle
Enter the radius :
5
The area of the circle = 78.539816
The circumference of the circle = 31.415927
Reverse a string
Enter the string :
hello
The string is = hello
The reversed string is = olleh
Some array manipulations
The array has 5 elements
The elements of the array are:
Element 0 = 70.000000
Element 1 = 83.200000
Element 2 = 91.500000
Element 3 = 72.100000
Element 4 = 55.500000
The sum of the elements of this array = 372.300000
The elements of the doubled array are:
Element 0 = 140.000000
Element 1 = 166.400000
Element 2 = 183.000000
Element 3 = 144.200000
Element 4 = 111.000000
The sum of the elements of this doubled array = 744.600000
关键信息就是通过C调用汇编,还是按照调用约定就行,返回值用rax或xmm0传递。
首先除非必要,尽量不用内联汇编。 内联汇编有2种,Basic Inline 和 Extended Inline。
首先看一个数字运算的例子:
#include <stdio.h>
int x=11,y=12,sum,prod;
int subtract(void);
void multiply(void);
int main(void)
{
printf("The numbers are %d and %d\n",x,y);
__asm__(
".intel_syntax noprefix;"
"mov rax,x;"
"add rax,y;"
"mov sum,rax"
);
printf("The sum is %d.\n",sum);
printf("The difference is %d.\n",subtract());
multiply();
printf("The product is %d.\n",prod);
}
int subtract(void)
{
__asm__(
".intel_syntax noprefix;"
"mov rax,x;"
"sub rax,y" // return value in rax
);
}
void multiply(void)
{
__asm__(
".intel_syntax noprefix;"
"mov rax,x;"
"imul rax,y;"
"mov prod,rax" //no return value, result in prod
);
}
结果如下:
$ ./inline1
The numbers are 11 and 12
The sum is 23.
The difference is -1.
The product is 132.
Basic 内联需要注意的点如下:
// inline2.c
#include <stdio.h>
int a=12; // global variables
int b=13;
int bsum;
int main(void)
{
printf("The global variables are %d and %d\n",a,b);
__asm__(
".intel_syntax noprefix\n"
"mov rax,a \n"
"add rax,b \n"
"mov bsum,rax \n"
:::"rax"
);
printf("The extended inline sum of global variables is %d.\n\n", bsum);
int x=14,y=16, esum, eproduct, edif; // local variables
printf("The local variables are %d and %d\n",x,y);
__asm__(
".intel_syntax noprefix;"
"mov rax,rdx;"
"add rax,rcx;"
:"=a"(esum)
:"d"(x), "c"(y)
);
printf("The extended inline sum is %d.\n", esum);
__asm__(
".intel_syntax noprefix;"
"mov rbx,rdx;"
"imul rbx,rcx;"
"mov rax,rbx;"
:"=a"(eproduct)
:"d"(x), "c"(y)
:"rbx"
);
printf("The extended inline product is %d.\n", eproduct);
__asm__(
".intel_syntax noprefix;"
"mov rax,rdx;"
"sub rax,rcx;"
:"=a"(edif)
:"d"(x), "c"(y)
);
printf("The extended inline asm difference is %d.\n", edif);
}
结果如下:
$ ./inline2
The global variables are 12 and 13
The extended inline sum of global variables is 25.
The local variables are 14 and 16
The extended inline sum is 30.
The extended inline product is 224.
The extended inline asm difference is -2.
模式如下:
asm (
assembler code
: output operands
*//* optional
: input operands
*//* optional
: list of clobbered registers
*//* optional
);
可选部分需要按照寄存器约束,常用部分映射关系如下:
image.png
这儿明显的差异如下: