文章目录
- 现代汇编学习
-
- 汇编语言类型
- 汇编器
-
- 汇编器列表
- 汇编器都干了什么
- 链接器
- ld(链接器)
-
- linker script
- 加载器
- 可执行文件格式
- objconv 工具
- x86汇编
-
- AT&T语法
-
- 函数定义
- AVX,SSE指令
- x64 寄存器
- calling conv
-
- ia32
- 函数调用
-
- stack unwinding
- return value
- nasm
-
- nasm命令
- gas
- Hello world
- armv8汇编
-
- linux 查看CPU信息的寄存器
- 常见的名词
- 寄存器
-
- armv7和aarch32
- aarch64
- calling conv
- 寻址方式
- arm嵌入汇编
-
- 加法例子
- 系统调用
- 库
-
- x86 再来一遍calling convention
- Linux 常识
本篇文章介绍汇编,链接等常识,同时介绍了x86-64和armv8-a的一些指令语法
现代汇编学习
之前学的
80x86
汇编太古老了,甚至连第一版
linux kernel
代码都看不懂,现在整理一下一些汇编知识,主要是针对
x86
和
armv8-a
架构的,但是本文并不会教具体的指令长什么样,也不会阐述怎么写一个通用汇编代码。学习汇编本身不是我们的终极目的。我们的目的是为了更好开发软件或者高性能库,因此会介绍一下编译,链接的原理和流程。
汇编语言类型
- AT&T
- 是这个实验室提出的一种语法,注意它和指令集是没关系的,仅仅是一种语法而已。
- 对于x86处理器,立即数由
,寄存器由$
引用。%
- 对于arm处理器,直接使用arm官方的语言格式。
- intel
- 本科会学到的一种语言格式,x86汇编
- 简单,易用
- 汇编器的作用是把汇编语言翻译成机器语言,所以不同的汇编语言可能会使用不同的汇编器。他们的机器语言即指令。
- GAS 汇编器可以汇编x86的汇编语言,也可以汇编arm。
- Intel assembler 只能汇编 intel的汇编语言,就像icc。
汇编器
汇编器列表
- AT&T assembler - as
- Borland’s Turbo Assembler - TASM
- GNU assembler - gas,GCC默认使用这个
- Intel Assembler
- Microsoft Assembler - MASM
- Netwide Assembler - NASM
- Yet Another Assembler - YASM
x86 gas语法
汇编器简短比较
汇编器都干了什么
- 汇编器负责把当前源文件的所有变量,常量,宏,
解析等等并且生成相应的符号表(这时候的解析是初步的,符号表是不完整的,后续工作链接器会接手),所谓解析就是确定他们的地址。这些地址都是基于本模块计算出来的相对地址,绝对地址的计算需要到链接阶段才能做。label
- 汇编器的输出是目标文件
Assembling:
- Assembling converts source program into object program if syntactically correct and
generates an intermediate .obj file or module.
- It calculates the offset address for every data item in data segment and every
instruction in code segment.
- A header is created which contains the incomplete address in front of the generated obj
module during the assembling.
- Assembler complains about the syntax error if any and does not generate the object
module.
- Assembler creates .obj .lst and .crf files and last two are optional files that can be
created at run time.
- For short programs, assembling can be done manually where the programmer translates
each mnemonic into the machine language using lookup table.
- Assembler reads each assembly instruction of a program as ASCII character and
translates them into respective machine code.
- 符号表实际上就是用于链接器定位每个目标文件的变量和函数信息比如汇编中重要的三个节
.bss .data .text
- 在汇编语言源代码可以调用外部文件的函数,就像C语言调用库函数一样。汇编器对这些函数或者变量无法进行地址计算。这些活由链接器干。
-
c 语言中 加了static的函数不会出现在 global section
- 目标文件的三种类型(来自CSAPP:chapter 7)
- Relocatable object file. Contains binary code and data in a form that can be combined with other relocatable object files at compile time to create an executable object file.
- Executable object file. Contains binary code and data in a form that can be copied directly into memory and executed.
- Shared object file. A special type of relocatable object file that can be loaded into memory and linked dynamically, at either load time or run time.
-
工具可以查看某个目标文件的符号(nm
都可以看符号表)readelf和objdump
-
nm -gD yourLib.so objdump -TC 也可以(C用于c++) readelf -Ws
-
- 一个简单的例子:
...
int x = 0;
int y = -1;
y = x;
...
上面这个片段定义了两个全局标号
x y
,这两个标号实际上是一个地址,但是我们在C语言引用的时候实际上是引用值而不是地址。只有
y = &x
这个语句的含义才是将
x
这个标号的值(即
x
的地址)赋给
y
y = x
这个语句实际上会被编译成
mov eax,[x]
mov dword[y],eax
链接器
- 链接器负责进一步解析所有的符号,函数名,(根据每个目标文件的符号表)把所有的目标文件链接成一个最终的可执行文件。链接器收集所有目标文件的符号表信息,合并同类的段(比如不同目标文件的代码段,数据段)等工作。最终可执行文件的符号表是完整的。
- 比如两个.o文件都定义了函数
,这时候链接器需要判断哪个是需要被采用的void foo()
- 在.o文件中没有在
中的函数是无法被其他.o文件调用的,实际上在汇编阶段不会出现在符号表,因此链接器也无法使用。(这些函数就是在global
中加了c
修饰符的变量)static
- 比如两个.o文件都定义了函数
This involves the converting of .OBJ module into .EXE(executable) module i.e.
executable machine code.
- It completes the address left by the assembler.
Microprocessors lecture 5: Programming with 8086 Microprocessor
- It combines separately assembled object files.
- Linking creates .EXE, .LIB, .MAP files among which last two are optional files.
链接器负责把全部的符号解析成最终的地址。它负责解析所有的符号。
-
即一个链接器ld
- 命令
很有用readelf
- 链接
-
static linking
现代汇编语言现代汇编学习objconv 工具x86汇编nasmarmv8汇编系统调用库Linux 常识 -
dynamic linking
-
runtime linking
参考1
quaro参考
-
- 更详细的参考另外的md文件
ld(链接器)
- 如果希望兼容32位的elf输出,这里需要使用
选项-m
ld -m elf_i386 -s -o file file.o
linker script
链接脚本是链接器使用的。脚本中会指定某些默认行为,比默认链接的一些标准库,比如可执行文件的入口地址。
加载器
- loader通常由shell进程负责唤起,它将可执行文件的代码加载到内存。
It Loads the program in memory for execution.
- It resolves remaining address.
- This process creates the program segment prefix (PSP) before loading.
- It executes to generate the result.
可执行文件格式
- 常用的有ELF:executable and linkable format
bin flat-form binary files (e.g. DOS .COM, .SYS) ith Intel hex srec Motorola S-records aout Linux a.out object files aoutb NetBSD/FreeBSD a.out object files coff COFF (i386) object files (e.g. DJGPP for DOS) elf32 ELF32 (i386) object files (e.g. Linux) elf64 ELF64 (x86_64) object files (e.g. Linux) elfx32 ELFX32 (x86_64) object files (e.g. Linux) as86 Linux as86 (bin86 version 0.3) object files obj MS-DOS 16-bit/32-bit OMF object files win32 Microsoft Win32 (i386) object files win64 Microsoft Win64 (x86-64) object files rdf Relocatable Dynamic Object File Format v2.0 ieee IEEE-695 (LADsoft variant) object file format macho32 NeXTstep/OpenStep/Rhapsody/Darwin/MacOS X (i386) object files macho64 NeXTstep/OpenStep/Rhapsody/Darwin/MacOS X (x86_64) object files dbg Trace of all info passed to output stage elf ELF (short name for ELF32) macho MACHO (short name for MACHO32) win WIN (short name for WIN32)
objconv 工具
-
把目标文件反汇编成指定格式的汇编语言objconv -fnasm main.o
x86汇编
-
,把符号设为具体的值.set symbol,expression
AT&T语法
- 指令可能会加后缀表示操作数的宽度
- “byte” refers to a one-byte integer (suffix b)
- “word” refers to a two-byte integer (suffix w),
- “doubleword” refers to a four-byte integer (suffix l), and
- “quadword” refers to an eight-byte value (suffix q).
- MOV:
.(32,16,8,64)一般如果没有后缀那就是默认的长度。movl,movw,movb,movq
mov 基本指令
如果用到扩展,mov还会有s(signed),z(zero)的后缀,
movzbl %al,%ebx 表示对al寄存器(8-bit)进行zero扩展放到ebx(64-bit)
mov 内存寻址方式segment:displacement(base register, index register, scale factor)
即segment:[base register + displacement + index register * scale factor]
movzwl (%rdx,%rax,2),%edx
函数定义
-
表示将符号start定义成一个函数标号.type start, @function
AVX,SSE指令
指令参考1
x64 寄存器
- There are sixteen 64-bit registers in x86-64: %rax, %rbx, %rcx, %rdx, %rdi, %rsi, %rbp, %rsp, and %r8-r15. Of these, %rax, %rcx, %rdx, %rdi, %rsi, %rsp, and %r8-r11 are considered caller-save registers, meaning that they are not necessarily saved across function calls. By convention, %rax is used to store a function’s return value, if it exists and is no more than 64 bits long. (Larger return types like structs are returned using the stack.) Registers %rbx, %rbp, and %r12-r15 are callee-save registers, meaning that they are saved across function calls.Register**%rsp **is used as the stackpointer, a pointer to the top most element in the stack.
calling conv
- X64 和 ia32的参数传递有所不同,后者使用栈(系统调用会使用寄存器),前者会使用寄存器以及栈
- windows和linux参数传递也不同
###x64
- linux和OS X: %rdi, %rsi, %rdx, %rcx, %r8, and %r9 are used to pass the first six integer or pointer parameters to called functions. Additional parameters (or large parameters such as structs passed by value) are passed on the stack.
;Example function call: extern putchar mov rdi,'H' ; function parameter: one char to print call putchar
- windows则不同
- Win64 function parameters go in registers rcx, rdx, r8, and r9.
- Win64 functions assume you’ve allocated 32 bytes of stack space to store the four parameter registers, plus another 8 bytes to align the stack to a 16-byte boundary.
sub rsp,32+8; parameter area, and stack alignment extern putchar mov rcx,'H' ; function parameter: one char to print call putchar add rsp,32+8 ; clean up stack
- Win64 treats the registers rdi and rsi as preserved.
ia32
- 对于常见的C++程序,像缺省_cdecl或使用_stdcall的函数压栈顺序都是采用的从右往左压栈的
void fun(int a, int b) 则b先入栈,a后入栈
-
In 32-bit x86, the base pointer (formerly %ebp, now %rbp) was used to keep track of the base of the current stack frame, and a called function would save the base pointer of its caller prior to updating the base pointer to its own stack frame. With the advent of the 64-bit architecture, this has been mostly eliminated, save for a few special cases when the compiler cannot determine ahead of time how much stack space needs to be allocated for a particular function (see Dynamic stack allocation).
in 32 bit mode, parameters are passed by pushing them onto the stack in reverse order, so the function’s first parameter is on top of the stack before making the call. In 32-bit mode Windows and OS X compilers also seem to add an underscore before the name of a user-defined function, so if you call a function foo from C/C++, you need to define it in assembly as “_foo”.
- “Scratch” registers you’re allowed to overwrite and use for anything you want,“Preserved” registers serve some important purpose somewhere else, so you have to put them back (“save” the register) if you use them.
函数调用
- 注意参数的压栈动作是在caller 程序里面的,这些都是一些convention,不是硬件实现的,是由软件 即程序员实现的。
- 发生函数调用时,caller按照convention, 将
(即scratch register)压入栈,然后负责压参数 , 调用call指令,call指令会自动push返回地址。jmp到callee内部,第一件事是保存ebp到栈顶, 然后callee负责将caller saved
寄存器压入栈。退出的时候是调用者负责清理参数callee saved
一个栈帧看起来应当是
当然,在
x64
里,可能没有参数入栈。如果使用寄存器传参,在子程序内应当将这些寄存器的值先入栈,否则后面没法使用这些值
下图展示
armv8
函数的参数入栈顺序
void test_para(int a0, int a1, int a2, int a3, int a4,int a5,int a6, int a7,int a8,int a9,int a10,int a11, a12);
//调用
test_para(0,1,2,3,4,5,6,7,8,9,10,11,12);
stack unwinding
stkoverflow
return value
- x86的返回值通常使用
RDX:RAX
c struct返回值
nasm
- nasm由几部分构成(layout)
- 指令(机器语言助记符)
- 伪指令:
-
,DB
,DW
,DD
,DQ
,DT
,DO
andDY
,DZ
-
,RESB
,RESW
,RESD
,RESQ
,REST
,RESO
andRESY
RESZ
-
: 包含一个二进制可执行文件INCBIN
incbin "file.dat" ; include the whole file incbin "file.dat",1024 ; skip the first 1024 bytes incbin "file.dat",1024,512 ; skip the first 1024, and actually include at most 512
-
: 定义常量。注意EQU
实际上等价于C中的宏,在指令中可以认为是立即数,经过汇编后就没有了(被数值替代),而equ
是在内存中分配了一个字节,在指令中认为是一个地址,也就是在执行过程中使用地址进行访问。下列定义中message实际上是一个地址(指针)db
message db 'hello, world' msglen equ $-message
-
:用于重复指令或者数据TIMES
zerobuf: times 64 db 0 ;定义64个byte 全都等于0 buffer: db 'hello, world' times 64-$+buffer db ' ' ;;;实际上buffer和64-$+buffer都只是地址,标号之间可以做数学运算, ;;;times有点像是循环这里因为times伪指令$ ;;;这里在buffer后面生命了64个byte ;;;等价于64条语句 db ' ' times 100 resb 1;;;timesk可以搭配指令,效果等价于resb 100,实际上它是重复执行100次指令resb 1
-
- Nasm的特点
- 以点
开头的标号是本地标号leading dot is NASM’s syntax for making local labels.
- 寻址方式写成
在gcc下是mov [ebx + eax * 4 + 1],1
mov 1,1(ebx,eax,4)
- 以点
- nasm对于x86几乎是原生格式(intel syntax)
- 汇编语句(layout):
-
,其中[label] mnemonic [operands] [;comment]
包含两部分,assembler directives and instructionsmnemonic
- 下图是64-bit的代码划分
现代汇编语言现代汇编学习objconv 工具x86汇编nasmarmv8汇编系统调用库Linux 常识 -
- section定义 以
开头的section是预定义好的,其属性是已经定义好的。.
- 数据定义:这部分出现在
或者.bss
节data
-
则定义了一个字符串,在指令中就可以使用message: db "Hello, World", 10
作为操作数了messege
- 可以在
节预留空间不初始化使用.bss
指令res
buffer: resb 64 ; reserve 64 bytes wordvar: resw 1 ; reserve a word realarray: resq 10 ; array of ten reals
-
db 0x55 ; just the byte 0x55
db 0x55,0x56,0x57 ; three bytes in succession
db 'a',0x55 ; character constants are OK
db 'hello',13,10,'$' ; so are string constants
dw 0x1234 ; 0x34 0x12
dw 'a' ; 0x61 0x00 (it's just a number)
dw 'ab' ; 0x61 0x62 (character constant)
dw 'abc' ; 0x61 0x62 0x63 0x00 (string)
dd 0x12345678 ; 0x78 0x56 0x34 0x12
dd 1.234567e20 ; floating-point constant
dq 0x123456789abcdef0 ; eight byte constant
dq 1.234567e20 ; double-precision float
dt 1.234567e20 ; extended-precision float
- 常数写法
后缀
H
or
X
,
D
or
T
,
Q
or
O
, and
B
or
Y
分别是 hexadecimal, decimal, octal and binary respectively,
16进制前缀
0x
or
$0
mov ax,200 ; decimal
mov ax,0200 ; still decimal
mov ax,0200d ; explicitly decimal
mov ax,0d200 ; also decimal
mov ax,0c8h ; hex
mov ax,$0c8 ; hex again: the 0 is required
mov ax,0xc8 ; hex yet again
mov ax,0hc8 ; still hex
mov ax,310q ; octal
mov ax,310o ; octal again
mov ax,0o310 ; octal yet again
mov ax,0q310 ; octal yet again
mov ax,11001000b ; binary
mov ax,1100_1000b ; same binary constant
mov ax,1100_1000y ; same binary constant once more
mov ax,0b1100_1000 ; same binary constant yet again
mov ax,0y1100_1000 ; same binary constant yet again
nasm命令
-
查看汇编器的支持的输出文件格式nasm -hf
- 汇编时,使用
指定输出文件格式-f
gas
- gas directives
64bit很好的学习网站
Hello world
- 32-bit
section .text
global _start
;;; write(1, msg, len);
;;; write的系统调用号是4
;;; 参数是edx,ecx,ebx
;;; eax存放系统调用号
_start :
mov eax,4
mov edx,len
mov ecx,msg
mov ebx,1
int 0x80
mov eax,1
int 0x80
section .rodata
msg db 'Hello world',0xa;
len equ $ - msg ; msg的长度
nasm -f elf32 hello.as,m -o hello.o && ld -m elf_i386 -o hello
- 64-bit
global _start
section .text
_start: mov rax, 1 ; system call for write
mov rdi, 1 ; file handle 1 is stdout
mov rsi, message ; address of string to output
mov rdx, 13 ; number of bytes
syscall ; invoke operating system to do the write
mov rax, 60 ; system call for exit
xor rdi, rdi ; exit code 0
syscall ; invoke operating system to exit
section .data
message: db "Hello, World", 10 ; note the newline at the end
😜
armv8汇编
linux 查看CPU信息的寄存器
Vendor Name | Vendor ID |
---|---|
ARM | 0x41 |
Broadcom | 0x42 |
Cavium | 0x43 |
DigitalEquipment | 0x44 |
HiSilicon | 0x48 |
Infineon | 0x49 |
Freescale | 0x4D |
NVIDIA | 0x4E |
APM | 0x50 |
Qualcomm | 0x51 |
Marvell | 0x56 |
Intel | 0x69 |
信息存储在寄存器MIDR_EL1
其中从低至高第0-3 bit表示revision,代表固件版本的小版本号,如r1p3中的p3;
第4-15 bit表示part number(id),代表这款CPU在所在vendor产品中定义的产品代码,如在HiSilicon产品中,part_id=0xd01代表Kunpeng-920芯片;
第16-19 bit表示architecture,即架构版本,0x8即ARMv8;
第20-23 bit表示variant,即固件版本的大版本号,如r1p3中的r1;
第24-31 bit表示implementer,即vendor id,如vendor_id=0x48表示HiSilicon
文件夹/sys/devices/system/cpu里面有详细的每个核的信息
寄存器的值如果是
0x00000000481fd010 海思供应商
- arm一个字是32位(single),intel的一个字是16位(word)
- 字节(B),半字(H),字(S),双字(D)。
- armv8支持64位的指令(A64),同时支持aarch64和aarch32两种执行状态
-
aarch64是armv8的一种执行状态,aarch32是为了兼容armv7,它是32位指令(A32)的超集。
armv8-a中的aarch32。从aarch32的寄存器和aarch64之间的寄存器必须有一个映射关系,就像写ia32和
X86_64的
,ax
,rax
一样(不同模式不同的映射关系)eax
- 无论aarch32以及aarch64,实际上都是在说armv8-a体系结构
- 问题来了:一个处理器怎么决定自己是aarch64还是aarch32
AArch64 or ARM64 is the 64-bit extension of the ARM architecture.
It was first introduced with the ARMv8-A architecture.
常见的名词
ARM:
- AArch64: AArch64 is the 64-bit execution state of the ARMv8 ISA, A machine in this state executes operates on the A64 instruction set
- AArch32:32位的运行模式,是ARMv8-a向前兼容的。也就是ARMv8-a有两种执行模式
- A64:指令集
- A32:32位指令集
- ARMv8:体系结构。通常armv8指的是armv8-a.Cortex-A32 是 32-bit ARMv8-A 的CPU,大部分 ARMv8-A都是支持64-bit的。除了armv8-a,实际上还有armv8-r系列,不过这些都是32位的。
- ISA:In computer science, an instruction set architecture (ISA) is an abstract model of a computer. It is also referred to as architecture or computer architecture. A realization of an ISA, such as a central processing unit (CPU), is called an implementation
- 微体系架构(microarchitecture)是ISA的一个实现
- 比方说物理寄存器有多少个,几发射,cache 一致性协议是啥,保留站项数等等
寄存器
armv7和aarch32
- 16个32位通用寄存器 (R0-R15).只用r0-13能使用
- r14是返回地址,别名
,link register(lr)
- r15是pc,可以使用
用来子程序返回mov pc,lr
- Arm v7是没有sp寄存器的,使用r13代替
- r14是返回地址,别名
- 向量寄存器:32 个 64-bit 的 (D0-D31),或者16个128位的(Q0-Q15).
现代汇编语言现代汇编学习objconv 工具x86汇编nasmarmv8汇编系统调用库Linux 常识
aarch64
- 31 个 64-bit 通用寄存器(X0-X30) 和1个 特殊的寄存器.也可以使用32位模式去访问(W0-W30)
- x29是frame pointer(在x86里是EBP)
- x30是LR
- 和armv7不一样,aarch64有专门的pc寄存器且不允许直接访问pc
- 32 个 128-bit 向量寄存器 (V0-V31). These registers can also be viewed as 32-bit Sn registers or 64-bit Dn registers.
- aarch64通用寄存器列表
Register Role Requirement X0 - X7 Parameter/result registers Can Corrupt X8 Indirect result location register X9 - X15 Temporary registers X16 - X17 Intra-procedure call temporary X18 Platform register, otherwise temporary X19 - X29 Callee-saved register Must preserve X30 Link Register(函数调用的返回地址) Can Corrupt - 通用寄存器映射(mode表示不同的异常状态)
calling conv
arm calling convention
- armv8的参数寄存器是
,x64的参数寄存器是r0-r7
额外的参数都是在栈上(从右边开始压)rdi,rsi,rdx,rcx,r8,r9
- x8是用来接收返回值的(存放返回地址), 相比之下x86-64用于存放返回值地址的是
寄存器rax
寻址方式
arm寻址方式,32位通用寄存器是是R,64是X
其中
base
是寄存器或者是
SP
- 注意:sp必须是16字节对齐的
- arm没有push,pop指令,全都post/pre-index的str和ld
- 从内存加载的数据的宽度由指令后缀指定
-
signed byte extendldrsb =>
-
- 加载到寄存器的宽度由寄存器的名字决定
-
目的寄存器是32位,从内存加载一个byte,高位使用signed extend扩充LDRSB W4, <addr>
-
uxtb, sxtb, uxth, sxth, uxtw, sxtw
Extending operators main purpose is to widen a narrower value found in a register to match the number of bits for the operation. An extending operator is of the form kxtw, where k is the kind of integer we want to widen and w is the width of the narrow value. For the former, the kind of integer can be U (unsigned) or S (signed, i.e. two’s complement). For the latter the width can be B, H or W which means respectively byte (least 8 significant bits of the register), half-word (least 16 significant bits of the register) or word (least significant 32 bits of the register).
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)
In both cases the least significant 8 bits of w2 are extended but in the first case they are extended to 64 bit and in the second case to 32-bit. Extension and shift
It is possible to extend a value and then shift it left 1, 2, 3 or 4 bits by specifying an amount after the extension operator. For instance
mov x0, #0 // x0 ← 0 mov x1, #0x1234 // x0 ← 0x1234
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
This may seem a bit odd and arbitrary at this point but in later chapters we will see that this is actually useful in many cases.
This is all for today.
反汇编一个例子
- nop的机器码
- 上述机器是小端,即高字节在高地址。2d8是地址从左到右依次递减,即
是从高地址开始的。d503201f
- 这个反汇编结果和x86的不太一样。。。x86的 arm的
现代汇编语言现代汇编学习objconv 工具x86汇编nasmarmv8汇编系统调用库Linux 常识
arm嵌入汇编
cookbook
- 语法
其中code_template一条指令用引号括起来的asm(code_template :output_operand_list :input_operand_list :clobbered_register_list)
asm ( "TST LR, #0x40\n\t" "BEQ from_nonsecure\n\t" "from_secure:\n\t" "TST LR, #0x04\n\t" "ITE EQ\n\t" "MRSEQ R0, MSP\n\t" "MRSNE R0, PSP\n\t" "B hard_fault_handler_c\n\t" "from_nonsecure:\n\t" "MRS R0, CONTROL_NS\n\t" "TST R0, #2\n\t" "ITE EQ\n\t" "MRSEQ R0, MSP_NS\n\t" "MRSNE R0, PSP_NS\n\t" "B hard_fault_handler_c\n\t" );
加法例子
#include <stdio.h>
int add(int i, int j)
{
int res = 0;
__asm ("ADD %[result], %[input_i], %[input_j]"
: [result] "=r" (res)
: [input_i] "r" (i), [input_j] "r" (j)
);
return res;
}
int main(void)
{
int a = 1;
int b = 2;
int c = 0;
c = add(a,b);
printf("Result of %d + %d = %d\n", a, b, c);
}
- 使用通用寄存器做整数加减法
系统调用
- 系统调用号:the syscall call numbers for the 32-bit ABI are in
(same contents in/usr/include/i386-linux-gnu/asm/unistd_32.h
)./usr/include/x86_64-linux-gnu/asm/unistd_32.h
- 注意系统调用怎么传参数的。(ia32和x64不太一样)
- 32位(i386)
-
系统调用号eax
-
系统调用参数ebx ecx...
-
存放结果eax
- There are six registers that stores the arguments of the system call used. These are the EBX, ECX, EDX, ESI, EDI, and EBP. These registers take the consecutive arguments, starting with the EBX register. If there are more than six arguments then the memory location of the first argument is stored in the EBX register.
-
- 32位(i386)
-
用来放系统调用号eax
-
命令可以用来跟踪执行的系统调用strace
-
的系统调用使用指令x64
而不是syscall
int 0x86
库
- 有两种库,静态库和动态库。静态库和动态库都是一些目标文件(即经过汇编后的文件)
-
静态库实际上就是链接时候把这个库全部代码集成到调用库的这个主文件。
静态库自然是静态链接的(实际上运行时候我们不再依赖于那个库文件,整个可执行文件已经包含了全部的库代码)
- 动态库是位置无关代码。在链接时链接器不集成这些代码,只是在符号表中记录调用的函数在哪个库里面有。运行时再从这个符号表里面去找这个函数,到时候再转移即可。这有点像系统调用。
-
- 名词解释
-
:dynamic shared object/dynamic link libraryDSO/DLL
-
- 可能用到的工具:
nm ldd objconv readelf
- 参考
x86 再来一遍calling convention
和系统调用相关调用形式:Interfacing with operating system libraries requires knowing how to pass parameters and manage the stack. These details on a platform are called a calling convention.
- Caller-saved 意思是被调程序允许随意使用这些寄存器,如果主程序在调用之前在使用这些寄存器,调用之后想继续使用,那主程序需要自己保存这些寄存器。这些寄存器就是普通的寄存器而已,保存与否取决于主程序的意愿(显然这是由caller保存)。
- Callee-saved意思需要跨越调用保存的寄存器的值,比如常见的
这些寄存器是有其他用途的,随时都必须保持在当前上下文时处于正确的值。这类寄存器必须要在使用之间保存(显然这是callee要使用的,应当由callee保存)rsp ,rbp
Caller-saved registers (AKA volatile registers, or call-clobbered) are used to hold temporary quantities that need not be preserved across calls.
For that reason, it is the caller’s responsibility to push these registers onto the stack or copy them somewhere else if it wants to restore this value after a procedure call.
It’s normal to let a
call
destroy temporary values in these registers, though.
Callee-saved registers (AKA non-volatile registers, or call-preserved) are used to hold long-lived values that should be preserved across calls.
- microsoft x64 calling convention
- wikipedia x86 calling convention
- agner pdf
Linux 常识
- Linux通常指的是linus Torvalds写的kernel,一般的linux os指的是linux distribution,比如ubuntu, debian,fedora。而GNU实际上是 richard stallman给他的project起的名字,做了很多os的软件(比如
),但是没有kernel。可以粗糙理解gcc
linux os = linux kernel + gnu软件
- System V 是at&t 开发的, 基于unix
- BSD是
不仅是kernel,而且是整个os。Berkeley Software Distribution
- FreeBSD: FreeBSD is the most popular BSD, aiming for high performance and ease of use. It works well on standard Intel and AMD 32-bit and 64-bit processors.
- NetBSD: NetBSD is designed to run on almost anything and supports many more architectures. The motto on their homepage is, “Of course it runs NetBSD.”
- OpenBSD: OpenBSD is designed for maximum security — not just with its features, but with its implementation practices. It’s designed to be an operating system banks and other serious institutions would use for critical systems.
- DragonFly BSD: DragonFly BSD was created with the design goal of providing an operating system that would run well in multithreaded environments — for example, in clusters of multiple computers.
- Darwin / Mac OS X: Mac OS X is actually based on the Darwin operating system, which is based on BSD. It’s a bit different from other BSDs. While the low-level kernel and other software is open-source BSD code, most of the rest of the operating system is closed-source Mac OS code. Apple built Mac OS X and iOS on top of BSD so they wouldn’t have to write the low-level operating system themselves, just as Google built Android on top of Linux
- 所以可以看到,MacOS实际上是基于BSD的,因此常用的软件
等和Linux os都有所区别编译器 sed