【ARM 汇编基础速成5】ARM汇编之连续存取

原文链接 https://azeria-labs.com/load-and-store-multiple-part-5/

连续加载/存储。

有时连续加载(存储)会显得更加高效。因为我们可以使用LDM(load multiple)以及STM(store multiple)。这些指令基于起始地址的不同,有不同的形式。下面是我们会在这一节用到的相关代码。在下文中会详细讲解。

.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             /* words[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] = 0x04; r6 -> array_buff[2] = 0x05 */
 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-r5}            /* 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的地址*/
 .word array_buff+8           /* array_buff[2]的地址 */

在开始前,再深化一个概念,就是.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
0x80bc <words+16>: 0x00000004 0x00000005 0x00000006

接下来我们把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
r2      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指令。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

PUSH和POP

在内存中存在一块进程相关的区域叫做栈。栈指针寄存器SP在正常情形下指向这篇区域。应用经常通过栈做临时的数据存储。X86使用PUSH和POP来访问存取栈上数据。在ARM中我们也可以用这两条指令:

当PUSH压栈时,会发生以下事情:

  1. SP值减4。
  2. 存放信息到SP指向的位置。

当POP出栈时,会发生以下事情:

  1. 数据从SP指向位置被加载
  2. SP值加4。

下面是我们使用PUSH/POP以及LDMIA/STMDB命令示例:

.text
.global _start

_start:
   mov r0, #3
   mov r1, #4
   push {r0, r1}
   pop {r2, r3}
   stmdb sp!, {r0, r1}
   ldmia sp!, {r4, r5}
   bkpt

让我们来看看这段汇编的反汇编:

azeria@labs:~$ as pushpop.s -o pushpop.o
azeria@labs:~$ ld pushpop.o -o pushpop
azeria@labs:~$ objdump -D pushpop
pushpop: file format elf32-littlearm

Disassembly of section .text:

00008054 <_start>:
 8054: e3a00003 mov r0, #3
 8058: e3a01004 mov r1, #4
 805c: e92d0003 push {r0, r1}
 8060: e8bd000c pop {r2, r3}
 8064: e92d0003 push {r0, r1}
 8068: e8bd0030 pop {r4, r5}
 806c: e1200070 bkpt 0x0000

可以看到,我们的LDMIA以及STMDB指令被编译器换为了PUSH和POP。因为PUSH和STMDB sp!是等效的。同样的还有POP和LDMIA sp!。让我们在GDB里面跑一下上面那段汇编代码。

gef> break _start
gef> run
gef> nexti 2
[...]
gef> x/w $sp
0xbefff7e0: 0x00000001

在连续执行完前两条指令后,我们来看看SP,下一条PUSH指令会将其减8,并将R1和R0的值按序存放到栈上。

gef> nexti
[...] ----- Stack -----
0xbefff7d8|+0x00: 0x3 <- $sp
0xbefff7dc|+0x04: 0x4
0xbefff7e0|+0x08: 0x1
[...] 
gef> x/w $sp
0xbefff7d8: 0x00000003

再之后,这两个值被出栈,按序存到寄存器R2和R3中,之后SP加8。

gef> nexti
gef> info register r2 r3
r2     0x3    3
r3     0x4    4
gef> x/w $sp
0xbefff7e0: 0x00000001
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容

  • 原文地址 一、Hopper中的ARM指令ARM处理器就不多说了,ARM处理器因为低功耗等原因,所以大部分移动设备上...
    颜值不够才华来凑阅读 1,130评论 0 4
  • 前言 本文翻译自iOS Assembly Tutorial: Understanding ARM 翻译的不对的地...
    桃红宿雨阅读 18,035评论 7 198
  • 或许我们应该感谢,这个浮躁的社会,正是这种社会给了我们犯错的机会,也给了我们犯错改正的机会。我们在前行的路途中,犯...
    北林可乐阅读 313评论 0 2
  • 落尽三千烦恼丝, 自此不问红尘事。 一身淄衣青山囚, 古佛青灯伴余生。 注释:这几句话是我心中的一种烦闷,因为一些...
    谭惜言阅读 1,094评论 0 2
  • 在你那待很久了,却突然发现不知该去哪。 兜兜转转,还是回到了一个人的生活。 走到哪,都能看到你和我走在一起的身影。...
    彭程氏阅读 177评论 0 0