当前位置:网站首页>ARM V7 连续加载/存储

ARM V7 连续加载/存储

2022-06-25 12:02:00 xiaozhiwise

/*
 * 2018/12/18    10:15    qing
 */


/*
 * 连续加载/存储
 */

    使用LDM(load multiple)以及STM(store multiple)


    stmdb r0!, {r4-r11, r14}    /* 将寄存器R4~R11也保存到堆栈r0中    [[ 从寄存器到内存 ]] */
    ldmia r0!, {r4-r11, r14}    /* 从堆栈r0恢复到R4~R11,R14            [ [从内存到寄存器 ]] */


    
    .data

    array_buff:
        .word    0x00000000    /* array_buff[0] */
        .word    0x00000000    /* array_buff[1] */
        .word    0x00000000    /* array_buff[2] 这一项存的是指向array_buff+8的指针 */
        .word    0x00000000    /* array_buff[3] */
        .word    0x00000000    /* array_buff[4] */

    .text
    .global main

    main:
        adr r0, words + 12                /* wrods[3] 的地址  ---> r0 */
        ldr r1, array_buff_bridge        /* array_buff[0]的地址 -> r1 */
        ldr r2, array_buff_bridge + 4    /* array_buff[2]的地址 -> r2 */
        ldm r0, {r4, r5}                /* words[3] --> r4 = 0x03; words[4] --> r5 = 0x04 */
        stm r1, {r4, r5}                /* r4 --> array_buff[0] = 0x03; r5 --> array_buff[1] = 0x04 */

        ldmia r0, {r4-r6}                /* words[3] --> r4 = 0x03, words[4] --> r5 = 0x04; words[5] --> r6 = 0x05 */
        stmia r1, {r4-r6}                /* r4 --> array_buff[0] = 0x03; r5 --> array_buff[1] = 0x4; r6 --> array_buff[2] = 0x5 */
        
        ldmib r0, {r4-r6}                /* words[4] --> r4 = 0x04; words[5] --> r5 = 0x05; words[6] --> r6 = 0x06 */
        stmib r1, {r4-r6}                /* r4 --> array_buff[1] = 0x04; r5 --> array_buff[2] = 0x05; r6 --> array_buff[3] = 0x06 */
        
        ldmda r0, {r4-r6}                /* words[3] --> r6 = 0x03; words[2] --> r5 = 0x02; words[1] --> r4 = 0x01 */
        ldmdb r0, {r4-r6}                /* words[2] --> r6 = 0x02; words[1] --> r5 = 0x01; words[0] --> r4 = 0x00 */
        
        stmda r2, {r4-r6}                /* r6 --> array_buff[2] = 0x02; r5 --> array_buff[1] = 0x01; r4 --> array_buff[0] = 0x00 */
        stmdb r2, {r4-r6}                /* r5 --> array_buff[1] = 0x01; r4 --> array_buff[0] = 0x00 */
        
        bx lr

    .words:
        .word 0x00000000        /* words[0] */
        .word 0x00000001        /* words[1] */
        .word 0x00000002        /* words[2] */
        .word 0x00000003        /* words[3] */
        .word 0x00000004        /* words[4] */
        .word 0x00000005        /* words[5] */
        .word 0x00000006        /* words[6] */
    
    array_buff_bridge:
        .word    array_buff        /* array_buff[0] address */
        .word    array_buff + 8    /* array_buff[2] address */


    .word标识是对内存中长度为32位的数据块作引用。这对于理解代码中的偏移量很重要。
    
    所以程序中由.data段组成的数据,内存中会申请一个长度为5的4字节数组array_buff。我们的所有内存存储操作,都是针对这段内存中的数据段做读写的。
    
    而.text端包含着我们对内存操作的代码以及只读的两个标签,
    
    一个标签是含有七个元素的数组,另一个是为了链接.text段和.data段所存在的对于array_buff的引用。


/*
 * 下面就开始一行行的分析
 */

    adr r0, words+12             /* words[3]的地址 -> r0 */

        用ADR指令来获得words[3]的地址,并存到R0中。我们选了一个中间的位置是因为一会要做向前以及向后的操作。

        gef> break _start
        gef> run
        gef> nexti
        
        R0当前就存着words[3]的地址了,也就是0x80B8。也就是说,我们的数组word[0]的地址是:0x80AC(0x80B8-0XC)。

        gef> x/7w 0x00080AC
        0x80ac <words>: 0x00000000 0x00000001 0x00000002 0x00000003        /* 0x80ac 80B0 80B4 80B8 */
        0x80bc <words+16>: 0x00000004 0x00000005 0x00000006                /* 0x80bc 80C0 80C4         */

        接下来我们把R1和R2指向array_buff[0]以及array_buff[2]。在获取了这些指针后,我们就可以操作这个数组了。


    ldr r1, array_buff_bridge    /* array_buff[0]的地址 -> r1 */
    ldr r2, array_buff_bridge+4  /* array_buff[2]的地址 -> r2 */

        执行完上面这两条指令后,R1和R2的变化。
        gef> info register r1 r2
        r1      0x100d0     65744        /* 0x100d0 = 65744 */
        r2      0x100d8     65752        /* 0x100d8 = 65752 */

        下一条LDM指令从R0指向的内存中加载了两个字的数据。因为R0指向words[3]的起始处,所以words[3]的值赋给R4,words[4]的值赋给R5。

    ldm r0, {r4,r5}              /* words[3] -> r4 = 0x03; words[4] -> r5 = 0x04 */

        所以我们用一条指令加载了两个数据块,并且放到了R4和R5中。

        gef> info registers r4 r5
        r4      0x3      3
        r5      0x4      4


    
        STM指令将R4与R5中的值0x3和0x4存储到R1指向的内存中。这里R1指向的是array_buff[0],也就是说 array_buff[0] = 0x00000003
        
        以及 array_buff[1] = 0x00000004 。如不特定指定,LDM与STM指令操作的最小单位都是一个字(四字节)。

    stm r1, {r4,r5}              /* r4 -> array_buff[0] = 0x03; r5 -> array_buff[1] = 0x04 */
    
        值0x3与0x4被存储到了R1指向的地方0x100D0以及0x100D4。

        gef> x/2w 0x000100D0
        0x100d0 <array_buff>:  0x00000003   0x00000004

    

        之前说过LDM和STM有多种形式。不同形式的扩展字符和含义都不同:

        IA(increase after)
        IB(increase before)
        DA(decrease after)
        DB(decrease before)

        这些扩展划分的主要依据是,作为源地址或者目的地址的指针是在访问内存前增减,还是访问内存后增减。以及,LDM与LDMIA功能相同,
        
        都是在加载操作完成后访问对地址增加的。通过这种方式,我们可以序列化的向前或者向后从一个指针指向的内存加载数据到寄存器,
        
        或者存放数据到内存。如下示意代码 。

    ldmia r0, {r4-r6}    /* words[3] -> r4 = 0x03, words[4] -> r5 = 0x04; words[5] -> r6 = 0x05; */
    stmia r1, {r4-r6}    /* r4 -> array_buff[0] = 0x03; r5 -> array_buff[1] = 0x04; r6 -> array_buff[2] = 0x05 */

        在执行完这两条代码后,R4到R6寄存器所访问的内存地址以及存取的值是  0x000100D0 , 0x000100D4 ,以及 0x000100D8 ,值对应是 0x3,0x4,以及 0x5 。

        gef> info registers r4 r5 r6
        r4     0x3     3
        r5     0x4     4
        r6     0x5     5
        gef> x/3w 0x000100D0
        0x100d0 <array_buff>: 0x00000003  0x00000004  0x00000005

        而LDMIB指令会首先对指向的地址先加 4 ,然后再加载数据到寄存器中。所以第一次加载的时候也会对指针加 4 ,
        
        所以存入寄存器的是 0x4 (words[4])而不是 0x3 (words[3])

    dmib r0, {r4-r6}            /* words[4] -> r4 = 0x04; words[5] -> r5 = 0x05; words[6] -> r6 = 0x06 */
    stmib r1,{r4-r6}            /* r4 -> array_buff[1] = 0x04; r5 -> array_buff[2] = 0x05; r6 -> array_buff[3] = 0x06 */

        执行后
        gef> x/3w 0x100D4
        0x100d4 <array_buff+4>: 0x00000004  0x00000005  0x00000006
        gef> info register r4 r5 r6
        r4     0x4    4
        r5     0x5    5
        r6     0x6    6

        当用LDMDA指令时,执行的就是反向操作了。R0指向words[3],当加载数据时数据的加载方向变成加载words[3],words[2],words[1]的值到R6,R5,R4中。
        
        这种加载流程发生的原因是我们LDM指令的后缀是DA,也就是在加载操作完成后,会将指针做递减的操作。注意在做减法模式下的寄存器的操作是反向的,
        
        这么设定的原因为了保持让编号大的寄存器访问高地址的内存的原则。

        多次加载,后置减法:

    ldmda r0, {r4-r6} /* words[3] -> r6 = 0x03; words[2] -> r5 = 0x02; words[1] -> r4 = 0x01 */

        执行之后,R4-R6的值:
        gef> info register r4 r5 r6
        r4     0x1    1
        r5     0x2    2
        r6     0x3    3

        多次加载,前置减法:

    ldmdb r0, {r4-r6} /* words[2] -> r6 = 0x02; words[1] -> r5 = 0x01; words[0] -> r4 = 0x00 */

        执行之后,R4-R6的值:
        gef> info register r4 r5 r6
        r4 0x0 0
        r5 0x1 1
        r6 0x2 2

        多次存储,后置减法:

    stmda r2, {r4-r6} /* r6 -> array_buff[2] = 0x02; r5 -> array_buff[1] = 0x01; r4 -> array_buff[0] = 0x00 */

        执行之后,array_buff[2],array_buff[1],以及array_buff[0]的值:
        gef> x/3w 0x100D0
        0x100d0 <array_buff>: 0x00000000 0x00000001 0x00000002

        多次存储,前置减法:

    stmdb r2, {r4-r5} /* r5 -> array_buff[1] = 0x01; r4 -> array_buff[0] = 0x00; */

        执行之后,array_buff[1],以及array_buff[0]的值:
        gef> x/2w 0x100D0
        0x100d0 <array_buff>: 0x00000000 0x00000001

原网站

版权声明
本文为[xiaozhiwise]所创,转载请带上原文链接,感谢
https://blog.csdn.net/xiaozhiwise/article/details/125320004