X86汇编语言学习手记(3)

2004年12月

在X86汇编语言学习手记(1)(2)中,可以看到栈(Stack)作为进程执行过程中数据的临时存储区域,通常包含如下几类数据:

局部变量

函数调用的返回地址

函数调用的入口参数

SFP 栈框架指针 (可以通过编译器优化选项去除)

本章中,将继续通过实验,了解全局变量和静态变量在进程中是如何存储和分配的。

注:不同的Calling Convention对入口参数的规定是有一定差别的,函数调用入口参数也有可能通过寄存器来传递。

例如IBM的Power PC和AMD的Opteron,函数的入口参数全部或部分就是通过寄存器来传递的。

1.全局变量和全局常量的实验

延续之前的方式,给出一个简单的C程序,其中声明的全局变量分为3种:

初始化过的全局变量

未初始化的全局变量

全局常量

#vi test5.c

int i=1;

int j=2;

int k=3;

int l,m;

int n;

const int o=7;

const int p=8;

const int q=9;

int main()

{

l=4;

m=5;

n=6;

return i+j+k+l+m+n+o+p+q;

}

# gcc test5.c -o test5

# mdb test5

Loading modules: [ libc.so.1 ]

> main::dis

main:              pushl   %ebp; main至main+1,创建Stack Frame

main+1:            movl    %esp,%ebp

main+3:            subl    $8,%esp

main+6:            andl    $0xf0,%esp

main+9:            movl    $0,%eax

main+0xe:          subl    %eax,%esp; main+(3-0xe),为局部变量预留栈空间,并保证栈16字节对齐

main+0x10:         movl    $4,0x8060948       ; l=4

main+0x1a:         movl    $5,0x806094c       ; m=5

main+0x24:         movl    $6,0x8060950       ; n=6

main+0x2e:         movl    0x8060908,%eax

main+0x33:         addl    0x8060904,%eax

main+0x39:         addl    0x806090c,%eax

main+0x3f:         addl    0x8060948,%eax

main+0x45:         addl    0x806094c,%eax

main+0x4b:         addl    0x8060950,%eax

main+0x51:         addl    0x8050808,%eax

main+0x57:         addl    0x805080c,%eax

main+0x5d:         addl    0x8050810,%eax     ; main+0x2e至main+0x5d,i+j+k+l+m+n+o+p+q

main+0x63:         leave                      ;撤销Stack Frame

main+0x64:         ret                        ; main函数返回

现在,让我们在全局变量初始化后的地方设置断点,观察一下这几个全局变量的值:

> main+0x2e:b                                 ;设置断点

> :r                                          ;运行程序

mdb: stop at main+0x2e

mdb: target stopped at:

main+0x2e:      movl    0x8060908,%eax

> 0x8060904,03/nap                            ;察看全局变量 i,j,k的值

test5`i:

test5`i:

test5`i:        1

test5`j:        2

test5`k:        3

> 0x8060948,03/nap                            ;察看全局变量l,m,n的值

test5`l:

test5`l:

test5`l:        4

test5`m:        5

test5`n:        6

> 0x8050808,03/nap                            ;察看全局变量o,p,q的值

o:

o:

o:              7

p:              8

q:              9

>

概念:进程地址空间 Process Address Space

+----------------------+ ----> 0xFFFFFFFF (4GB)

|                                   |

|   Kernel Space           |

|                                   |

+----------------------+ ----> _kernel_base (0xE0000000)

|                                   |

|   Other Library           |

:                                   :

:                                   :

|                                   |

+----------------------+

|     data section            |

|   Lib C Library           |

|     text section             |

:                                   :

:                                   :

+----------------------+

|                                   |

|                                   |

:                                   :

:       grow up                :

:                                   :

|    User Heap               |

|                                   |

+----------------------+

|      bss                        |

|                                   |

|    User Data                |

|                                   |

+----------------------+

|                                   |

|    User Text                |

|                                   |

|                                   |

+----------------------+ ----> 0x08050000

|                                   |

|    User Stack              |

|                                   |

:     grow down             :

:                                   :

:                                   :

|                                   |

|                                   |

+----------------------+ ----> 0

图 3-1 Solaris在IA32上的进程地址空间

如图3-1所示,Solaris在IA32上的进程地址空间和Linux是相似的,在用户进程的4GB地址空间内:

Kernel总是映射到用户地址空间的最高端,从宏定义_kernel_base至0xFFFFFFFF的区域

用户进程所依赖的各个共享库紧接着Kernel映射在用户地址空间的高端

最后是用户进程地址空间在地址空间的低端

各共享库的代码段,存放着二进制可执行的机器指令,是由kernel把该库ELF文件的代码段map到虚存空间,属性是read/exec/share

各共享库的数据段,存放着程序执行所需的全局变量,是由kernel把ELF文件的数据段map到虚存空间,属性为read/write/private

用户代码段,存放着二进制形式的可执行的机器指令,是由kernel把ELF文件的代码段map到虚存空间,属性为read/exec

用户代码段之上是数据段,存放着程序执行所需的全局变量,是由kernel把ELF文件的数据段map到虚存空间,属性为 read/write/private

用户代码段之下是栈(stack),作为进程的临时数据区,是由kernel把匿名内存map到虚存空间,属性为read/write/exec

用户数据段之上是堆(heap),当且仅当malloc调用时存在,是由kernel把匿名内存map到虚存空间,属性为read/write/exec

注意Stack和Heap的区别和联系:

相同点:

1. 都是来自于kernel分配的匿名内存,和磁盘上的ELF文件无关

2. 属性均为read/write/exec

不同点:

1.栈的分配在C语言层面一般是通过声明局部变量,调用函数引起的;堆的分配则是通过显式的调用(malloc)引起的

2.栈的释放在C语言层面是对用户透明的,用户不需要关心,由C编译器产生的相应的指令代劳;堆则需显式的调用(free)来释放

3.栈空间的增长方向是从高地址到低地址;堆空间的增长方向是由低地址到高地址

4.栈存在于任何进程的地址空间;堆则在程序中没有调用malloc的情况下不存在

用户地址空间的布局随着CPU和OS的不同,略有差异,以上都是基于X86 CPU在Solaris OS上的情况的讨论。

使用pmap命令,可以观察到系统中的指定进程的地址空间分布情况,下面就是用pmap观察bash进程的一个例子:

# pmap 1030

1030:   -bash

08045000      12K rw---    [ stack ]                        ; bash的栈

08050000     444K r-x--  /usr/bin/bash                      ; bash文本段

080CE000      72K rwx--  /usr/bin/bash                      ; bash的数据段

080E0000     156K rwx--    [ heap ]                         ; bash的堆

DD8C0000       8K r-x--  /usr/lib/locale/zh_CN.GB18030/methods_zh_CN.GB18030.so.2; so的文本段

DD8D1000       4K rwx--  /usr/lib/locale/zh_CN.GB18030/methods_zh_CN.GB18030.so.2 ; so的数据段

DD8E0000     324K r-x--  /usr/lib/locale/zh_CN.GB18030/zh_CN.GB18030.so.2

DD940000       8K rwx--  /usr/lib/locale/zh_CN.GB18030/zh_CN.GB18030.so.2

DD950000       4K rwx--    [ anon ]                         ;匿名内存,由映射/dev/zero设备来创建的

DD960000      12K r-x--  /usr/lib/libmp.so.2

DD973000       4K rwx--  /usr/lib/libmp.so.2

DD980000     628K r-x--  /usr/lib/libc.so.1

DDA2D000      24K rwx--  /usr/lib/libc.so.1

DDA33000       4K rwx--  /usr/lib/libc.so.1

DDA50000       4K rwx--    [ anon ]

DDA60000     548K r-x--  /usr/lib/libnsl.so.1

DDAF9000      20K rwx--  /usr/lib/libnsl.so.1

DDAFE000      32K rwx--  /usr/lib/libnsl.so.1

DDB10000      44K r-x--  /usr/lib/libsocket.so.1

DDB2B000       4K rwx--  /usr/lib/libsocket.so.1

DDB30000     152K r-x--  /usr/lib/libcurses.so.1

DDB66000      28K rwx--  /usr/lib/libcurses.so.1

DDB6D000       8K rwx--  /usr/lib/libcurses.so.1

DDB80000       4K r-x--  /usr/lib/libdl.so.1

DDB90000     292K r-x--  /usr/lib/ld.so.1

DDBE9000      16K rwx--  /usr/lib/ld.so.1

DDBED000       8K rwx--  /usr/lib/ld.so.1

total      2864K

问题:全局变量和全局常量在进程地址空间的位置?

显然,根据前面的叙述,全局变量在用户的数据段,那么全局常量呢,是数据段吗?

同样的,可以利用mdb将test5进程挂起,然后用pmap命令求证一下:

# mdb test5

Loading modules: [ libc.so.1 ]

> ::sysbp _exit         ;在系统调用_exit处设置断点

> :r                    ;运行程序

mdb: stop on entry to _exit

mdb: target stopped at:

libc.so.1`exit+0x2b:    jae     +0x15

>

此时,程序运行后在_exit处挂起,可以利用pmap在另一个终端内查看test5进程的地址空间了:

# ps -ef | grep test5

root  1387  1386  0 02:23:53 pts/1    0:00 test5

root  1399  1390  0 02:25:03 pts/3    0:00 grep test5

root  1386  1338  0 02:23:41 pts/1    0:00 mdb test5

# pmap -F 1387       ;用pmap强制查看

1387:   test5

08044000      16K rwx--    [ stack ]         ; test5的stack

08050000       4K r-x--  /export/home/asm/L3/test5        ; test5的代码段,起始地址为0x08050000

08060000       4K rwx--  /export/home/asm/L3/test5        ; test5的数据段,起始地址为0x08060000

DDAC0000     628K r-x--  /usr/lib/libc.so.1

DDB6D000      24K rwx--  /usr/lib/libc.so.1

DDB73000       4K rwx--  /usr/lib/libc.so.1

DDB80000       4K r-x--  /usr/lib/libdl.so.1

DDB90000     292K r-x--  /usr/lib/ld.so.1

DDBE9000      16K rwx--  /usr/lib/ld.so.1

DDBED000       8K rwx--  /usr/lib/ld.so.1

total      1000K

可以看到,由于test5程序没有使用malloc来申请内存,所以没有heap的映射

前面用mdb观察过这些全局变量和常量的初始化值,它们的地址分别是:

全局变量i,j,k:

0x8060904起始的12字节

全局变量l,m,n:

0x8060948起始的12字节

全局常量o,p,q:

0x8050808起始的12字节

显然,根据这些变量的地址,我们可以初步判断出这些变量属于哪个段:

由于test5数据段起始地址为0x08060000,我们得出结论:全局变量i,j,k,l,m,n属于数据段而test5代码段的起始地址为0x08050000,我们得出结论:全局常量o,p,q属于代码段

得出这个结论的确有点让人意外:全局常量竟然在代码段。

却又似乎在情理之中:数据段内存映射后的属性是r/w/x,而常量要求是只读属性,所以在代码段(r-x)就合情合理了。

问题:为什么这些全局变量地址不是连续的?

很容易注意到,全局变量i,j,k和l,m,n以及全局常量o,p,q是连续声明的,但地址实际上并不连续,而是在3段连续12字节的地址上。

当然,全局常量属于代码段,所以地址和全局变量是分开的;那么,为什么全局变量也并非连续呢?

前面谈到数据段实际上是从ELF格式的二进制文件映射到进程的地址空间的,就通过分析ELF文件格式来寻找答案吧:

# file test5

test5:          ELF 32-bit LSB executable 80386 Version 1, dynamically linked, not stripped

# elfdump test5

ELF Header                       ; ELF头信息共52(0x34)字节,具体意义可以参考ELF format的相关文档

ei_magic:   { 0x7f, E, L, F }         ; ELF的幻数

ei_class:   ELFCLASS32          ei_data:      ELFDATA2LSB   ; 32位的ELF文件,小端(LSB)编码

e_machine:  EM_386              e_version:    EV_CURRENT    ; Intel 80386,版本1

e_type:     ET_EXEC                                         ;可执行文件

e_flags:                     0

e_entry:             0x8050600  e_ehsize:     52  e_shstrndx:   27   ;程序入口点_start的地址0x8050600

e_shoff:                0x1584  e_shentsize:  40  e_shnum:      29   ; Section header table的大小是29*40

e_phoff:                  0x34  e_phentsize:  32  e_phnum:       5

Program Header[0]:                    ;描述Program header table本身在内存中如何映射

p_vaddr:      0x8050034       p_flags:    [ PF_X  PF_R ]

p_paddr:      0               p_type:     [ PT_PHDR ]

p_filesz:     0xa0            p_memsz:    0xa0

p_offset:     0x34            p_align:    0

Program Header[1]:                    ;描述程序装载器的路径名(.interp section)存放在文件的位置

p_vaddr:      0               p_flags:    [ PF_R ]

p_paddr:      0               p_type:     [ PT_INTERP ]

p_filesz:     0x11            p_memsz:    0

p_offset:     0xd4            p_align:    0

Program Header[2]:                    ;描述代码段在内存中如何映射,起始地址0x8050000,大小为 0x814

p_vaddr:      0x8050000       p_flags:    [ PF_X  PF_R ]

p_paddr:      0               p_type:     [ PT_LOAD ]

p_filesz:     0x814           p_memsz:    0x814

p_offset:     0               p_align:    0x10000

Program Header[3]:                    ;描述数据段在内存中如何映射,起始地址0x8060814,大小为0x144

p_vaddr:      0x8060814       p_flags:    [ PF_X  PF_W  PF_R ]

p_paddr:      0               p_type:     [ PT_LOAD ]

p_filesz:     0x118           p_memsz:    0x144

p_offset:     0x814           p_align:    0x10000

Program Header[4]:                    ;描述动态链接信息(.dynamic section)在内存中如何映射

p_vaddr:      0x8060848       p_flags:    [ PF_X  PF_W  PF_R ]

p_paddr:      0               p_type:     [ PT_DYNAMIC ]

p_filesz:     0xb8            p_memsz:    0

p_offset:     0x848           p_align:    0

Section Header[1]:  sh_name: .interp  ;该section保存了程序的解释程序(interpreter)的路径

sh_addr:      0x80500d4       sh_flags:   [ SHF_ALLOC ]

sh_size:      0x11            sh_type:    [ SHT_PROGBITS ]

sh_offset:    0xd4            sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x1

Section Header[2]:  sh_name: .hash;该section保存着一个符号的哈希表

sh_addr:      0x80500e8       sh_flags:   [ SHF_ALLOC ]

sh_size:      0x104           sh_type:    [ SHT_HASH ]

sh_offset:    0xe8            sh_entsize: 0x4

sh_link:      3               sh_info:    0

sh_addralign: 0x4

Section Header[3]:  sh_name: .dynsym   ;该section保存着动态符号表

sh_addr:      0x80501ec       sh_flags:   [ SHF_ALLOC ]

sh_size:      0x200           sh_type:    [ SHT_DYNSYM ]

sh_offset:    0x1ec           sh_entsize: 0x10

sh_link:      4               sh_info:    1

sh_addralign: 0x4

Section Header[4]:  sh_name: .dynstr   ;该section保存着动态连接时需要的字符串

sh_addr:      0x80503ec       sh_flags:   [ SHF_ALLOC  SHF_STRINGS ]

sh_size:      0x11a           sh_type:    [ SHT_STRTAB ]

sh_offset:    0x3ec           sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x1

Section Header[5]:  sh_name: .SUNW_version   ;该section是SUN扩展的,保存版本信息

sh_addr:      0x8050508       sh_flags:   [ SHF_ALLOC ]

sh_size:      0x20            sh_type:    [ SHT_SUNW_verneed ]

sh_offset:    0x508           sh_entsize: 0

sh_link:      4               sh_info:    1

sh_addralign: 0x4

Section Header[6]:  sh_name: .rel.got   ;该section保存着.got section中部分符号的重定位信息

sh_addr:      0x8050528       sh_flags:   [ SHF_ALLOC  SHF_INFO_LINK ]

sh_size:      0x18            sh_type:    [ SHT_REL ]

sh_offset:    0x528           sh_entsize: 0x8

sh_link:      3               sh_info:    14

sh_addralign: 0x4

Section Header[7]:  sh_name: .rel.bss   ;该section保存着.bss section中部分符号的重定位信息

sh_addr:      0x8050540       sh_flags:   [ SHF_ALLOC  SHF_INFO_LINK ]

sh_size:      0x8             sh_type:    [ SHT_REL ]

sh_offset:    0x540           sh_entsize: 0x8

sh_link:      3               sh_info:    22

sh_addralign: 0x4

Section Header[8]:  sh_name: .rel.plt   ;该section保存着.plt section中部分符号的重定位信息

sh_addr:      0x8050548       sh_flags:   [ SHF_ALLOC  SHF_INFO_LINK ]

sh_size:      0x38            sh_type:    [ SHT_REL ]

sh_offset:    0x548           sh_entsize: 0x8

sh_link:      3               sh_info:    9

sh_addralign: 0x4

Section Header[9]:  sh_name: .plt   ;该section保存着过程连接表(Procedure Linkage Table)

sh_addr:      0x8050580       sh_flags:   [ SHF_ALLOC  SHF_EXECINSTR ]

sh_size:      0x80            sh_type:    [ SHT_PROGBITS ]

sh_offset:    0x580           sh_entsize: 0x10

sh_link:      0               sh_info:    0

sh_addralign: 0x4

Section Header[10]:  sh_name: .text   ;该section保存着程序的正文部分,即可执行指令

sh_addr:      0x8050600       sh_flags:   [ SHF_ALLOC  SHF_EXECINSTR ]

sh_size:      0x1ec           sh_type:    [ SHT_PROGBITS ]

sh_offset:    0x600           sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x4

Section Header[11]:  sh_name: .init    ;该section保存着可执行指令,它构成了进程的初始化代码

sh_addr:      0x80507ec       sh_flags:   [ SHF_ALLOC  SHF_EXECINSTR ]

sh_size:      0xd             sh_type:    [ SHT_PROGBITS ]

sh_offset:    0x7ec           sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x1

Section Header[12]:  sh_name: .fini    ;该section保存着可执行指令,它构成了进程的终止代码

sh_addr:      0x80507f9       sh_flags:   [ SHF_ALLOC  SHF_EXECINSTR ]

sh_size:      0x8             sh_type:    [ SHT_PROGBITS ]

sh_offset:    0x7f9           sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x1

Section Header[13]:  sh_name: .rodata   ;该section保存着只读数据

sh_addr:      0x8050804       sh_flags:   [ SHF_ALLOC ]

sh_size:      0x10            sh_type:    [ SHT_PROGBITS ]

sh_offset:    0x804           sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x4

Section Header[14]:  sh_name: .got   ;该section保存着全局的偏移量表

sh_addr:      0x8060814       sh_flags:   [ SHF_WRITE  SHF_ALLOC ]

sh_size:      0x34            sh_type:    [ SHT_PROGBITS ]

sh_offset:    0x814           sh_entsize: 0x4

sh_link:      0               sh_info:    0

sh_addralign: 0x4

Section Header[15]:  sh_name: .dynamic   ;该section保存着动态连接的信息

sh_addr:      0x8060848       sh_flags:   [ SHF_WRITE  SHF_ALLOC ]

sh_size:      0xb8            sh_type:    [ SHT_DYNAMIC ]

sh_offset:    0x848           sh_entsize: 0x8

sh_link:      4               sh_info:    0

sh_addralign: 0x4

Section Header[16]:  sh_name: .data      ;该sections保存着初始化了的数据

sh_addr:      0x8060900       sh_flags:   [ SHF_WRITE  SHF_ALLOC ]

sh_size:      0x10            sh_type:    [ SHT_PROGBITS ]

sh_offset:    0x900           sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x4

Section Header[17]:  sh_name: .ctors

sh_addr:      0x8060910       sh_flags:   [ SHF_WRITE  SHF_ALLOC ]

sh_size:      0x8             sh_type:    [ SHT_PROGBITS ]

sh_offset:    0x910           sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x4

Section Header[18]:  sh_name: .dtors

sh_addr:      0x8060918       sh_flags:   [ SHF_WRITE  SHF_ALLOC ]

sh_size:      0x8             sh_type:    [ SHT_PROGBITS ]

sh_offset:    0x918           sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x4

Section Header[19]:  sh_name: .eh_frame

sh_addr:      0x8060920       sh_flags:   [ SHF_WRITE  SHF_ALLOC ]

sh_size:      0x4             sh_type:    [ SHT_PROGBITS ]

sh_offset:    0x920           sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x4

Section Header[20]:  sh_name: .jcr

sh_addr:      0x8060924       sh_flags:   [ SHF_WRITE  SHF_ALLOC ]

sh_size:      0x4             sh_type:    [ SHT_PROGBITS ]

sh_offset:    0x924           sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x4

Section Header[21]:  sh_name: .data.rel.local

sh_addr:      0x8060928       sh_flags:   [ SHF_WRITE  SHF_ALLOC ]

sh_size:      0x4             sh_type:    [ SHT_PROGBITS ]

sh_offset:    0x928           sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x4

Section Header[22]:  sh_name: .bss   ;该sectiopn保存着未初始化的数据

sh_addr:      0x806092c       sh_flags:   [ SHF_WRITE  SHF_ALLOC ]

sh_size:      0x2c            sh_type:    [ SHT_NOBITS ]   ;指示不占据ELF空间sh_size是内存大小

sh_offset:    0x92c           sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x4

Section Header[23]:  sh_name: .symtab   ;该section保存着一个符号表

sh_addr:      0               sh_flags:   0

sh_size:      0x540           sh_type:    [ SHT_SYMTAB ]

sh_offset:    0x92c           sh_entsize: 0x10

sh_link:      24              sh_info:    53

sh_addralign: 0x4

Section Header[24]:  sh_name: .strtab   ;该section保存着字符串表

sh_addr:      0               sh_flags:   [ SHF_STRINGS ]

sh_size:      0x20b           sh_type:    [ SHT_STRTAB ]

sh_offset:    0xe6c           sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x1

Section Header[25]:  sh_name: .comment   ;该section保存着版本控制信息

sh_addr:      0               sh_flags:   0

sh_size:      0x24d           sh_type:    [ SHT_PROGBITS ]

sh_offset:    0x1077          sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x1

Section Header[26]:  sh_name: .stab.index

sh_addr:      0               sh_flags:   0

sh_size:      0x24            sh_type:    [ SHT_PROGBITS ]

sh_offset:    0x12c4          sh_entsize: 0xc

sh_link:      0               sh_info:    0

sh_addralign: 0x4

Section Header[27]:  sh_name: .shstrtab   ;该section保存着section名称

sh_addr:      0               sh_flags:   [ SHF_STRINGS ]

sh_size:      0xdc            sh_type:    [ SHT_STRTAB ]

sh_offset:    0x12e8          sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x1

Section Header[28]:  sh_name: .stab.indexstr

sh_addr:      0               sh_flags:   0

sh_size:      0x1c0           sh_type:    [ SHT_STRTAB ]

sh_offset:    0x13c4          sh_entsize: 0

sh_link:      0               sh_info:    0

sh_addralign: 0x1

Interpreter:

/usr/lib/ld.so.1

Version Needed Section:  .SUNW_version

file                        version

libc.so.1                   SYSVABI_1.3

Symbol Table:  .dynsym                     ;动态解析和链接所需的符号表

index    value       size     type bind oth ver shndx       name

[0]  0x00000000 0x00000000  NOTY LOCL  D    0 UNDEF

[1]  0x080507ec 0x0000000d  FUNC GLOB  D    0 .init       _init

[2]  0x08050804 0x00000004  OBJT GLOB  D    0 .rodata     _lib_version

[3]  0x08050580 0x00000000  OBJT GLOB  D    0 .plt        _PROCEDURE_LINKAGE_TABLE_

[4]  0x08050600 0x00000075  FUNC GLOB  D    0 .text       _start

[5]  0x08060900 0x00000000  OBJT GLOB  D    0 .data       __dso_handle

[6]  0x08060848 0x00000000  OBJT GLOB  D    0 .dynamic    _DYNAMIC

[7]  0x00000000 0x00000000  NOTY WEAK  D    0 UNDEF       __deregister_frame_info_bases

[8]  0x08050814 0x00000000  OBJT GLOB  D    0 .rodata     _etext

[9]  0x08060958 0x00000000  OBJT GLOB  D    0 .bss        _end

[10]  0x08050590 0x00000000  FUNC WEAK  D    0 UNDEF       _cleanup

[11]  0x08050675 0x00000001  FUNC WEAK  D    0 .text       _mcount

[12]  0x08060904 0x00000004  OBJT GLOB  D    0 .data       i

[13]  0x08060908 0x00000004  OBJT GLOB  D    0 .data       j

[14]  0x0806090c 0x00000004  OBJT GLOB  D    0 .data       k

[15]  0x08060948 0x00000004  OBJT GLOB  D    0 .bss        l

[16]  0x08060954 0x00000004  OBJT GLOB  D    0 .bss        _environ

[17]  0x08060814 0x00000000  OBJT GLOB  D    0 .got        _GLOBAL_OFFSET_TABLE_

[18]  0x0806094c 0x00000004  OBJT GLOB  D    0 .bss        m

[19]  0x0806092c 0x00000000  OBJT GLOB  D    0 .data.rel.l _edata

[20]  0x08060954 0x00000004  OBJT WEAK  D    0 .bss        environ

[21]  0x080507f9 0x00000008  FUNC GLOB  D    0 .fini       _fini

[22]  0x080505a0 0x00000000  FUNC GLOB  D    0 UNDEF       atexit

[23]  0x08060950 0x00000004  OBJT GLOB  D    0 .bss        n

[24]  0x08050808 0x00000004  OBJT GLOB  D    0 .rodata     o

[25]  0x0805080c 0x00000004  OBJT GLOB  D    0 .rodata     p

[26]  0x00000000 0x00000000  NOTY WEAK  D    0 UNDEF       _Jv_RegisterClasses

[27]  0x08050810 0x00000004  OBJT GLOB  D    0 .rodata     q

[28]  0x080505b0 0x00000000  FUNC GLOB  D    0 UNDEF       __fpstart

[29]  0x08050753 0x00000065  FUNC GLOB  D    0 .text       main

[30]  0x00000000 0x00000000  NOTY WEAK  D    0 UNDEF       __register_frame_info_bases

[31]  0x080505c0 0x00000000  FUNC GLOB  D    0 UNDEF       exit

Symbol Table:  .symtab                     ;程序链接所需的符号表

index    value       size     type bind oth ver shndx       name

[0]  0x00000000 0x00000000  NOTY LOCL  D    0 UNDEF

[1]  0x00000000 0x00000000  FILE LOCL  D    0 ABS         test5

[2]  0x080500d4 0x00000000  SECT LOCL  D    0 .interp

[3]  0x080500e8 0x00000000  SECT LOCL  D    0 .hash

[4]  0x080501ec 0x00000000  SECT LOCL  D    0 .dynsym

[5]  0x080503ec 0x00000000  SECT LOCL  D    0 .dynstr

[6]  0x08050508 0x00000000  SECT LOCL  D    0 .SUNW_versi

[7]  0x08050528 0x00000000  SECT LOCL  D    0 .rel.got

[8]  0x08050540 0x00000000  SECT LOCL  D    0 .rel.bss

[9]  0x08050548 0x00000000  SECT LOCL  D    0 .rel.plt

[10]  0x08050580 0x00000000  SECT LOCL  D    0 .plt

[11]  0x08050600 0x00000000  SECT LOCL  D    0 .text

[12]  0x080507ec 0x00000000  SECT LOCL  D    0 .init

[13]  0x080507f9 0x00000000  SECT LOCL  D    0 .fini

[14]  0x08050804 0x00000000  SECT LOCL  D    0 .rodata

[15]  0x08060814 0x00000000  SECT LOCL  D    0 .got

[16]  0x08060848 0x00000000  SECT LOCL  D    0 .dynamic

[17]  0x08060900 0x00000000  SECT LOCL  D    0 .data

[18]  0x08060910 0x00000000  SECT LOCL  D    0 .ctors

[19]  0x08060918 0x00000000  SECT LOCL  D    0 .dtors

[20]  0x08060920 0x00000000  SECT LOCL  D    0 .eh_frame

[21]  0x08060924 0x00000000  SECT LOCL  D    0 .jcr

[22]  0x08060928 0x00000000  SECT LOCL  D    0 .data.rel.l

[23]  0x0806092c 0x00000000  SECT LOCL  D    0 .bss

[24]  0x00000000 0x00000000  SECT LOCL  D    0 .symtab

[25]  0x00000000 0x00000000  SECT LOCL  D    0 .strtab

[26]  0x00000000 0x00000000  SECT LOCL  D    0 .comment

[27]  0x00000000 0x00000000  SECT LOCL  D    0 .stab.index

[28]  0x00000000 0x00000000  SECT LOCL  D    0 .shstrtab

[29]  0x00000000 0x00000000  SECT LOCL  D    0 .stab.index

[30]  0x08050000 0x00000000  OBJT LOCL  D    0 .interp     _START_

[31]  0x08060958 0x00000000  OBJT LOCL  D    0 .bss        _END_

[32]  0x00000000 0x00000000  FILE LOCL  D    0 ABS         crt1.s

[33]  0x00000000 0x00000000  FILE LOCL  D    0 ABS         crti.s

[34]  0x00000000 0x00000000  FILE LOCL  D    0 ABS         values-Xa.c

[35]  0x00000000 0x00000000  FILE LOCL  D    0 ABS         crtstuff.c

[36]  0x08060910 0x00000000  OBJT LOCL  D    0 .ctors      __CTOR_LIST__

[37]  0x08060918 0x00000000  OBJT LOCL  D    0 .dtors      __DTOR_LIST__

[38]  0x08060920 0x00000000  OBJT LOCL  D    0 .eh_frame   __EH_FRAME_BEGIN__

[39]  0x08060924 0x00000000  OBJT LOCL  D    0 .jcr        __JCR_LIST__

[40]  0x08060928 0x00000000  OBJT LOCL  D    0 .data.rel.l p.0

[41]  0x0806092c 0x00000001  OBJT LOCL  D    0 .bss        completed.1

[42]  0x08050678 0x00000000  FUNC LOCL  D    0 .text       __do_global_dtors_aux

[43]  0x08060930 0x00000018  OBJT LOCL  D    0 .bss        object.2

[44]  0x080506e4 0x00000000  FUNC LOCL  D    0 .text       frame_dummy

[45]  0x00000000 0x00000000  FILE LOCL  D    0 ABS         test5.c

[46]  0x00000000 0x00000000  FILE LOCL  D    0 ABS         crtstuff.c

[47]  0x08060914 0x00000000  OBJT LOCL  D    0 .ctors      __CTOR_END__

[48]  0x0806091c 0x00000000  OBJT LOCL  D    0 .dtors      __DTOR_END__

[49]  0x08060920 0x00000000  OBJT LOCL  D    0 .eh_frame   __FRAME_END__

[50]  0x08060924 0x00000000  OBJT LOCL  D    0 .jcr        __JCR_END__

[51]  0x080507b8 0x00000000  FUNC LOCL  D    0 .text       __do_global_ctors_aux

[52]  0x00000000 0x00000000  FILE LOCL  D    0 ABS         crtn.o

[53]  0x080507ec 0x0000000d  FUNC GLOB  D    0 .init       _init

[54]  0x08050804 0x00000004  OBJT GLOB  D    0 .rodata     _lib_version

[55]  0x08050580 0x00000000  OBJT GLOB  D    0 .plt        _PROCEDURE_LINKAGE_TABLE_

[56]  0x08050600 0x00000075  FUNC GLOB  D    0 .text       _start

[57]  0x08060900 0x00000000  OBJT GLOB  D    0 .data       __dso_handle

[58]  0x08060848 0x00000000  OBJT GLOB  D    0 .dynamic    _DYNAMIC

[59]  0x00000000 0x00000000  NOTY WEAK  D    0 UNDEF       __deregister_frame_info_bases

[60]  0x08050814 0x00000000  OBJT GLOB  D    0 .rodata     _etext

[61]  0x08060958 0x00000000  OBJT GLOB  D    0 .bss        _end

[62]  0x08050590 0x00000000  FUNC WEAK  D    0 UNDEF       _cleanup

[63]  0x08050675 0x00000001  FUNC WEAK  D    0 .text       _mcount

[64]  0x08060904 0x00000004  OBJT GLOB  D    0 .data       i

[65]  0x08060908 0x00000004  OBJT GLOB  D    0 .data       j

[66]  0x0806090c 0x00000004  OBJT GLOB  D    0 .data       k

[67]  0x08060948 0x00000004  OBJT GLOB  D    0 .bss        l

[68]  0x08060954 0x00000004  OBJT GLOB  D    0 .bss        _environ

[69]  0x08060814 0x00000000  OBJT GLOB  D    0 .got        _GLOBAL_OFFSET_TABLE_

[70]  0x0806094c 0x00000004  OBJT GLOB  D    0 .bss        m

[71]  0x0806092c 0x00000000  OBJT GLOB  D    0 .data.rel.l _edata

[72]  0x08060954 0x00000004  OBJT WEAK  D    0 .bss        environ

[73]  0x080507f9 0x00000008  FUNC GLOB  D    0 .fini       _fini

[74]  0x080505a0 0x00000000  FUNC GLOB  D    0 UNDEF       atexit

[75]  0x08060950 0x00000004  OBJT GLOB  D    0 .bss        n

[76]  0x08050808 0x00000004  OBJT GLOB  D    0 .rodata     o

[77]  0x0805080c 0x00000004  OBJT GLOB  D    0 .rodata     p

[78]  0x00000000 0x00000000  NOTY WEAK  D    0 UNDEF       _Jv_RegisterClasses

[79]  0x08050810 0x00000004  OBJT GLOB  D    0 .rodata     q

[80]  0x080505b0 0x00000000  FUNC GLOB  D    0 UNDEF       __fpstart

[81]  0x08050753 0x00000065  FUNC GLOB  D    0 .text       main

[82]  0x00000000 0x00000000  NOTY WEAK  D    0 UNDEF       __register_frame_info_bases

[83]  0x080505c0 0x00000000  FUNC GLOB  D    0 UNDEF       exit

Hash Section:  .hash

bucket    symndx    name

0  [1]         _init

1  [2]         _lib_version

[3]         _PROCEDURE_LINKAGE_TABLE_

2  [4]         _start

[5]         __dso_handle

4  [6]         _DYNAMIC

9  [7]         __deregister_frame_info_bases

[8]         _etext

10  [9]         _end

12  [10]        _cleanup

[11]        _mcount

[12]        i

13  [13]        j

14  [14]        k

15  [15]        l

16  [16]        _environ

[17]        _GLOBAL_OFFSET_TABLE_

[18]        m

[19]        _edata

[20]        environ

17  [21]        _fini

[22]        atexit

[23]        n

18  [24]        o

19  [25]        p

20  [26]        _Jv_RegisterClasses

[27]        q

25  [28]        __fpstart

26  [29]        main

29  [30]        __register_frame_info_bases

[31]        exit

13  buckets contain        0 symbols

10  buckets contain        1 symbols

5  buckets contain        2 symbols

2  buckets contain        3 symbols

1  buckets contain        5 symbols

31  buckets               31 symbols (globals)

Global Offset Table: 13 entries

ndx     addr             value           reloc                     addend            symbol

[00000]  08060814  08060848 R_386_NONE         00000000

[00001]  08060818  00000000 R_386_NONE         00000000

[00002]  0806081c  00000000 R_386_NONE         00000000

[00003]  08060820  08050596 R_386_JMP_SLOT     00000000 _cleanup

[00004]  08060824  080505a6 R_386_JMP_SLOT     00000000 atexit

[00005]  08060828  080505b6 R_386_JMP_SLOT     00000000 __fpstart

[00006]  0806082c  080505c6 R_386_JMP_SLOT     00000000 exit

[00007]  08060830  00000000 R_386_GLOB_DAT     00000000 __deregister_frame_info_bases

[00008]  08060834  080505d6 R_386_JMP_SLOT     00000000 __deregister_frame_info_bases

[00009]  08060838  00000000 R_386_GLOB_DAT     00000000 __register_frame_info_bases

[00010]  0806083c  00000000 R_386_GLOB_DAT     00000000 _Jv_RegisterClasses

[00011]  08060840  080505e6 R_386_JMP_SLOT     00000000 _Jv_RegisterClasses

[00012]  08060844  080505f6 R_386_JMP_SLOT     00000000 __register_frame_info_bases

Relocation: .rel.got

type                       offset              section   with respect to

R_386_GLOB_DAT             0x8060830             .rel.got       __deregister_frame_info_bases

R_386_GLOB_DAT             0x8060838             .rel.got       __register_frame_info_bases

R_386_GLOB_DAT             0x806083c             .rel.got       _Jv_RegisterClasses

Relocation: .rel.bss

type                       offset              section   with respect to

R_386_COPY                 0x8060954             .rel.bss       _environ

Relocation: .rel.plt

type                       offset              section   with respect to

R_386_JMP_SLOT             0x8060820             .rel.plt       _cleanup

R_386_JMP_SLOT             0x8060824             .rel.plt       atexit

R_386_JMP_SLOT             0x8060828             .rel.plt       __fpstart

R_386_JMP_SLOT             0x806082c             .rel.plt       exit

R_386_JMP_SLOT             0x8060834             .rel.plt       __deregister_frame_info_bases

R_386_JMP_SLOT             0x8060840             .rel.plt       _Jv_RegisterClasses

R_386_JMP_SLOT             0x8060844             .rel.plt       __register_frame_info_bases

Dynamic Section:  .dynamic

index  tag               value

[0]  NEEDED           0x104             libc.so.1

[1]  INIT             0x80507ec

[2]  FINI             0x80507f9

[3]  HASH             0x80500e8

[4]  STRTAB           0x80503ec

[5]  STRSZ            0x11a

[6]  SYMTAB           0x80501ec

[7]  SYMENT           0x10

[8]  CHECKSUM         0x6a10

[9]  VERNEED          0x8050508

[10]  VERNEEDNUM       0x1

[11]  PLTRELSZ         0x38

[12]  PLTREL           0x11

[13]  JMPREL           0x8050548

[14]  REL              0x8050528

[15]  RELSZ            0x58

[16]  RELENT           0x8

[17]  DEBUG            0

[18]  FEATURE_1        0x1               [ PARINIT ]

[19]  FLAGS            0                 0

[20]  FLAGS_1          0                 0

[21]  PLTGOT           0x8060814

利用elfdump可以查看ELF文件格式的详细信息,可以在符号表.dynsym和.symtab中找到程序中定义的全局变量和全局常量:

i,j,k在.data section中

l,m,n在.bss section中

o,p,q在.rodata section中

概念:ELF(Executable and Linking Format)可执行连接格式

ELF格式是UNIX系统实验室(USL)作为应用程序二进制接口(Application Binary Interface,ABI)而开发和发布的。

目前,ELF格式是Unix/Linux平台上应用最广泛的二进制工业标准之一

下图从不同视角给出了ELF文件的一般格式:

Linking 视角                       Execution视角

============                      ==============

ELF header                        ELF header

Program header table (optional)   Program header table

Section 1                         Segment 1

...                               Segment 2

Section n                         ...

Section header table              Section header table (optional)

图 3-2 ELF文件格式摘自 EXECUTABLE AND LINKABLE FORMAT (ELF)

可以根据test5 ELF文件的Program header table和Section header table中文件偏移量的信息描绘出test5的内容:

entry name      起始文件偏移+实际大小=下个entry起始偏移

-------------------------------------------------------

ELF header                  0x0+0x34=0x34

Program header              0x34+0xa0=0xd4

Section 1  .interp          0xd4+0x11=0xe5

000                         0xe5+0x3=0xe8

Section 2  .hash            0xe8+0x104=0x1ec

Section 3  .dynsym          0x1ec+0x200=0x3ec

Section 4  .dynstr          0x3ec+0x11a=0x506

00                          0x506+0x2=0x508

Section 5  .SUNW_version    0x508+0x20=0x528

Section 6  .rel.got         0x528+0x18=0x540

Section 7  .rel.bss         0x540+0x8=0x548

Section 8  .rel.plt         0x548+0x38=0x580

Section 9  .plt             0x580+0x80=0x600

Section 10 .text            0x600+0x1ec=0x7ec

Section 11 .init            0x7ec+0xd=0x7f9

Section 12 .fini            0x7f9+0x8=0x801

000                         0x801+0x3=0x804

Section 13 .rodata          0x804+0x10=0x814

Section 14 .got             0x814+0x34=0x848

Section 15 .dynamic         0x848+0xb8=900

Section 16 .data            0x900+0x10=0x910

Section 17 .ctors           0x910+0x8=0x918

Section 18 .dtors           0x918+0x8=0x920

Section 19 .eh_frame        0x920+0x4=0x924

Section 20 .jcr             0x924+0x4=0x928

Section 21 .data.rel.local  0x928+0x4=0x92c

Section 22 .bss          0x92c+0x0=0x958

Section 23 .symtab          0x92c+0x540=0xe6c

Section 24 .strtab          0xe6c+0x20b=1077

Section 25 .comment         0x1077+0x24d=0x12c4

Section 26 .stab.index      0x12c4+0x24=0x12e8

Section 27 .shstrtab        0x12e8+0xdc=0x13c4

Section 28 .stab.indexstr   0x13c4+0x1c0=0x1584

Section header table        0x1584+0x488=0x1a0c ;29x40=1160=0x488这是根据Elf header信息算得

------------------------------------------------------

图 3-3 test5的ELF文件格式

# ls -al test5

-rwxr-xr-x    1 root     other        6668 2004-12-19 06:56 test5

可以看到test5的大小是0x1a0c字节,即6688字节。

可以看到,.bss section用于保存未初始化的全局变量,因此不占据ELF文件空间;

ELF文件装入时,会按照Section header table 22中的.bss的相关属性,为.bss映射相应大小的内存空间,并初始化为0

ELF文件的Program header table描述了如何将ELF装入内存:

Program Header 2 描述了用户代码段的起始地址和大小: 0x8050000+0x814=0x8050814

Program Header 3 描述了用户数据段的起始地址和大小: 0x8060814+0x144=0x8060958

问题:为何前面pmap得到的结果是数据段从0x8060000开始,而ELF文件的Program Header 3却是从0x8060814开始?

如果查一下ELF文件的格式规范的话,就能找到答案:

Program Header[2]:

p_vaddr:      0x8050000       p_flags:    [ PF_X  PF_R ]

p_paddr:      0               p_type:     [ PT_LOAD ]

p_filesz:     0x814           p_memsz:    0x814

p_offset:     0               p_align:    0x10000   ;指定64K页对齐

Program Header[3]:

p_vaddr:      0x8060814       p_flags:    [ PF_X  PF_W  PF_R ]

p_paddr:      0               p_type:     [ PT_LOAD ]

p_filesz:     0x118           p_memsz:    0x144

p_offset:     0x814           p_align:    0x10000   ;指定64K页对齐

p_align指定了映射代码段和数据段的时候,必须按照64K页对齐的方式,即起始映射地址必须以0000结尾。

代码段的起始地址正好满足该条件,如果数据段不考虑页对齐的话,应该紧跟代码段的下一个字节即0x8050814开始。

但是正因为64K页对齐的缘故,只能从最接近0x8050814的64K页对齐地址0x8060000开始。

而实际上,在对ELF进行内存映射时,是按页为单位进行映射的,test5的大小是0x1a0c,不足1页大小,代码段和数据段都是在第1页。

映射发生时,先映射这第1页到0x8050000的代码段,属性read/exec;再映射这1页到0x8060000的数据段,属性 read/write/exec。

这样,实际上的数据段起始地址就是0x8060000+0x814=0x8060814

问题:为什么要页对齐 page align?

首先,内存映射是以页为最小单位的,这是因为Solaris的内存管理是页式内存管理(目前大多数现代OS都是如此);

其次,代码段和数据段因为有不同的权限要求(代码段要求只读),因此必须进行2次映射;

最后,就是效率的要求;

尽管ELF文件的映射是solaris内核中seg_vn段驱动程序来完成的,但仍可以通过系统调用mmap(2)来学习基本的内存映射常识。

对于其它系统,如Linux情况也类似。

那为何在本例中是要求64K页对齐呢?答案是:这也是Solaris在Sparc上的页对齐要求

概念:ELF文件loading

根据Program header table及section header table描绘出test5代码段及数据段的内部情况就很容易了:

entry name          起始地址+实际大小=下个entry起始地址

=================User Text============================ 0x8050000

ELF header                  0x8050000+0x34=0x8050034

Program header              0x8050034+0xa0=0x80500d4

Section 1  .interp          0x80500d4+0x11=0x80500e5

0                           0x80500e5+0x3=0x80500e8; 3字节0填充Section 2  .hash            0x80500e8+0x104=0x80501ec

Section 3  .dynsym          0x80501ec+0x200=0x80503ec

Section 4  .dynstr          0x80503ec+0x11a=0x8050506

0                           0x8050506+0x2=0x8050508; 2字节0填充Section 5  .SUNW_version    0x8050508+0x20=0x8050528

Section 6  .rel.got         0x8050528+0x18=0x8050540

Section 7  .rel.bss         0x8050540+0x8=0x8050548

Section 8  .rel.plt         0x8050548+0x38=0x8050580

Section 9  .plt             0x8050580+0x80=0x8050600

Section 10 .text            0x8050600+0x1ec=0x80507ec

Section 11 .init            0x80507ec+0xd=0x80507f9

Section 12 .fini            0x80507f9+0x8=0x8050801

0                           0x8050801+0x3=0x8050804; 3字节0填充Section 13 .rodata       0x8050804+0x10=0x8050814;o,p,q在代码段的. rodata section中

-----------------------------------------------------

Section 14 .got             0x8050814+0x34=0x8050848;这是代码段的第一页也是最后一页,

Section 15 .dynamic         0x8050848+0xb8=8050900  ;因此数据段内容会追加到代码段最后一页末尾

Section 16 .data            0x8050900+0x10=0x8050910

Section 17 .ctors           0x8050910+0x8=0x8050918

Section 18 .dtors           0x8050918+0x8=0x8050920

Section 19 .eh_frame        0x8050920+0x4=0x8050924

Section 20 .jcr             0x8050924+0x4=0x8050928

Section 21 .data.rel.local  0x8050928+0x4=0x805092c

Section 22 .bss             0x805092c+0x2c=0x8050958

pending data                0x8050958+0x6a8=8051000         ;页末是0x6a8字节填充

======================================================

no mapping

=================User Data============================ 0x8060000

ELF header                  0x8060000+0x34=0x8060034;这是数据段的最后一页也是第一页,

Program header              0x8060034+0xa0=0x80600d4   ;因此代码段内容会追加到数据段第一页之前

Section 1  .interp          0x80600d4+0x11=0x80600e5

0                           0x80600e5+0x3=0x80600e8; 3字节0填充

Section 2  .hash            0x80600e8+0x104=0x80601ec

Section 3  .dynsym          0x80601ec+0x200=0x80603ec

Section 4  .dynstr          0x80603ec+0x11a=0x8060506

0                           0x8060506+0x2=0x8060508; 2字节0填充

Section 5  .SUNW_version    0x8060508+0x20=0x8060528

Section 6  .rel.got         0x8060528+0x18=0x8060540

Section 7  .rel.bss         0x8060540+0x8=0x8060548

Section 8  .rel.plt         0x8060548+0x38=0x8060580

Section 9  .plt             0x8060580+0x80=0x8060600

Section 10 .text            0x8060600+0x1ec=0x80607ec

Section 11 .init            0x80607ec+0xd=0x80607f9

Section 12 .fini            0x80607f9+0x8=0x8060801

0                           0x8060801+0x3=0x8060804; 3字节0填充

Section 13 .rodata          0x8060804+0x10=0x8060814

-----------------------------------------------------

Section 14 .got             0x8060814+0x34=0x8060848

Section 15 .dynamic         0x8060848+0xb8=8060900

Section 16 .data         0x8060900+0x10=0x8060910;i,j,k在数据段的.data section中Section 17 .ctors           0x8060910+0x8=0x8060918

Section 18 .dtors           0x8060918+0x8=0x8060920

Section 19 .eh_frame        0x8060920+0x4=0x8060924

Section 20 .jcr             0x8060924+0x4=0x8060928

Section 21 .data.rel.local  0x8060928+0x4=0x806092c

Section 22 .bss          0x806092c+0x2c=0x8060958; l,m,n在数据段的.bss section中

0                           0x8060958+0x6a8=8061000; 页末是0x6a8字节0填充

=======================================================

图 3-4 test5的代码段和数据段内部结构

以下各section因为section header table中的sh_flags不包含SHF_ALLOC,因此不会被映射到内存:

Section 23 .symtab

符号表,不映射到内存,strip命令可以去除

Section 24 .strtab

字符串表,主要保存着和 .symtab的名字字符串以及其它字符串,不映射到内存,strip命令可去除

Section 25 .comment

保存着该二进制程序相关的信息,格式内容决定于二进制程序本身,不映射到内存,strip命令保留该section

Section 26 .stab.index

保存着该二进制程序相关的信息,格式内容决定于二进制程序本身,不映射到内存,strip命令可以去除

Section 27 .shstrtab

字符串表,只保存section name,不映射到内存,strip命令保留该section

Section 28 .stab.indexstr

字符串表,不映射到内存,strip命令可以去除

有了图 3-4,就能清楚的找到全局变量和全局常量的在数据段和代码段的精确位置,也就能回答前面提出的问题:

全局常量o,p,q属于代码段的.rodata section,这个section因为属于代码段而具有只读属性,用于保存只读数据

全局变量i,j,k属于数据段的.data section,用于保存有初值的全局变量,这个section同时在ELF文件和内存中占据空间

全局变量l,m,n属于代码段的.bss section,用于保存未初始化的全局变量,这个section占据内存空间而不占据ELF文件空间

由于分属于几个不同的section,地址空间必定不连续了

下面把ELF文件的装入归纳如下:

* 第一个代码段页面包含了 ELF header、Program header table以及其他信息

* 最后的代码段页末尾追加一个数据段开始的拷贝

* 第一个数据段页面前有一个代码段结束的拷贝

* 最后的数据段页面也许会包含与正在运行的进程无关的文件信息

2. ELF文件装载的验证

ELF文件本身的格式可以直接用工具观察二进制文件,下面的命令可以观察到.comment section的相关内容:

bash-2.05# od -A x -c -j 0x1077 -N 0x24d test5

0000000   G   N   U       C       c   r   t   1   .   s  /0   a   s   :

0000010       F   o   r   t   e       D   e   v   e   l   o   p   e   r

0000020       7       C   o   m   p   i   l   e   r       C   o   m   m

0000030   o   n       7   .   0       I   A   3   2   -   i   t   e   a

0000040   m       2   0   0   1   /   1   2   /   1   2  /0   G   N   U

0000050       C       c   r   t   i   .   s  /0   a   s   :       F   o

0000060   r   t   e       D   e   v   e   l   o   p   e   r       7

0000070   C   o   m   p   i   l   e   r       C   o   m   m   o   n

0000080   7   .   0       I   A   3   2   -   i   t   e   a   m       2

0000090   0   0   1   /   1   2   /   1   2  /0  /0   @   (   #   )   S

00000a0   u   n   O   S       5   .   9       G   e   n   e   r   i   c

00000b0   _   1   1   2   2   3   4   -   0   3       N   o   v   e   m

00000c0   b   e   r       2   0   0   2  /0   G   C   C   :       (   G

00000d0   N   U   )       3   .   3   .   2  /0   a   s   :       F   o

00000e0   r   t   e       D   e   v   e   l   o   p   e   r       7

00000f0   C   o   m   p   i   l   e   r       C   o   m   m   o   n

0000100   7   .   0       I   A   3   2   -   i   t   e   a   m       2

0000110   0   0   1   /   1   2   /   1   2  /0   G   C   C   :       (

0000120   G   N   U   )       3   .   3   .   2  /0   a   s   :       F

0000130   o   r   t   e       D   e   v   e   l   o   p   e   r       7

0000140       C   o   m   p   i   l   e   r       C   o   m   m   o   n

0000150       7   .   0       I   A   3   2   -   i   t   e   a   m

0000160   2   0   0   1   /   1   2   /   1   2  /0   G   C   C   :

0000170   (   G   N   U   )       3   .   3   .   2  /0   a   s   :

0000180   F   o   r   t   e       D   e   v   e   l   o   p   e   r

0000190   7       C   o   m   p   i   l   e   r       C   o   m   m   o

00001a0   n       7   .   0       I   A   3   2   -   i   t   e   a   m

00001b0       2   0   0   1   /   1   2   /   1   2  /0   G   N   U

00001c0   C       c   r   t   n   .   o  /0   a   s   :       F   o   r

00001d0   t   e       D   e   v   e   l   o   p   e   r       7       C

00001e0   o   m   p   i   l   e   r       C   o   m   m   o   n       7

00001f0   .   0       I   A   3   2   -   i   t   e   a   m       2   0

0000200   0   1   /   1   2   /   1   2  /0   l   d   :       S   o   f

0000210   t   w   a   r   e       G   e   n   e   r   a   t   i   o   n

0000220       U   t   i   l   i   t   i   e   s       -       S   o   l

0000230   a   r   i   s       L   i   n   k       E   d   i   t   o   r

0000240   s   :       5   .   9   -   1   .   2   7   6  /0

000024d

显然,.comment section的内容是编译器和链接器的版本信息。

观察ELF载入内存后的情况则需要该ELF程序的进程在系统中挂起,才能读到相关内容。

利用mdb可以查看装入内存中的test5的代码段和数据段值,下面就验证一下图3-4:

# mdb test5

Loading modules: [ libc.so.1 ]

> main::dis

main:                           pushl   %ebp

main+1:                         movl    %esp,%ebp

main+3:                         subl    $8,%esp

main+6:                         andl    $0xf0,%esp

main+9:                         movl    $0,%eax

main+0xe:                       subl    %eax,%esp

main+0x10:                      movl    $4,0x8060948

main+0x1a:                      movl    $5,0x806094c

main+0x24:                      movl    $6,0x8060950

main+0x2e:                      movl    0x8060908,%eax

main+0x33:                      addl    0x8060904,%eax

main+0x39:                      addl    0x806090c,%eax

main+0x3f:                      addl    0x8060948,%eax

main+0x45:                      addl    0x806094c,%eax

main+0x4b:                      addl    0x8060950,%eax

main+0x51:                      addl    0x8050808,%eax

main+0x57:                      addl    0x805080c,%eax

main+0x5d:                      addl    0x8050810,%eax

main+0x63:                      leave

main+0x64:                      ret

> main+0x2e:b            ;设置断点

> :r                     ;运行

mdb: stop at main+0x2e

mdb: target stopped at:

main+0x2e:      movl    0x8060908,%eax

> 0x8050000,0x4/naB      ;查看ELF header头4字节

0x8050000:

0x8050000:      7f

0x8050001:      45

0x8050002:      4c

0x8050003:      46

> 0x8050000,0x4/nac      ;查看ELF header头4字节

0x8050000:

0x8050000:

0x8050001:      E

0x8050002:      L

0x8050003:      F

> 0x80500d4,11/c         ;查看.interp section

0x80500d4:      /usr/lib/ld.so.1

> 0x8050600::dis         ;查看.text section的第一的过程,也是ELF的入口点

_start:                         pushl   $0

_start+2:                       pushl   $0

_start+4:                       movl    %esp,%ebp

_start+6:                       pushl   %edx

_start+7:                       movl    $0x8050590,%eax

_start+0xc:                     testl   %eax,%eax

_start+0xe:                     je      +0xf            <_start>

_start+0x10:                    pushl   $0x8050590

_start+0x15:                    call    -0x75

_start+0x1a:                    addl    $4,%esp

_start+0x1d:                    movl    $0x8060848,%eax

_start+0x22:                    testl   %eax,%eax

_start+0x24:                    je      +7              <_start>

_start+0x26:                    call    -0x86

_start+0x2b:                    pushl   $0x80507f9

_start+0x30:                    call    -0x90

_start+0x35:                    movl    +8(%ebp),%eax

_start+0x38:                    leal    +0x10(%ebp,%eax,4),%edx

_start+0x3c:                    movl    %edx,0x8060954

_start+0x42:                    andl    $0xf0,%esp

_start+0x45:                    subl    $4,%esp

_start+0x48:                    pushl   %edx

_start+0x49:                    leal    +0xc(%ebp),%edx

_start+0x4c:                    pushl   %edx

_start+0x4d:                    pushl   %eax

_start+0x4e:                    call    +0x19e          <_init>

_start+0x53:                    call    -0xa3

_start+0x58:                    call    +0xfb

_start+0x5d:                    addl    $0xc,%esp

_start+0x60:                    pushl   %eax

_start+0x61:                    call    -0xa1

_start+0x66:                    pushl   $0

_start+0x68:                    movl    $1,%eax

_start+0x6d:                    lcall   $7,$0

_start+0x74:                    hlt

> 0x8050804,0x4/nap      ;查看.rodata section,包含真正的o,p,q几个全局常量

_lib_version:

_lib_version:

_lib_version:   1

o:              7

p:              8

q:              9

> 0x8050900,0x4/nap      ;查看填充在代码段之后的.data section,这部分实际上是无效的数据

0x8050900:

0x8050900:      0

0x8050904:      1        ;全局变量i

0x8050908:      2        ;全局变量j

0x805090c:      3        ;全局变量k

> 0x8060000,0x4/naB      ;查看填充到数据段之前的ELF header头4字节,这部分实际是无效的

0x8060000:

0x8060000:      7f

0x8060001:      45

0x8060002:      4c

0x8060003:      46

> 0x8060000,0x4/nac      ;查看填充到数据段之前的ELF header头4字节,这部分实际是无效的

0x8060000:

0x8060000:

0x8060001:      E

0x8060002:      L

0x8060003:      F

> 0x8060804,0x4/nap      ;查看填充到数据段之前的.rodata section,这部分实际是无效的

0x8060804:

0x8060804:      1

0x8060808:      7        ;全局常量o

0x806080c:      8        ;全局常量p

0x8060810:      9        ;全局常量q

> 0x8060900,0x4/nap      ;查看.date section,包含真正的i,j,k几个全局变量

0x8060900:

0x8060900:      0

test5`i:        1

test5`j:        2

test5`k:        3

> 0x806092c,0xb/nap      ;查看.bss section,包含真正的l,m,n几个全局变量

test5`completed.1:

test5`completed.1:

test5`completed.1:              0

test5`object.2: 0

test5`object.2+4:               0

test5`object.2+8:               0

test5`object.2+0xc:             0

test5`object.2+0x10:            0

test5`object.2+0x14:            0

test5`l:        4

test5`m:        5

test5`n:        6

test5`environ:  0x8047e00

> 0x8060958,0x6a8/nab    ;查看数据段末尾追加的0x6a8字节数据,全部为0

0x8060958:

0x8060958:      0

0x8060959:      0

0x806095a:      0

.................

.................

0x8060ffe:      0

0x8060fff:      0

> 0x8060fff,2/nab        ;验证页边界数据是否映射

0x8060fff:

0x8060fff:      0

mdb: failed to read data from target: no mapping for address

0x8061000:

> 0x8050fff,2/nab        ;验证页边界数据是否映射

0x8050fff:

0x8050fff:      0151

mdb: failed to read data from target: no mapping for address

0x8051000:

3.小结

本次实验再次分析和验证了全局变量和全局常量在进程地址空间的位置以及和ELF文件的关系,并涉及到以下几方面的概念:

Process Address Space 进程地址空间

ELF EXECUTABLE AND LINKABLE FORMAT 可执行链接格式

Page align 页对齐

并且,利用Solaris提供的mdb,pmap,elfdump,od工具,直接观察到ELF文件的装载和格式。

AM335X的汇编语言与c语言,X86汇编语言学习手记 -- 汇编和C协同相关推荐

  1. c语言可以调用汇编语言吗,C语言与汇编语言混编方式

    C语言是目前非常流行的一种编程语言,除具有高级语言使用方便灵活.数据处理能力强.编程简单等优点外:还可实现汇编语言的大部分功能,如可直接对硬件进行操作.生成的目标代码质量较高等,而汇编语言没有高级语言 ...

  2. python是什么语言汇编语言吗-python语言属于汇编语言吗?_后端开发

    c语言主函数名是什么?_后端开发 C语言主函数名是main,main函数又称主函数,是程序执行的起点,如果有其他函数,则完成对其他函数的调用后再返回到主函数,最后由main函数结束整个程序. pyth ...

  3. c汇编语言实验,C语言与汇编语言编程实验

    1. 掌握在ADT IDE中编写汇编和C语言混合编程程序; 2. 掌握C语言和汇编语言相互调用的过程; 3. 掌握GNU C中内嵌汇编语言的编程方法. C语言与汇编语言编程实验 实验目的 1. 掌握在 ...

  4. 汇编语言调用c语言ads,ADS1.2 在汇编代码中调用C函数

    EDA365欢迎您登录! 您需要 登录 才可以下载或查看,没有帐号?注册 x , U) b) }+ U8 \" d/ v( \$ ~  T对于ARM体系来说,不同语言撰写的函数之间相互调用( ...

  5. c语言汇编混合编程方法,C语言和汇编语言混合编程方法

    摘要: C语言是一种高级的面向过程的开发语言,汇编语言是一种低级的面向机器的编程语言.两者在程序设计开发方面各有优劣,目前两者的混合编程得到了广泛的应用.本文通过具体的实例,说明了混合编程的基本方法, ...

  6. 【Android 逆向】x86 汇编 ( 使用 IDA 解析 x86 架构的动态库文件 | x86 汇编语言分析 )

    文章目录 一.x86 汇编语言分析 一.x86 汇编语言分析 在上一篇博客 [Android 逆向]x86 汇编 ( 使用 IDA 解析 x86 架构的动态库文件 | 使用 IDA 打开动态库文件 | ...

  7. 任务和特权级保护(五)——《x86汇编语言:从实模式到保护模式》读书笔记36

    任务和特权级保护(五)--<x86汇编语言:从实模式到保护模式>读书笔记36 修改后的代码,有需要的朋友可以去下载(c14_new文件夹).下载地址是: GitHub: https://g ...

  8. 程序的加载和执行(六)——《x86汇编语言:从实模式到保护模式》读书笔记26

    程序的加载和执行(六)--<x86汇编语言:从实模式到保护模式>读书笔记26 通过本文能学到什么? NASM的条件汇编 用NASM编译的时候,通过命令行选项定义宏 Makefile的条件语 ...

  9. 程序的加载和执行(五)——《x86汇编语言:从实模式到保护模式》读书笔记25

    程序的加载和执行(五)--<x86汇编语言:从实模式到保护模式>读书笔记25 前面几篇博文终于把代码分析完了.这篇就来说说代码的编译.运行和调试. 1.代码的编译及写入镜像文件 之前我们都 ...

最新文章

  1. 如何检查jQuery中是否已选中复选框?
  2. 近邻取样插值和其速度优化
  3. 个人开发者 android内购,【开发者账号】关于内购,协议税务的一些坑
  4. 用UE4创造开放世界:Kite 实时演示
  5. PowerPoint Storyboarding:Visual Studio 2012 RC带给开发者的秘密杀器
  6. 大型网站典型故障案例分析
  7. Servlet第二篇【Servlet调用图、Servlet细节、ServletConfig、ServletContext】
  8. CCF - 201403-3 - 命令行选项
  9. 静态变量加载时间,静态代码块加载时间
  10. 前端经常遇到的跨域问题几种解决方案
  11. oracle中的 expdp命令,Oracle 10G 数据泵中EXPDP命令行选项介绍
  12. ACL2021 | 知识对比:基于外部知识的图神经虚假新闻检测
  13. [Wireshark]_002_玩转数据包
  14. iOS tabbar 控制器基本使用
  15. Srm32 dwm1000 tdoa定位源码
  16. USB转串口,JLINK驱动安装(亲测有效)
  17. 电影评论分类:二分类问题(IMDB数据集)
  18. xp计算机用户名和密码忘记了怎么办,忘记XP的用户名和密码怎么办?
  19. 泰山OFFICE技术讲座:标点关系穷举研究-05
  20. Xftp/Xshell 要继续使用此程序,您必须应用最新的更新或使用新版本

热门文章

  1. 厉害了,用Python实现自动扫雷!
  2. 我敢打赌!你见过的所有程序员都是这样的!!!
  3. yum update 正在尝试其他镜像
  4. 操作系统学习:内存分页与中断
  5. Django博客系统(登录)
  6. python进程的注意点(进程之间不共享全局变量、主进程会等待所有的子进程执行结束再结束)
  7. cat在MATLAB中的应用
  8. python报错 ValueError: dictionary update sequence element #0 has length 1; 2 is require
  9. win10+centos7+Anaconda3+python+pytorch
  10. “计算机之子”winter:我的前端学习路线与方法