汇编学习(9), 命令行参数,C与汇编
2023-02-18 16:33:33 时间
本篇介绍
本篇介绍下汇编如何支持命令行函数,以及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 访问汇编
接下来写一个例子,用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。
Basic 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 内联需要注意的点如下:
- 第一行需要标明汇编类型,是ATT还是Intel,这样汇编器才会正确解析
- 汇编用到的参数必须定义成全局变量
- 如果使用的寄存器保存有关键数据,会被覆盖,这时候后果不确定,关于这一点通过 Extended Inline 可以解决。
Extended Inline
// 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
这儿明显的差异如下:
- 通过寄存器显示传递参数
- 需要声明可能会修改的寄存器,这样系统就会帮恢复原始值,避免异常
相关文章
- 透过底层看本质,hashMap原理讲解
- 华为3D实景地图,30分钟构建超精细数字世界,达到厘米级
- 全新版Adobe2023版本全家桶更新下载
- Topaz DeNoise AI for mac/win(图片降噪软件)
- JVM学习笔记之类装载器-ClassLoader
- 寒气遍布硅谷:推特员工公开指出马斯克错误遭解雇,亚马逊启动万人大裁员
- 【JVM】浅谈双亲委派和破坏双亲委派
- 大规模开放数字商业知识图谱评测基准来了:OpenBG上线天池
- 大模型能自己「写」论文了,还带公式和参考文献,试用版已上线
- 2022稳居C位的AIGC,到底有什么用?
- 用CNN做基础模型,可变形卷积InternImage实现检测分割新纪录!
- 解决神经网络的百年难题,MIT新模型Liquid CfC让模拟大脑动力学成为可能
- 腾讯教育助力首都师范大学“双优云桥”在平谷区落地实施“首师优字·墨香平谷”项目
- 闰秒终于要取消了!一文详解其来源及影响
- 大作玩着,显卡就着了火:英伟达因RTX 4090缺陷被用户起诉
- 无例可循,双十一倒逼出中国互联网「三高架构」
- 开源引擎GTS乾坤鼎:自动生产模型拿下FewCLUE榜单冠军
- Listener,Filter,Servlet执行顺序和生命周期
- 上线仅两天,AI大模型写论文网站光速下架:不负责任的胡编乱造
- 使用 TAT 命令禁用和恢复显卡驱动