首页
学习
活动
专区
圈层
工具
发布
21 篇文章
1
《coredump问题原理探究》Linux x86版6.3节有成员变量的类coredump例子
2
《coredump问题原理探究》Linux x86版6.5节虚函数的coredump例子
3
《coredump问题原理探究》Linux x86版6.8节多继承coredump例子
4
《coredump问题原理探究》Linux x86版7.2节vector coredump例子
5
《coredump问题原理探究》Linux x86版7.4节List coredump例子
6
《coredump问题原理探究》Linux x86版7.6节 Map coredump例子
7
《coredump问题原理探究》Linux x86版5.9节C风格数据结构内存布局之联合体
8
《coredump问题原理探究》Linux x86版6.1节C++风格数据结构内存布局之无成员变量的类
9
《coredump问题原理探究》Linux x86版6.2节C++风格数据结构内存布局之有成员变量的类
10
《coredump问题原理探究》Linux x86版4.5节函数的逆向之coredump例子
11
《coredump问题原理探究》Linux x86版5.1节C风格数据结构内存布局之引言
12
《coredump问题原理探究》Linux x86版5.2节C风格数据结构内存布局之基本数据类型
13
《coredump问题原理探究》Linux x86版5.3节C风格数据结构内存布局之数组
14
《coredump问题原理探究》Linux x86版5.4节C风格数据结构内存布局之数组coredump例子
15
《coredump问题原理探究》Linux x86版5.5节C风格数据结构内存布局之基本数据类型构成的结构体
16
《coredump问题原理探究》Linux x86版5.6节C风格数据结构内存布局之复合类型构成的结构体
17
《coredump问题原理探究》Linux x86版5.7节C风格数据结构内存布局之结构体数组
18
《coredump问题原理探究》Linux x86版5.8节C风格数据结构内存布局之结构体数组结构体coredump
19
《coredump问题原理探究》Linux x86版3.5节栈布局之-fomit-frame-pointer编译选项
20
《coredump问题原理探究》Linux x86版3.6节栈布局之gcc内嵌关键字
21
《coredump问题原理探究》Linux x86版3.8节栈布局之栈溢出coredump例子

《coredump问题原理探究》Linux x86版5.3节C风格数据结构内存布局之数组

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://cloud.tencent.com/developer/article/1344512

在C语言里,数组就是相同类型变量的集合体。由这个定义,可大致得知数组的特征:

1.有首元素。而首元素的地址和数组地址一样,即有基地址

2.每个元素的大小是一样的。那么每个元素相对基地址的偏移值应该是元素大小和索引值的乘积。

也就是说,基地址和与索引值成比例的偏移值有可能是数组的特征。

还是按照上面的方式来逐个对各类型的数组进行探究。

先看一下char型的数组

代码语言:javascript
复制
 #include <stdio.h>
 int main()
 {
     char buf[16];
     char c = 'a';
 
     printf( "head of array:%x, tail of array:%x", buf, &buf[15] );
 
     for ( int i = 0; i < 16; i++, c++ )
     {
         buf[i] = c;
     }
     buf[15] = '\0';

     printf( "%s\n", buf );
 
     return 0;
}

再看一下它的汇编:

代码语言:javascript
复制
(gdb) disassemble main
Dump of assembler code for function main:
   0x080485a0 <+0>:     push   %ebp
   0x080485a1 <+1>:     mov    %esp,%ebp
   0x080485a3 <+3>:     and    $0xfffffff0,%esp
   0x080485a6 <+6>:     sub    $0x30,%esp
   0x080485a9 <+9>:     movb   $0x61,0x2f(%esp)

   0x080485ae <+14>:    lea    0x18(%esp),%eax
   0x080485b2 <+18>:    add    $0xf,%eax
   0x080485b5 <+21>:    mov    %eax,0x8(%esp)
   0x080485b9 <+25>:    lea    0x18(%esp),%eax
   0x080485bd <+29>:    mov    %eax,0x4(%esp)
   0x080485c1 <+33>:    movl   $0x80486b4,(%esp)
   0x080485c8 <+40>:    call   0x8048460 <printf@plt>

   0x080485cd <+45>:    movl   $0x0,0x28(%esp)
   0x080485d5 <+53>:    jmp    0x80485f2 <main+82>

   0x080485d7 <+55>:    lea    0x18(%esp),%edx
   0x080485db <+59>:    mov    0x28(%esp),%eax
   0x080485df <+63>:    add    %eax,%edx
   0x080485e1 <+65>:    movzbl 0x2f(%esp),%eax
   0x080485e6 <+70>:    mov    %al,(%edx)
   0x080485e8 <+72>:    addl   $0x1,0x28(%esp)
   0x080485ed <+77>:    addb   $0x1,0x2f(%esp)
   0x080485f2 <+82>:    cmpl   $0xf,0x28(%esp)
   0x080485f7 <+87>:    setle  %al
   0x080485fa <+90>:    test   %al,%al
   0x080485fc <+92>:    jne    0x80485d7 <main+55>

   0x080485fe <+94>:    movb   $0x0,0x27(%esp)
   0x08048603 <+99>:    lea    0x18(%esp),%eax
   0x08048607 <+103>:   mov    %eax,(%esp)
   0x0804860a <+106>:   call   0x8048470 <puts@plt>
   0x0804860f <+111>:   mov    $0x0,%eax
   0x08048614 <+116>:   jmp    0x804861e <main+126>
   0x08048616 <+118>:   mov    %eax,(%esp)
   0x08048619 <+121>:   call   0x8048490 <_Unwind_Resume@plt>
   0x0804861e <+126>:   leave  
   0x0804861f <+127>:   ret    
End of assembler dump.

代码语言:javascript
复制
   0x080485ae <+14>:    lea    0x18(%esp),%eax
   0x080485b2 <+18>:    add    $0xf,%eax
   0x080485b5 <+21>:    mov    %eax,0x8(%esp)
   0x080485b9 <+25>:    lea    0x18(%esp),%eax
   0x080485bd <+29>:    mov    %eax,0x4(%esp)
   0x080485c1 <+33>:    movl   $0x80486b4,(%esp)
   0x080485c8 <+40>:    call   0x8048460 <printf@plt>

可以看到:

1.      第二个参数,buf是由eax得来,而eax是由esp+0x18,也就是说,esp+0x18是buf的基地址

2.      第三个参数,&buf15,是由esp+0x18 + 0xf得来的。0xf刚好和buf15相对buf基地址的偏移相等。

3.      由buf15的地址比buf0的高,可知,数组在栈上是递增的。

代码语言:javascript
复制
   0x080485a9 <+9>:     movb   $0x61,0x2f(%esp)

可知,局部变量c是放在esp+0x2f里。

再由

代码语言:javascript
复制
   0x080485d7 <+55>:    lea    0x18(%esp),%edx
   0x080485db <+59>:    mov    0x28(%esp),%eax
   0x080485df <+63>:    add    %eax,%edx
   0x080485e1 <+65>:    movzbl 0x2f(%esp),%eax
   0x080485e6 <+70>:    mov    %al,(%edx)
   0x080485e8 <+72>:    addl   $0x1,0x28(%esp)
   0x080485ed <+77>:    addb   $0x1,0x2f(%esp)
   0x080485f2 <+82>:    cmpl   $0xf,0x28(%esp)
   0x080485f7 <+87>:    setle  %al
   0x080485fa <+90>:    test   %al,%al
   0x080485fc <+92>:    jne    0x80485d7 <main+55>

这个循环里的

代码语言:javascript
复制
   0x080485ed <+77>:    addb   $0x1,0x2f(%esp)

可知

   c递增的步长是1,刚好和char的大小一样。

且由

代码语言:javascript
复制
   0x080485d7 <+55>:    lea    0x18(%esp),%edx
   0x080485db <+59>:    mov    0x28(%esp),%eax
   0x080485df <+63>:    add    %eax,%edx

代码语言:javascript
复制
   0x080485e8 <+72>:    addl   $0x1,0x28(%esp)

可知,数组的元素地址确实是递增的,且每个元素的地址都是esp+0x18+i,即基地址+i

PS:下面的汇编指令

代码语言:javascript
复制
   0x080485e8 <+72>:    addl   $0x1,0x28(%esp)
   0x080485ed <+77>:    addb   $0x1,0x2f(%esp)
   0x080485f2 <+82>:    cmpl   $0xf,0x28(%esp)

的意思,就是c++, i++和i < 15。但把c++放入在i++,i<15之间主要是对c,i两个变量操作的指令之间没有依赖,混编的话,在多核多线程处理器能够同时并发执行。

打一下断点来验证一下上面结论

代码语言:javascript
复制
(gdb) tbreak *0x080485fe
Temporary breakpoint 1 at 0x80485fe
(gdb) r
Starting program: /home/buckxu/work/5/2/xuzhina_dump_c5_s2 

Temporary breakpoint 1, 0x080485fe in main ()
 (gdb) x /16c $esp+0x18
0xbffff468:     97 'a'  98 'b'  99 'c'  100 'd' 101 'e' 102 'f' 103 'g' 104 'h'
0xbffff470:     105 'i' 106 'j' 107 'k' 108 'l' 109 'm' 110 'n' 111 'o' 112 'p'

继续看一下short的数组:

代码语言:javascript
复制
   #include <stdio.h>
   int main()
   {
       short buf[16];
       short s = 'a';
  
       printf( "head of array:%x, tail of array:%x", buf, &buf[15] );
   
       for ( int i = 0; i < 16; i++, s++ )
      {
          buf[i] = s;
      }
  
  
      return buf[15];
  }

看一下相应的汇编:

代码语言:javascript
复制
(gdb) disassemble main
Dump of assembler code for function main:
   0x08048570 <+0>:     push   %ebp
   0x08048571 <+1>:     mov    %esp,%ebp
   0x08048573 <+3>:     and    $0xfffffff0,%esp
   0x08048576 <+6>:     sub    $0x40,%esp
   0x08048579 <+9>:     movw   $0x61,0x3e(%esp)

   0x08048580 <+16>:    lea    0x18(%esp),%eax
   0x08048584 <+20>:    add    $0x1e,%eax
   0x08048587 <+23>:    mov    %eax,0x8(%esp)
   0x0804858b <+27>:    lea    0x18(%esp),%eax
   0x0804858f <+31>:    mov    %eax,0x4(%esp)
   0x08048593 <+35>:    movl   $0x8048674,(%esp)
   0x0804859a <+42>:    call   0x8048440 <printf@plt>

   0x0804859f <+47>:    movl   $0x0,0x38(%esp)
   0x080485a7 <+55>:    jmp    0x80485c2 <main+82>

   0x080485a9 <+57>:    mov    0x38(%esp),%eax
   0x080485ad <+61>:    movzwl 0x3e(%esp),%edx
   0x080485b2 <+66>:    mov    %dx,0x18(%esp,%eax,2)
   0x080485b7 <+71>:    addl   $0x1,0x38(%esp)
   0x080485bc <+76>:    addw   $0x1,0x3e(%esp)
   0x080485c2 <+82>:    cmpl   $0xf,0x38(%esp)
   0x080485c7 <+87>:    setle  %al
   0x080485ca <+90>:    test   %al,%al
   0x080485cc <+92>:    jne    0x80485a9 <main+57>

   0x080485ce <+94>:    movzwl 0x36(%esp),%eax
   0x080485d3 <+99>:    cwtl   
   0x080485d4 <+100>:   jmp    0x80485de <main+110>
   0x080485d6 <+102>:   mov    %eax,(%esp)
   0x080485d9 <+105>:   call   0x8048460 <_Unwind_Resume@plt>
   0x080485de <+110>:   leave  
   0x080485df <+111>:   ret    
End of assembler dump.

按照char型数组类似的分析,可得:

1.      局部变量s存放在esp+0x3e

2.      buf的首地址是esp+0x18,尾元素的地址是esp+0x18+0x1e。可见buf在栈里也是递增的。

3.      buf的空间大小是0x1e+2=0x20 = 32。正好是16个short类型的大小。

4.      由0x080485bc可知,short型数组的递增步长是2,刚好是short的大小。

5.      由0x080485b2可知,对short数组每一个元素的引用,都要用到esp+0x18+2*eax,即基地址+i*sizeof(short)

继续对int, long,float,double,可以得到下表:

类型

特征

char

基地址 + 索引值*1

short

基地址 + 索引值*2

int

基地址 + 索引值*4

long

32-bit:基地址 + 索引值*4 64-bit:基地址 + 索引值*8

float

基地址 + 索引值*4 (因为单精度是占4个字节的),要配合浮点计算的指令确认

double

基地址 + 索引值*8 (双精度占8个字节) ,要配合浮点计算的指令确认

指针

32-bit:基地址 + 索引值*4 64-bit:基地址 + 索引值*8

其实,基地址+索引值*sizeof( element)这些方式,用汇编可以有很多种表示形式。如下面也算是一种

| lea $base,%eax  // 把base地址放到eax里 mov $index,%ecx // 把index放到ecx里。 mul $4, %ecx add %ecx, %eax   //这里eax就存放了index指向的元素地址了。 |

|:----|

详细可以搜索一下“寄存器寻址方式”里的”寄存器变址寻址方式“

下一篇
举报
领券