An example: GCC generated ARM assembly

align:机器码opcode缩进(align)你会了吗?

  • align:带有一个数字,规定了没条指令或数据的对应的opcode(机器码)的长度
    对于数据: 多余的部分补充为NULLs
    对于指令: 用NULL指令或者(等效如 MOV EAX,EAX )
    数据段
.DATA
var2 BYTE  02; 变量长度为1
ALIGN 4
var2 BYTE  02; 变量长度为1
;对应的的编译器生成代码如下:
.DATA
;------------分割线------------------------------
.data:00402000 var1            db 1
.data:00402001                 db    0; 这个NULL是用来强制下一条指令的机器代码长度为4的倍数
.data:00402002                 db    0; 这个NULL是用来强制下一条指令的机器代码长度为4的倍数
.data:00402003                 db    0; 这个NULL是用来强制下一条指令的机器代码长度为4的倍数
.data:00402004 var2            db 2 

代码段

.CODE
;强制第一条指令的机器码长度为4的倍数
ALIGN 4
;入口点
EntryPoint:
MOVZX EAX, var1 
MOVZX EAX, var2 
MOVZX EAX, var3 
;对应的的编译器生成代码如下:
.CODE
;------------分割线------------------------------
.text:00401000 start:
;不能强制缩进入口点的代码对应的机器码长度
.text:00401000                 movzx   eax, var1
.text:00401007                 movzx   eax, var2
.text:0040100E                 movzx   eax, var3
;下面的指令是七字节指令,强制第三条指令的机器码长度为4的倍数
MOVZX EAX, var1 
MOVZX EAX, var2 
ALIGN 4 
MOVZX EAX, var3 
;这个指令的是1字节长度--opcode(cc)
;强制将其机器码缩进成2字节的倍数
ALIGN 2
INT 3
;------------分割线------------------------------
.text:00401015                 movzx   eax, var1
.text:0040101C                 movzx   eax, var2
.text:00401023                 nop; 这个NOP的填充用来强制第三条指令的机器码为4的倍数
.text:00401024                 movzx   eax, var3
.text:00401043                 nop; 这个NOP的填充用来强制第三条指令的机器码为2的倍数
.text:00401044                 int     3              ; Trap to Debugger
.text:00401044; --------------------------------------------------------------------------
  • 以句号(period)开头的是汇编程序的提示符(directives)

表示一个循环LOOP : .L4:
下面这两条指令声明下面的函数使用16位的thumb指令集
.code 16
.thumb_func

  • 函数指令

函数调用:bl
从函数中返回:bx
函数调用使用的参数存放在r0-r3中,如果超过三个参数,使用堆栈存放参数,并且返回值存放在r0
函数调用完后,r0-r3(r12)被清除,所以想要使用函数调用完得到的值,是需要手动将其保存下来的
r4-r15:在函数调用时被push到栈中

https://stackoverflow.com/questions/11277652/what-is-the-meaning-of-align-an-the-start-of-a-section

one example:使用GCC来生成汇编程序

使用参数-S或者-save-temps来捕获

指令(instruction)解释说明
ldr 字数据加载指令 ldr 目的寄存器,源,将源(内存中的数据)存进目的寄存器中
str 字数据存储指令str {条件} 源寄存器,<存储器地址>,将源寄存器中的数据存入内存地址
小知识
r0arm的寄存器,寄存器中可以存储数据,并且r0就可以看成存储的数据
r11(FP)可看做intel架构的EBP,指向栈帧的底部,是一个固定值
r13(SP)栈指针,可看做ESP,动态的,随着栈的pop,push而改变
[r0]r0存储的数据作为内存地址,[r0]表示这个内存地址
LDR例子
ldr r2,[r0]将[r0]这个数据存进r2这个寄存器内
LDR R1,=0xE0000000R1=0xE0000000,立即数赋值,将立即数存进R1寄存器中
LDR R1,0xE0000000将内存地址为0xE0000000所指向的数据存入寄存器R1
STR例子
STR R1,[R2]将寄存器R1中存储的数据存入,内存地址为R2(寄存器R2存储的值为内存地址)的区域

小结:mov指令是寄存器之间的数据操作,STR和LDR是寄存器和内存地址之间的操作

非常简单的一个小函数
func1.cpp(C++)+ARM gcc 5.4(linux)

//从参数num2中得到数据,然后乘以240赋值给参数num1,然后返回参数num1
//实际上参数num1的内容并没有使用,但是就这样分析吧,懒得更改了
int main(int num1, int num2) {
    num1 = num2 * 240;
    return num1;
}
;------------分割线------------------------------
main:
        @@ int main(int num1, int num2) {
        str     fp, [sp, #-4]!   //push {fp},sp-4=fp,保存fp的值(感叹号:先修改寄存器的值再使用)
        add     fp, sp, #0       //fp=sp,将fp指向sp位置
        sub     sp, sp, #12      //sp=sp-12,栈指针下移,存储变量
        str     r0, [fp, #-8]    //引入参数数据(r0),将数据赋值给变量num1(栈地址:fp-8)
        str     r1, [fp, #-12]   //变量num2,栈地址:fp-12

        @@ num1 = num2 * 240;
        @@ 基数左移4位即乘以16,减一个基数即乘以15,在左移4(乘以16),总计乘以15*16=240
        ldr     r2, [fp, #-12]   //r2 = num2
        mov     r3, r2           //r3=r2=num2
        mov     r3, r3, asl #4   //r3左移4位,赋值给r3(r3 = r3*16 = num2*16)
        rsb     r3, r2, r3       //r3 = r3-r2,反向减法(r3=r3*16-r3=num2*15)
        mov     r3, r3, asl #4   //r3 =num2 *15 *16
        str     r3, [fp, #-8]    //num1 = r3=num2*240

        @@ return num1;
        ldr     r3, [fp, #-8]    

        @@ }
        mov     r0, r3           //r0中也存有返回值
        sub     sp, fp, #0
        ldr     fp, [sp], #4     //pop {fp}, post-indexed后变,将内存地址的数据赋给fp后,再sp=sp+4
        bx      lr               //返回到下一条指令,LinkPointer存有下一条指令的内存地址

基础功能 Basic Features

  • 寄存器

ARM有1632位的寄存器r0-r15

stdgccarm描述
r0-r3r0-r3a1-a3函数参数的存放,暂存寄存器scratch registers
r12ipIP和r0-r4同类
r4-r8r4-r8v1-v5variable函数调用过程中,存储变量的寄存器
r9r9v6/SB特殊平台/variable
r10slv7variable
r11fpv8varible/帧指针,用于存储栈底地址(EBP)
r13spSP栈指针,指向栈顶
r14lrLR链接寄存器,存储下一条指令的内存地址
r15pcPC程序计数器,随着指令的执行而增加;当执行到子函数时,用来存储当前指令的地址
  • 指令分类
    1、数据操作,算术和位操作
    2、内存操作,load或者store
    3、分支操作,条件跳转
每个ARM的指令都可以有条件的执行
  • 不带条件的指令,不管是否执行都必须内存中读取
@@ r2 = r0>=r1 ? r0:r1;
@@ 如果r0 >= r1,就将r0赋值给r2

    @@ 传统汇编
    cmp     r0, r1      //比较r0,r1
    blt .Lbmax          //r0<r1 brance less than小于就跳转到.Lbmax
    mov     r2, r0      //r2 = r0
    b       .Lrest
.Lbmax:
    mov     r2, r1
.Lrest

带有条件的汇编,指令简便、就会快捷

cmp     r0, r1
movge   r2, r1    //大于等于则执行
movlt   r2, r0    //less than小于就执行
指令解释
sub不设置状态寄存器
subs设置状态寄存器
  • 桶型位移器(Barrel Shifter)
    内嵌的ARM核心组成之一,所以比任何算数运算都快
    任何指令都可以再没有消耗的情况使用桶型位移器移动到其中一个操作数
桶型位移操作注释
lsllogic left shift逻辑左移(同asl算数左移)
lsrlogic right shift逻辑右移
asrarithmetic right shift算数右移
rorrotate right循环右移

逻辑右移和算数右移的区别在于signed/unsigned有符号/无符号详情查看bit ops section

移位寄存器可以用来操作Op2这个操作数

@@r0= r1+(r1<<3) = r1 + r1*8
add    r0, r1, r1, lsl #3   

受到限制的立即数

op{cond}{s} Rd, Rn, Op2
  • ARM每条指令32位长度,4位条件代码、4位目的操作数、4位的第一操作寄存器,最后只剩下12位留给了立即数
  • 12位立即数:8位数字(n),4位循环移位字段(r)
立即数 = n ror 2*r

数据指令

  • 只能再寄存器中,不能直接在内存变量中
  • 大多数据指令是有一个目的寄存器Rd,两个操作数Rn,Op2,Rn经常是寄存器,Op2可以是四类(立即数#n、寄存器Rm、带有立即数的移位寄存器Rm ,lsl #n,带有寄存器的移位寄存器Rm ,lsl Rs
13671484-deb4032a213ce71f.jpg
数据操作
  • Arithmetic算数表,只有加减法。rsbreverse subtract是特殊的减法,Op2是被减数,并且Op2只允许是立即数和移位寄存器
    结尾带有c(carry)的指令表示条件指令,带有进位的指令,允许算数值超过寄存器的大小,例如计算0x00FF+0x0104,因为第二个数超过了8位,需要分步计算,从最低有效位开始,最低字节位给出,0xFF + 0x04这个加法再c的条件下,允许进位,设置进位标志,计0x03加一个进位标志。然后是第二部分,高位加法0x00+0x01+1最后一个1是进位,计0x02,合计0x203。

QUESTION

  • 当程序从子方法返回到父方法时,PC是接着进入子方法之前,还是接着子方法结果来继续?

ARM在线汇编网站
ARM Instruction Set

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值