0%

ARM汇编-4 指针

1. 指针

指针也就是内存地址,指针变量是用来存放内存地址的变量。不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。
可使用 & 运算符访问地址。

之前的文章中有过说明,指针在内存中占8个字节。
可以是用sizeof来打印指针的size。

1
2
3
4
5
6
void func() {
int *a;
a = (int *)100;
a ++;
printf("%d", a);
}

这里定义一个int类型的指针a,然后赋值位100,我们知道指针的size是8个字节,a++之后打印多少?

答案是104。是的,没有看错,这里是因为指针的自增和自减操作,与执行的数据类型的宽度有关。

如果a = (char *)100,则打印的就是101。

1
2
3
4
5
6
void func() {
int *a;
a = (int *)100;
a = a + 1;
printf("%d", a);
}

这个不是指针的自增、自减了,这个时候就跟指针的size有关了,打印108。

1
2
3
4
5
6
7
8
9
10
void func_add() {
int *a;
a = (int *)100;

int *b;
b = (int *)200;

int x = a - b;
printf("x = %d", x);
}

先说答案,打印的结果是x = -25

a - b = -100, 然后除以4就得到了这个结果。

指针的运算单位是执行的数据类型的宽度。

1.1 二级指针

1
2
3
4
5
6
void func() {
int **a;
a = (int **)100;
a = a + 1;
printf("%d", a);
}

这个时候a运算时,执行的类型就是 char *类型,这是一个指针,8个字节。所以结果就是108。

2. 指针的汇编

1
2
3
4
5
void func() {
int *a;
int b = 10;
a = &b;
}

按照我们正常的理解,上述代码的意思就是把b的地址给到a,这个时候*a=10

看一下上面的代码汇编之后是什么样子的。

1
2
3
4
5
6
7
8
9
10
11
12
Demo`func:
0x100206130 <+0>: sub sp, sp, #0x10 ; =0x10
// 1. x8 = sp + 0x4,x8指向这个位置
0x100206134 <+4>: add x8, sp, #0x4 ; =0x4
// 2. 局部变量,w9=10
0x100206138 <+8>: mov w9, #0xa
// 3. 把w9的值放在x8所在的地址上。
0x10020613c <+12>: str w9, [sp, #0x4]
// 4. 把x8存储的地址放在sp + 0x8的位置上。
-> 0x100206140 <+16>: str x8, [sp, #0x8]
0x100206144 <+20>: add sp, sp, #0x10 ; =0x10
0x100206148 <+24>: ret

通过lldb打印一下相关数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
(lldb) register read sp
sp = 0x000000016fbff880

(lldb) register read x8
x8 = 0x000000016fbff884

(lldb) register read x9
x9 = 0x000000000000000a

// 打印一下x8寄存器里的内存地址情况,里头存的值是0xa
(lldb) x 0x000000016fbff884
0x16fbff884: 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x16fbff894: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

// x8的地址放在了sp+0x8的位置,打印一下内存,就是x8存储的地址。
(lldb) x 0x000000016fbff888
0x16fbff888: 84 f8 bf 6f 01 00 00 00 00 00 00 00 00 00 00 00 ...o............
0x16fbff898: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

2.1 数组

1
2
3
4
5
6
void func() {
int arr[5] = {1,2,3,4,5};
for (int i = 0; i < 5; i++) {
printf("%d\n", *(arr + i));
}
}

2.2 野指针

1
2
3
4
void func() {
char *p;
char a = *p;
}

通过代码,我们知道,只是把*p的值给了a
为什么会发生野指针呢?

1
2
3
4
5
6
7
8
9
Demo`func:
0x100812134 <+0>: sub sp, sp, #0x10 ; =0x10
// 1. 因为p是指针。把sp + 0x8的地址中的值给x8
-> 0x100812138 <+4>: ldr x8, [sp, #0x8]
// 2. 把x8寄存器中存的地址的值给w9
0x10081213c <+8>: ldrb w9, [x8]
0x100812140 <+12>: strb w9, [sp, #0x7]
0x100812144 <+16>: add sp, sp, #0x10 ; =0x10
0x100812148 <+20>: ret
  1. 第一步寻址操作,获取x8寄存器的值的地址

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    (lldb) register read sp
    sp = 0x000000016f5f3880

    // sp + 0x8 = 0x000000016f5f3888
    (lldb) x 0x000000016f5f3888
    0x16f5f3888: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    0x16f5f3898: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

    (lldb) register read x8
    x8 = 0x0000000000000000

    这里发现 x8寄存器中存的地址是空,全是0。

  2. 把x8寄存器中地址所在的值给w9。寻址操作
    这里寻址是从0x00000000上找值,从空地址上找值,就会发生crash。