杂项记录 arm64 的一些特性
函数返回值:arm64 规定了整数型返回值放在 x0
寄存器里
sp(栈空间)字节对齐:该临时变量占用 4
字节空间;又因为 arm64 下对于使用 sp
作为地址基址寻址的时候,必须要 16byte-alignment
(对齐),所以申请了 16
字节空间作为临时变量使用。具体参见 这里。
指令模式
Thumbe 或 Arm32 或 Arm64
在一段代码的开头有 .CODE16 CODE32 CODE64
IDA 中按 ALT+G 选择 T (T是虚拟寄存器,当值手动赋值为1表示是 Thumbe 指令解析模式, 0 则是 ARM指令解析模式)
svc 0 和 swi 0 指令
分析 __bionic_clone.S ,在 libc.so 中
arm32:
源码
/bionic/libc/arch-arm/bionic/__bionic_clone.S
48 # Make the system call. 49 ldr r7, =__NR_clone 50 swi #0
/bionic/libc/kernel/uapi/asm-arm/asm/unistd.h
#define __NR_SYSCALL_BASE 0 #define __NR_clone (__NR_SYSCALL_BASE + 120) //120=0x78
指令
/system/lib/libc.so
.text:0001712C LDR R7, =0x78 //0x78=__NR_clone .text:00017130 SVC 0
svc 指令机器码(小端):00 00 00 EF
1110 1111 0000 0000 0000000000000000
根据最后的附录的表的倒数第二个
swi 指令的 0~31位 中的 4~7位为 1111,从机器码角度可以看到 SWI 指令 就是 SVC指令
源码 swi > svc
R7 向内核传递系统调用号
arm64:
源码:
/bionic/libc/arch-arm64/bionic/__bionic_clone.S
37 # Make the system call. 38 mov x8, __NR_clone 39 svc #0
/bionic/libc/kernel/uapi/asm-arm64/asm/unistd.h
#include <asm-generic/unistd.h>
转去看
/bionic/libc/kernel/uapi/asm-generic/unistd.h
#define __NR_clone 220 //220=0xDC
指令
/system/lib64/libc.so
.text:000000000001B9A4 MOV X8, #0xDC //0xDC = __NR_clone
.text:000000000001B9A8 SVC 0
svc 指令机器码(小端):01 00 00 D4
网上没有找到ARM64 的 二进制机器码 格式表格所以,而ARM64的二进制机器码发生了改变。。。。
所以看起来是同一个指令
源码 svc -> svc
x8 用于向内核传递系统调用号
svc的 thumb,arm32,arm64 的格式都不太一样
svc指令官方手册:
Supervisor call to allow application code to call the OS. It generates an exception targeting exception level 1 (EL1). (生成一个异常级别1 的异常)
Supervisor Call causes an exception to be taken to EL1.
Arm®v8架构定义了四个异常级别,从EL0到EL3,其中EL3是最高的异常级别,具有最多的执行特权。在接受异常时,异常级别可以增加或保持不变,而从异常返回时,异常级别可以减少或保持不变。
EL0 Applications. 应用
EL1 OS kernels and associated functions that are typically described as privileged. 系统内核
EL2 Hypervisor. (硬件辅助虚拟化。arm目前采用的是硬件辅助虚拟化的方式,即在处理器模式上增加了el2级别,将hypervisor运行在el2上。将hypervisor与VM kernel运行在不同的模式下,VM运行在一个受控模式,hypervisor可以配置哪些敏感指令是否触发异常。总的来说目的是为了捕获敏感指令以触发异常。 https://blog.csdn.net/zgy666/article/details/79238646)
EL3 Secure monitor.
arm32指令机器码表格:
Thumb SVC 指令
系统调用号如何获取?用哪个寄存器?
官方文档:
问题:
为什么我们看到 arm32下,使用 svc 指令陷入内核调用 __NR_clone 的参数是用 R7 传递 而不是 R0~R3 ? arm官方推荐R0-R3 ,但Linux内核实现用的是R7 ,X8 ?对吗
从 swi 陷入内核看起,调用swi 指令后,cpu会产生一个中断信号,因为是swi指令产生的,所以会将pc赋值为 中断向量表基地址 + 0x8
此处有一条跳转指令
这张表定义在 /arch/arm/kernel/entry-armv.S
...
1131 .equ stubs_offset, __vectors_start + 0x200 - __stubs_start
1133 .globl __vectors_start 1134__vectors_start: 1135 ARM( swi SYS_ERROR0 )
1136 THUMB( svc #0 ) //一个表项32位,一条thumb指令16位,所以第一个表项 用了2个 thumb沾满了32位,第一个指令是 svc 0 ,第二个是 nop,用于cpu复位后调用 1137 THUMB( nop ) 1138 W(b) vector_und + stubs_offset 1139 W(ldr) pc, .LCvswi + stubs_offset //这里是我们关注的 swi 的中断向量跳转 指令 = ldr pc, .LCvswi + stubs_offset 1140 W(b) vector_pabt + stubs_offset 1141 W(b) vector_dabt + stubs_offset 1142 W(b) vector_addrexcptn + stubs_offset 1143 W(b) vector_irq + stubs_offset 1144 W(b) vector_fiq + stubs_offset
1145 1146 .globl __vectors_end 1147__vectors_end:
...
1125.LCvswi: 1126 .word vector_swi
/arch/arm/kernel/entry-common.S
353ENTRY(vector_swi) 354 sub sp, sp, #S_FRAME_SIZE 355 stmia sp, {r0 - r12} @ Calling r0 - r12 356 ARM( add r8, sp, #S_PC ) 357 ARM( stmdb r8, {sp, lr}^ ) @ Calling sp, lr 358 THUMB( mov r8, sp ) 359 THUMB( store_user_sp_lr r8, r10, S_SP ) @ calling sp, lr 360 mrs r8, spsr @ called from non-FIQ mode, so ok. 361 str lr, [sp, #S_PC] @ Save calling PC 362 str r8, [sp, #S_PSR] @ Save CPSR 363 str r0, [sp, #S_OLD_R0] @ Save OLD_R0 364 zero_fp 365 366 /* 367 * Get the system call number. 获取系统调用号 368 */ 369 370#if defined(CONFIG_OABI_COMPAT) 371 372 /* 373 * If we have CONFIG_OABI_COMPAT then we need to look at the swi 374 * value to determine if it is an EABI or an old ABI call. 375 */ 376#ifdef CONFIG_ARM_THUMB 377 tst r8, #PSR_T_BIT 378 movne r10, #0 @ no thumb OABI emulation 379 ldreq r10, [lr, #-4] @ get SWI instruction 380#else 381 ldr r10, [lr, #-4] @ get SWI instruction 382 A710( and ip, r10, #0x0f000000 @ check for SWI ) 383 A710( teq ip, #0x0f000000 ) 384 A710( bne .Larm710bug ) 385#endif 386#ifdef CONFIG_CPU_ENDIAN_BE8 387 rev r10, r10 @ little endian instruction 388#endif 389 390#elif defined(CONFIG_AEABI) 391 392 /* 393 * Pure EABI user space always put syscall number into scno (r7). 394 */ 395 A710( ldr ip, [lr, #-4] @ get SWI instruction ) 396 A710( and ip, ip, #0x0f000000 @ check for SWI ) 397 A710( teq ip, #0x0f000000 ) 398 A710( bne .Larm710bug ) 399 400#elif defined(CONFIG_ARM_THUMB) 401 402 /* Legacy ABI only, possibly thumb mode. */ 403 tst r8, #PSR_T_BIT @ this is SPSR from save_user_regs 404 addne scno, r7, #__NR_SYSCALL_BASE @ put OS number in 405 ldreq scno, [lr, #-4] 406 407#else 408 409 /* Legacy ABI only. */ 410 ldr scno, [lr, #-4] @ get SWI instruction 411 A710( and ip, scno, #0x0f000000 @ check for SWI ) 412 A710( teq ip, #0x0f000000 ) 413 A710( bne .Larm710bug ) 414 415#endif 416 417#ifdef CONFIG_ALIGNMENT_TRAP 418 ldr ip, __cr_alignment 419 ldr ip, [ip] 420 mcr p15, 0, ip, c1, c0 @ update control register 421#endif 422 enable_irq 423 424 get_thread_info tsk 425 adr tbl, sys_call_table @ load syscall table pointer 426 427#if defined(CONFIG_OABI_COMPAT) 428 /* 429 * If the swi argument is zero, this is an EABI call and we do nothing. 430 * 431 * If this is an old ABI call, get the syscall number into scno and 432 * get the old ABI syscall table address. 433 */ 434 bics r10, r10, #0xff000000 435 eorne scno, r10, #__NR_OABI_SYSCALL_BASE 436 ldrne tbl, =sys_oabi_call_table 437#elif !defined(CONFIG_AEABI) 438 bic scno, scno, #0xff000000 @ mask off SWI op-code 439 eor scno, scno, #__NR_SYSCALL_BASE @ check OS number 440#endif 441 442 ldr r10, [tsk, #TI_FLAGS] @ check for syscall tracing 443 stmdb sp!, {r4, r5} @ push fifth and sixth args 444 445#ifdef CONFIG_SECCOMP 446 tst r10, #_TIF_SECCOMP 447 beq 1f 448 mov r0, scno 449 bl __secure_computing 450 add r0, sp, #S_R0 + S_OFF @ pointer to regs 451 ldmia r0, {r0 - r3} @ have to reload r0 - r3 4521: 453#endif 454 455 tst r10, #_TIF_SYSCALL_WORK @ are we tracing syscalls? 456 bne __sys_trace 457 458 cmp scno, #NR_syscalls @ check upper syscall limit 459 adr lr, BSYM(ret_fast_syscall) @ return address 460 ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine 461 462 add r1, sp, #S_OFF 4632: mov why, #0 @ no longer a real syscall 464 cmp scno, #(__ARM_NR_BASE - __NR_SYSCALL_BASE) 465 eor r0, scno, #__NR_SYSCALL_BASE @ put OS number back 466 bcs arm_syscall 467 b sys_ni_syscall @ not private func 468ENDPROC(vector_swi)
if def 的嵌套:
//1.
//现场保护:保存寄存器到内存
//2.
//从swi 指令的立即数处获取系统调用号
#if defined(CONFIG_OABI_COMPAT)
#ifdef CONFIG_ARM_THUMB
#else
#end
#ifdef CONFIG_CPU_ENDIAN_BE8
#end
#elif defined(CONFIG_AEABI)
#elif defined(CONFIG_ARM_THUMB)
#else
//遗留ABI
#end
。。。
//3.
//获取系统调用表基地址 到 tbl(r8)
adr tbl, sys_call_table @ load syscall table pointer
。。。
//4.
//如果 swi 指令的立即数是0,那么表示是EABI调用。
//如果是旧ABI调用,则从scno 获取系统调用号,并获得旧ABI系统调用表的地址
//scno 是什么? r7寄存器别称,纯EABI用户空间总是将syscall号放入scno (r7)。 SystemCallNO
#if defined(CONFIG_OABI_COMPAT)
#elif !defined(CONFIG_AEABI)
#endif
。。。
//5.
ldrcc pc, [tbl, scno, lsl #2] @ call sys_* routine
。。。
.type sys_call_table, #object
ENTRY(sys_call_table)
#include "calls.S"
tbl,scno 等定义在文件
/arch/arm/kernel/entry-header.S
166/* 167 * These are the registers used in the syscall handler, and allow us to 168 * have in theory up to 7 arguments to a function - r0 to r6. 允许携带7个参数给方法 r0~r6 169 * 170 * r7 is reserved for the system call number for thumb mode. r7 是在thumb模式下的保留的系统调用号寄存器 171 * 。。。175 */ 176scno .req r7 @ syscall number 177tbl .req r8 @ syscall table pointer 178why .req r8 @ Linux syscall (!= 0) 179tsk .req r9 @ current thread_info