首页
学习
活动
专区
圈层
工具
发布
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.5节C风格数据结构内存布局之基本数据类型构成的结构体

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

在C语言里,结构体是不同类型数据结构的集合。由于结构体比较复杂,分为几种情况来讨论:

1. 成员全是由基本数据类型构成

2. 成员有基本数据类型,也有其它结构体构成,构成成员的结构体是1,2两种情况。

3. 结构体构成的数组

这一节先讨论第一种情况.要了解这种类型结构体的内存布局,首先要知道成员在内存里是如何排序,及各个成员在内存占的大小。先看一个例子

代码语言:javascript
复制
 #include <stdio.h>
 struct xuzhina_dump_c05_s3_stru
 {
     char a;
     short b;
     int c;
     long d;
     float e;
     double f;
     void* g;
 };
 
 int main()
 {
     struct xuzhina_dump_c05_s3_stru test;
     printf( "addr:%x, 1st:%x, 2nd:%x, 3rd:%x, 4th:%x, 5th:%x, 6th:%x, 7th:%x
    ",
             &test, &test.a, &test.b, &test.c, &test.d, &test.e, &test.f, &te
    st.g );
     return 0;
 }

运行结果:

代码语言:javascript
复制
addr:bf909eb4, 1st:bf909eb4, 2nd:bf909eb6, 3rd:bf909eb8, 4th:bf909ebc, 5th:bf909ec0, 6th:bf909ec4, 7th:bf909ecc

可得这样的结论:

1.        结构体对象的地址和第一个成员地址一样。也就是所谓的基地址

2.        第一个成员a和第二个成员b分别是char,short,不足dword大小,为了内存对齐,挤一个dword里。int, long, float, double,指针由于大小分别是4,4,4,8,4,直接按照4字节进行内存对齐,也分别占4,4,4,8,4个字节的空间.

3.        各个成员还是按照结构体的声明顺序由低到高排列。

4.        每个成员的地址都是它前面所有成员的大小(包括内存对齐)和基地址的累加。像test.d的地址就是bf909eb4+0x8 = bf909ebc.它前面的成员a ,b在一个dword, c占一个dword,刚好是8.

可能成员类型会由于内存对齐而分辨不出来。其实,用5.1节的方法是可以分辨得出来。有兴趣可以尝试一下。

那么一个结构体如何从汇编里识别出来呢,它究竟有什么特征?

先看一下这个例子。

代码语言:javascript
复制
 struct xuzhina_dump_c05_s3_stru
 {
     char a;
     short b;
     int c;
 };
 
 bool equal( struct xuzhina_dump_c05_s3_stru* first,
         struct xuzhina_dump_c05_s3_stru* second )
 {
     if ( first->a != second->a )
     {
         return false;
     }
     if ( first->b != second->b )
     {
         return false;
     }
     if ( first->c != second->c )
     {
         return false;
     }
     return true;
 }
 
 int main()
 {
     struct xuzhina_dump_c05_s3_stru first = { 'a', 10, 20 };
     struct xuzhina_dump_c05_s3_stru second = {'a', 10, 21 };

     if ( equal( &first, &second ) )
     {
         return 0;
     }
 
     return 1;
 }

汇编代码

代码语言:javascript
复制
(gdb) disassemble equal                                                       
Dump of assembler code for function _Z5equalP24xuzhina_dump_c05_s3_struS0_:
   0x08048470 <+0>:     push   %ebp
   0x08048471 <+1>:     mov    %esp,%ebp
   0x08048473 <+3>:     mov    0x8(%ebp),%eax
   0x08048476 <+6>:     movzbl (%eax),%edx
   0x08048479 <+9>:     mov    0xc(%ebp),%eax
   0x0804847c <+12>:    movzbl (%eax),%eax
   0x0804847f <+15>:    cmp    %al,%dl

   0x08048481 <+17>:    je     0x804848a <_Z5equalP24xuzhina_dump_c05_s3_struS0_+26>

   0x08048483 <+19>:    mov    $0x0,%eax
   0x08048488 <+24>:    jmp    0x80484c0 <_Z5equalP24xuzhina_dump_c05_s3_struS0_+80>

   0x0804848a <+26>:    mov    0x8(%ebp),%eax

   0x0804848d <+29>:    movzwl 0x2(%eax),%edx
   0x08048491 <+33>:    mov    0xc(%ebp),%eax
   0x08048494 <+36>:    movzwl 0x2(%eax),%eax
   0x08048498 <+40>:    cmp    %ax,%dx
   0x0804849b <+43>:    je     0x80484a4 <_Z5equalP24xuzhina_dump_c05_s3_struS0_+52>
   0x0804849d <+45>:    mov    $0x0,%eax
   0x080484a2 <+50>:    jmp    0x80484c0 <_Z5equalP24xuzhina_dump_c05_s3_struS0_+80>

   0x080484a4 <+52>:    mov    0x8(%ebp),%eax

   0x080484a7 <+55>:    mov    0x4(%eax),%edx
   0x080484aa <+58>:    mov    0xc(%ebp),%eax
   0x080484ad <+61>:    mov    0x4(%eax),%eax
   0x080484b0 <+64>:    cmp    %eax,%edx

   0x080484b2 <+66>:    je     0x80484bb <_Z5equalP24xuzhina_dump_c05_s3_struS0_+75>

   0x080484b4 <+68>:    mov    $0x0,%eax
   0x080484b9 <+73>:    jmp    0x80484c0 <_Z5equalP24xuzhina_dump_c05_s3_struS0_+80>

   0x080484bb <+75>:    mov    $0x1,%eax

   0x080484c0 <+80>:    pop    %ebp
   0x080484c1 <+81>:    ret    
End of assembler dump.

(gdb) disassemble main
Dump of assembler code for function main:
   0x080484c2 <+0>:     push   %ebp
   0x080484c3 <+1>:     mov    %esp,%ebp
   0x080484c5 <+3>:     sub    $0x18,%esp

   0x080484c8 <+6>:     movb   $0x61,-0x8(%ebp)
   0x080484cc <+10>:    movw   $0xa,-0x6(%ebp)
   0x080484d2 <+16>:    movl   $0x14,-0x4(%ebp)

   0x080484d9 <+23>:    movb   $0x61,-0x10(%ebp)
   0x080484dd <+27>:    movw   $0xa,-0xe(%ebp)
   0x080484e3 <+33>:    movl   $0x15,-0xc(%ebp)

   0x080484ea <+40>:    lea    -0x10(%ebp),%eax
   0x080484ed <+43>:    mov    %eax,0x4(%esp)
   0x080484f1 <+47>:    lea    -0x8(%ebp),%eax
   0x080484f4 <+50>:    mov    %eax,(%esp)
   0x080484f7 <+53>:    call   0x8048470 <_Z5equalP24xuzhina_dump_c05_s3_struS0_>
   0x080484fc <+58>:    test   %al,%al
   0x080484fe <+60>:    je     0x8048507 <main+69>
   0x08048500 <+62>:    mov    $0x0,%eax
   0x08048505 <+67>:    jmp    0x804850c <main+74>
   0x08048507 <+69>:    mov    $0x1,%eax
   0x0804850c <+74>:    leave  
   0x0804850d <+75>:    ret    
End of assembler dump.

从equal函数的汇编代码可以看到,

1.        结构体都是一个基地址,从它三个成员的寻址看到,都是先把基地址放到某寄存器里。如mov  0x8(%ebp),%eax

2.        结构体成员的访问,都在基址基础上加上前面所有成员的大小。如成员c,前面a和b占四个字节,所以就有“mov 0x8(%ebp),%eax”, “mov 0x4(%eax),%edx”.

3.        每个成员与基址的偏移量,不是构成一个等差数列。

下一篇
举报
领券