第9部分- Linux ARM汇编 语法
2021/4/16 7:27:31
本文主要是介绍第9部分- Linux ARM汇编 语法,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
AArch64上基本所有指令都有3个操作符。一个目标寄存器和2个源寄存器。
例如:
add w5, w3, w4 // w5 ← w3 + w4 复制代码
或者:
add x5, x3, x4 // x5 ← x3 + x4 复制代码
可以第32个通用寄存器:
add w0, w1, wzr // w0 ← w1 + 0 复制代码
add w0, w1, #2 // w0 ← w1 + 2 复制代码
add w0, w1, #-2 // w0 ← w1 + (-2) 复制代码
这里要注意的是如果是立即数,只有第二个源操作数才被允许是立即数。
减法同理。
注意的是:减法中有rsb(Reverse Substract)就是被减数和减数相比sub是反的。
乘法
在32位中有mul,smull,umull。
Mul不会更新cpsr。
Smull通过补码相乘。
Umull是无符号值。
语法:
{s,u}mull RdestLower, RdestHigher, Rsource1, Rsource2
乘法示例
一个64位的数变成两部分,高32位和低32位。
n = 232 × nhigher + nlower
例子(232 * 2+ 3755744309)*12345678
x 和y = z
Z=X*Y=(232 * X1+ X0)*(232 * Y1+ Y0)
那么 z = 264 × x1 × y1 + 232 × (x0 × y1 + x1 × y0) + x0 × y0
.data .align 4 message : .asciz "Multiplication of %lld by %lld is %lld\n" .align 8 number_a_low: .word 3755744309 number_a_high: .word 2 .align 8 number_b_low: .word 12345678 number_b_high: .word 0 .text /* 不是64位乘法的高效方法,用于指令乘目的,逻辑上相比不严谨.*/ mult64: /* 参数通过r0, r1 and r2, r3 and 传递,并通过r0, r1返回 */ /* 保存需要被覆盖的寄存器*/ push {r4, r5, r6, r7, r8, lr} /* 移动{r0,r1} 到{r4,r5} */ mov r4, r0 /* r4 ← r0 */ mov r5, r1 /* r5 ← r1 */ umull r0, r6, r2, r4 /* {r0,r6} ← r2 * r4 (X0*Y1)*/ umull r7, r8, r3, r4 /* {r7,r8} ← r3 * r4 (x0 × y1 )*/ umull r4, r5, r2, r5 /* {r4,r5} ← r2 * r5 (x1 × y0 )*/ adds r2, r7, r4 /* r2 ← r7 + r4 and update cpsr */ adc r1, r2, r6 /* r1 ← r2 + r6 + C,高位相加,X1*Y1没有 */ /* Restore registers */ pop {r4, r5, r6, r7, r8, lr} bx lr /* Leave mult64 */ .global main main: push {r4, r5, r6, r7, r8, lr} /* 保存寄存器 */ /* Load the numbers from memory */ /* {r4,r5} ← a */ ldr r4, addr_number_a_low /* r4 ← &a_low */ ldr r4, [r4] /* r4 ← *r4 */ ldr r5, addr_number_a_high /* r5 ← &a_high */ ldr r5, [r5] /* r5 ← *r5 */ /* {r6,r7} ← b */ ldr r6, addr_number_b_low /* r6 ← &b_low */ ldr r6, [r6] /* r6 ← *r6 */ ldr r7, addr_number_b_high /* r7 ← &b_high */ ldr r7, [r7] /* r7 ← *r7 */ /*第一个参数通过 {r0,r1} ,第二个参数通过 {r2,r3}*/ mov r0, r4 /* r0 ← r4 */ mov r1, r5 /* r1 ← r5 */ mov r2, r6 /* r2 ← r6 */ mov r3, r7 /* r3 ← r7 */ bl mult64 /* 调用mult64函数*/ /* 结果保存在r0,r1 */ /* Now prepare the call to printf */ /* We have to pass &message, {r4,r5}, {r6,r7} and {r0,r1} */ push {r1} /* 4th (higher) parameter */ push {r0} /* 4th (lower) parameter */ push {r7} /* 3rd (higher) parameter */ push {r6} /* 3rd (lower) parameter */ mov r3, r5 /* r3 ← r5. 2rd (higher) parameter */ mov r2, r4 /* r2 ← r4. 2nd (lower) parameter */ ldr r0, addr_of_message /*r0 ← &message 第一个参数,这里r1被跳过*/ bl printf /*调用printf函数*/ add sp, sp, #16 /* sp ← sp + 16 */ /* Pop the two registers we pushed above */ mov r0, #0 /* r0 ← 0 */ pop {r4, r5, r6, r7, r8, lr}/* 恢复寄存器*/ bx lr addr_of_message : .word message addr_number_a_low: .word number_a_low addr_number_a_high: .word number_a_high addr_number_b_low: .word number_b_low addr_number_b_high: .word number_b_high 复制代码
as -g -o mult64.o mult64.s
gcc -o mult64 mult64.o
调用函数mult64。
$./mult64
Multiplication of 12345678901 by 12345678 is 152415776403139878
其中打印结果有点复杂。64位必须作为成对的连续寄存器传递,其中下半部分位于偶数寄存器中。
同时函数的前4个参数通过r0,r1,r2,r3顺序传递。超过4个参数后就会使用堆栈。这个其实就是个约定。
64位乘法示例
上诉的32位乘法改成64位机器上就非常建议的,因为寄存器变成64位了。
例子(232 * 2+ 3755744309)*12345678
按照32位的逻辑如下:
.data .align 8 message : .asciz "Multiplication of %lld,%lld by %lld,%lld is %lld\n" .align 8 number_a_low: .word 3755744309 number_a_high: .word 2 .align 8 number_b_low: .word 12345678 number_b_high: .word 0 .text /* 不是64位乘法的高效方法,用于指令乘目的,逻辑上相比不严谨.*/ .type factorial,@function mult64: /* 参数通过x0, x1 ,x2, x3传递,并通过x0返回 */ /* 移动{x0,x1} 到{x4,x5} */ mov w4, w0 /* x4 ← x0 */ mov w5, w1 /* x5 ← x1 */ umulh x6, x2, x4 /* 低32位相乘,如果有高位,则位于w6中*/ umull x0, w2, w4 /*低32位相乘的结果 */ umull x7, w3, w4 /* {r7,r8} ← r3 * r4 (x1 × y0)*/ umull x4, w2, w5 /* {r4,r5} ← r2 * r5 (x0 × y1 )*/ adds x2, x7, x4/*r2 ← r7 + r4 and update cpsr,高32位相加得到结果*/ adc x1, x2, x6 /* x1 ← x2 + w6 + C,高位相加,X1*Y1没有 */ mov x1, x1, lsl #32//将高位右移动32位,然后和低位相加就是最后的结果 adc x0, x0, x1 ret .global _start _start: /* {x4,r5} ← a */ ldr x4, addr_number_a_low /* r4 ← &a_low */ ldr w4, [x4] /* r4 ← *r4 */ ldr x5, addr_number_a_high /* r5 ← &a_high */ ldr w5, [x5] /* r5 ← *r5 */ /* {x6,x7} ← b */ ldr x6, addr_number_b_low /* r6 ← &b_low */ ldr w6, [x6] /* r6 ← *r6 */ ldr x7, addr_number_b_high /* r7 ← &b_high */ ldr w7, [x7] /* r7 ← *r7 */ /*第一个参数通过 {r0,r1} ,第二个参数通过 {r2,r3}*/ mov w0, w4 /* r0 ← r4 */ mov w1, w5 /* r1 ← r5 */ mov w2, w6 /* r2 ← r6 */ mov w3, w7 /* r3 ← r7 */ bl mult64 /* 调用mult64函数*/ /* 结果保存在r0,r1 */ /* 准备调用 call 函数*/ mov x5,x0;//64位结果 ldr x2, addr_number_a_low ldr x2, [x2] ldr x1, addr_number_a_high ldr x1, [x1] ldr x3, addr_number_b_low ldr x3, [x3] ldr x4, addr_number_b_high ldr x4, [x4] ldr x0, addr_of_message /*r0 ← &message 第一个参数,这里r1被跳过*/ bl printf /*调用printf函数*/ mov x0, #0 /* r0 ← 0 */ mov x8, 93 svc 0 addr_of_message : .dword message addr_number_a_low: .dword number_a_low addr_number_a_high: .dword number_a_high addr_number_b_low: .dword number_b_low addr_number_b_high: .dword number_b_high 复制代码
as -g -o mult64.o mult64.s
ld -o mult64 mult64.o -lc -I /lib64/ld-linux-aarch64.so.1
ARM64中参数1~参数8 分别保存到 X0~X7 寄存器中 ,剩下的参数从右往左一次入栈,被调用者实现栈平衡,返回值存放在 X0 中。
$./mult64
Multiplication of 2,12345678901 by 12345678,0 is 152415776403139878
64位简化逻辑
按照64位的简化逻辑如下:
.data .align 8 message : .asciz "Multiplication of %lld by %lld is %lld\n" .align 8 number_a: .dword 12345678901 .align 8 number_b: .dword 12345678 .text /* 不是64位乘法的高效方法,用于指令乘目的,逻辑上相比不严谨.*/ .type factorial,@function mult64: mul x0, x0, x1 ret .global _start _start: /* {x4} ← a */ ldr x4, addr_number_a /* r4 ← &a */ ldr x4, [x4] /* r4 ← *r4 */ /* {x6} ← b */ ldr x6, addr_number_b /* r6 ← &b */ ldr x6, [x6] /* r6 ← *r6 */ /*参数通过 x0,x1*/ mov x0, x4 /* x0 ← x4 */ mov x1, x6 /* x1 ← x5 */ bl mult64 /* 调用mult64函数*/ /* 结果保存在r0,r1 */ /* 准备调用 call 函数*/ mov x3,x0;//64位结果 mov x1,x4 mov x2,x6 ldr x0, addr_of_message /*r0 ← &message 第一个参数,这里r1被跳过*/ bl printf /*调用printf函数*/ mov x0, #0 /* r0 ← 0 */ mov x8, 93 svc 0 addr_of_message : .dword message addr_number_a: .dword number_a addr_number_b: .dword number_b 复制代码
as -g -o mult64s.o mult64s.s
ld -o mult64s mult64s.o -lc -I /lib64/ld-linux-aarch64.so.1
结果如下:
$./mult64s
Multiplication of 12345678901 by 12345678 is 152415776403139878
除法
大多数计算机执行整数除法,其余数部分与分子的符号相同。
无符号整数除法是包含两个无符号整数N和D的整数除法。商Q和余数R始终为正。
在ARMv6没有整数除法指令的(有浮点除法指令),但是在armv8上是有除法指令的。
看下除法指令:
整数除法
SDIV Wd, Wn, Wm
Signed Divide: Wd = Wn ÷ Wm, treating source operands as signed.
SDIV Xd, Xn, Xm
Signed Divide (extended): Xd = Xn ÷ Xm, treating source operands as signed.
UDIV Wd, Wn, Wm
Unsigned Divide: Wd = Wn ÷ Wm, treating source operands as unsigned.
UDIV Xd, Xn, Xm
Unsigned Divide (extended): Xd = Xn ÷ Xm, treating source operands as unsigned.
64位整数除法示例
.data .balign 4 message1: .asciz "Hey, type a number: " .balign 4 message2: .asciz "I read the number %d/%d\n" .balign 4 message3: .asciz "Result, Q: %d,R: %d\n" /* Format pattern for scanf */ .balign 4 scan_pattern : .asciz "%d %d" /* Where scanf will store the number read */ .balign 4 number_N: .dword 0 number_D: .dword 0 .balign 4 return: .word 0 .arch armv8-a .global _start .text _start: ldr x0, address_of_message1 /* r0 ← &message1 */ bl printf /* call to printf */ ldr x0, address_of_scan_pattern /* r0 ← &scan_pattern */ ldr x1, address_of_number_N /* r1 ← &number_read */ ldr x2, address_of_number_D /* r1 ← &number_read */ bl scanf /* call to scanf */ ldr x0, address_of_message2 /* r0 ← &message2 */ ldr x1, address_of_number_N /* r1 ← &number_read */ ldr x1, [x1] /* r1 ← *r1 */ ldr x2, address_of_number_D /* r1 ← &number_read */ ldr x2, [x2] /* r1 ← *r1 */ bl printf /* call to printf */ ldr x0, address_of_message3 /* r0 ← &number_read */ ldr x1, address_of_number_N /* r1 ← &number_read */ ldr x1, [x1] /* r1 ← *r1 */ ldr x2, address_of_number_D /* r1 ← &number_read */ ldr x2, [x2] /* r1 ← *r1 */ udiv x3, x1, x2 msub x4,x3,x2,x1 mov x2,x4//余数给x2 mov x1,x3//商 bl printf /* call to printf */ ldr x0, 0 /* r0 ← *r0 */ mov x8, 93 svc 0 address_of_message1 : .dword message1 address_of_message2 : .dword message2 address_of_message3 : .dword message3 address_of_scan_pattern : .dword scan_pattern address_of_number_N: .dword number_N address_of_number_D : .dword number_D address_of_return : .dword return /* External */ .global printf .global scanf 复制代码
as -g -o div.o div.s
ld -o div div.o -lc -I /lib64/ld-linux-aarch64.so.1
这里的余数获取,并有直接的寄存器保存,所以获取通过msub指令:
MSUB Wd, Wn, Wm, Wa
Multiply-Subtract: Wd = Wa – (Wn × Wm).
测试运行如下:
$./div
Hey, type a number: 242 3
I read the number 242/3
Result, Q: 80,R: 2
浮点除法
FDIV Sd, Sn, Sm
Single-precision floating-point scalar division: Sd = Sn / Sm.
FDIV Dd, Dn, Dm
Double-precision floating-point scalar division: Dd = Dn / Dm.
FDIV Vd.<T>, Vn.<T>, Vm.<T>
Floating-point divide (vector). Where <T> is 2S, 4S or 2D.
64位浮点除法示例
.data .balign 4 message1: .asciz "Hey, type a number: " .balign 4 message2: .asciz "I read the number %5.2f/%5.2f\n" .balign 4 message3: .asciz "Result, Q: %5.2f\n" /* Format pattern for scanf */ .balign 4 scan_pattern : .asciz "%f %f" /* Where scanf will store the number read */ .balign 4 number_N: .dword 0 number_D: .dword 0 .balign 4 return: .word 0 .arch armv8-a .global _start .text _start: ldr x0, address_of_message1 /* r0 ← &message1 */ bl printf /* call to printf */ ldr x0, address_of_scan_pattern /* r0 ← &scan_pattern */ ldr x1, address_of_number_N /* r1 ← &number_read */ //ldr d0, address_of_number_N ldr x2, address_of_number_D /* r1 ← &number_read */ //ldr d1, address_of_number_D bl scanf /* call to scanf */ ldr x0, address_of_message2 /* r0 ← &message2 */ ldr x1, address_of_number_N /* r1 ← &number_read */ ldr s0, [x1] /* r1 ← *r1 */ fcvt d0,s0 ldr x2, address_of_number_D /* r1 ← &number_read */ ldr s1, [x2] /* r1 ← *r1 */ fcvt d1,s1 bl printf /* call to printf */ ldr x0, address_of_message3 /* r0 ← &number_read */ ldr x1, address_of_number_N /* r1 ← &number_read */ ldr s0, [x1] /* r1 ← *r1 */ fcvt d0,s0 ldr x2, address_of_number_D /* r1 ← &number_read */ ldr s1, [x2] /* r1 ← *r1 */ fcvt d1,s1 fdiv d3, d0, d1 fmov d0,d3//商 bl printf /* call to printf */ ldr x0, 0 /* r0 ← *r0 */ mov x8, 93 svc 0 address_of_message1 : .dword message1 address_of_message2 : .dword message2 address_of_message3 : .dword message3 address_of_scan_pattern : .dword scan_pattern address_of_number_N: .dword number_N address_of_number_D : .dword number_D address_of_return : .dword return /* External */ .global printf .global scanf 复制代码
as -g -o fdiv.o fdiv.s
ld -o fdiv fdiv.o -lc -I /lib64/ld-linux-aarch64.so.1
执行如下:
$./fdiv
Hey, type a number: 3.5 1.2
I read the number 3.50/ 1.20
Result, Q: 2.92
扩展指令
格式是:kxtw,其中k是我们要扩展的整数类型,w是窄值的宽度。 对于前者,整数的类型可以是U(无符号)或S(有符号,即两个补数)。 对于后者,宽度可以是B,H或W,分别表示字节(寄存器的最低8位),半字(寄存器的最低16位)或字(寄存器的最低32位)。
扩展指令uxtb, sxtb, uxth, sxth, uxtw, sxtw.
Add指令
add x0, x1, w2, sxtw // x0 ← x1 + ExtendSigned32To64 ( w2 )
使用这些扩展运算符时,必须考虑某种上下文。 例如,以下两条指令的含义略有不同:
add x0, x1, w2, sxtb // x0 ← x1 + ExtendSigned8To64 ( w2 )
add w0, w1, w2, sxtb // w0 ← w1 + ExtendSigned8To32 ( w2 )
add x2, x0, x1, sxtw #1 // x2 ← x0 + ( ExtendSigned16To64 ( x1 ) << 1 )
// this sets x2 to 0x2468
add x2, x0, x1, sxtw #2 // x2 ← x0 + ( ExtendSigned16To64 ( x1 ) << 2 )
// this sets x2 to 0x48d0
add x2, x0, x1, sxtw #3 // x2 ← x0 + ( ExtendSigned16To64 ( x1 ) << 3 )
// this sets x2 to 0x91a0
add x2, x0, x1, sxtw #4 // x2 ← x0 + ( ExtendSigned16To64 ( x1 ) << 4 )
// this sets x2 to 0x12340
系统调用
系统调用指令是SWI和SVC.
SWI和SVC是同一件事,只是名称的更改。 以前,SVC指令称为SWI,即软件中断。
这篇关于第9部分- Linux ARM汇编 语法的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23linux 系统宝塔查看网站访问的命令是什么?-icode9专业技术文章分享
- 2024-11-12如何创建可引导的 ESXi USB 安装介质 (macOS, Linux, Windows)
- 2024-11-08linux的 vi编辑器中搜索关键字有哪些常用的命令和技巧?-icode9专业技术文章分享
- 2024-11-08在 Linux 的 vi 或 vim 编辑器中什么命令可以直接跳到文件的结尾?-icode9专业技术文章分享
- 2024-10-22原生鸿蒙操作系统HarmonyOS NEXT(HarmonyOS 5)正式发布
- 2024-10-18操作系统入门教程:新手必看的基本操作指南
- 2024-10-18初学者必看:操作系统入门全攻略
- 2024-10-17操作系统入门教程:轻松掌握操作系统基础知识
- 2024-09-11Linux部署Scrapy学习:入门级指南
- 2024-09-11Linux部署Scrapy:入门级指南