分享

解释一个ld.script资料

 TUBOSS 2014-09-02
GNU ld (GNU Binutils for Ubuntu) 2.20.1-system.20100303
  Supported emulations:
   elf_i386
   i386linux
   elf_x86_64
   elf_l1om
using internal linker script:
==================================================
/* Script for -z combreloc: combine and sort reloc sections */
OUTPUT_FORMAT("elf32-i386", "elf32-i386",
	      "elf32-i386")
OUTPUT_ARCH(i386)
ENTRY(_start)
SEARCH_DIR("/usr/i486-linux-gnu/lib32"); SEARCH_DIR("/usr/local/lib32"); 
SEARCH_DIR("/lib32"); SEARCH_DIR("/usr/lib32"); SEARCH_DIR("/usr/i486-linux-gnu/lib");
SEARCH_DIR("/usr/local/lib"); SEARCH_DIR("/lib"); SEARCH_DIR("/usr/lib");
SECTIONS
{
  /* Read-only sections, merged into text segment: */
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x08048000)); 
       . = SEGMENT_START("text-segment", 0x08048000) + SIZEOF_HEADERS;
  .interp         : { *(.interp) }
  .note.gnu.build-id : { *(.note.gnu.build-id) }
  .hash           : { *(.hash) }
  .gnu.hash       : { *(.gnu.hash) }
  .dynsym         : { *(.dynsym) }
  .dynstr         : { *(.dynstr) }
  .gnu.version    : { *(.gnu.version) }
  .gnu.version_d  : { *(.gnu.version_d) }
  .gnu.version_r  : { *(.gnu.version_r) }
  .rel.dyn        :
    {
      *(.rel.init)
      *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
      *(.rel.fini)
      *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
      *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*)
      *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
      *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
      *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
      *(.rel.ctors)
      *(.rel.dtors)
      *(.rel.got)
      *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
      *(.rel.ifunc)
    }
  .rel.plt        :
    {
      *(.rel.plt)
      PROVIDE_HIDDEN (__rel_iplt_start = .);
      *(.rel.iplt)
      PROVIDE_HIDDEN (__rel_iplt_end = .);
    }
  .init           :
  {
    KEEP (*(.init))
  } =0x90909090
  .plt            : { *(.plt) *(.iplt) }
  .text           :
  {
    *(.text.unlikely .text.*_unlikely)
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf32.em.  */
    *(.gnu.warning)
  } =0x90909090
  .fini           :
  {
    KEEP (*(.fini))
  } =0x90909090
  PROVIDE (__etext = .);
  PROVIDE (_etext = .);
  PROVIDE (etext = .);
  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
  .rodata1        : { *(.rodata1) }
  .eh_frame_hdr : { *(.eh_frame_hdr) }
  .eh_frame       : ONLY_IF_RO { KEEP (*(.eh_frame)) }
  .gcc_except_table   : ONLY_IF_RO { *(.gcc_except_table .gcc_except_table.*) }
  /* Adjust the address for the data segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1)); . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
  /* Exception handling  */
  .eh_frame       : ONLY_IF_RW { KEEP (*(.eh_frame)) }
  .gcc_except_table   : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
  /* Thread Local Storage sections  */
  .tdata	  : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
  .tbss		  : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  }
  .init_array     :
  {
     PROVIDE_HIDDEN (__init_array_start = .);
     KEEP (*(SORT(.init_array.*)))
     KEEP (*(.init_array))
     PROVIDE_HIDDEN (__init_array_end = .);
  }
  .fini_array     :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(.fini_array))
    KEEP (*(SORT(.fini_array.*)))
    PROVIDE_HIDDEN (__fini_array_end = .);
  }
  .ctors          :
  {
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don't want to include the .ctor section from
       the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  }
  .dtors          :
  {
    KEEP (*crtbegin.o(.dtors))
    KEEP (*crtbegin?.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
  }
  .jcr            : { KEEP (*(.jcr)) }
  .data.rel.ro : { *(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*) *(.data.rel.ro* .gnu.linkonce.d.rel.ro.*) }
  .dynamic        : { *(.dynamic) }
  .got            : { *(.got) *(.igot) }
  . = DATA_SEGMENT_RELRO_END (12, .);
  .got.plt        : { *(.got.plt)  *(.igot.plt) }
  .data           :
  {
    *(.data .data.* .gnu.linkonce.d.*)
    SORT(CONSTRUCTORS)
  }
  .data1          : { *(.data1) }
  _edata = .; PROVIDE (edata = .);
  __bss_start = .;
  .bss            :
  {
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   /* Align here to ensure that the .bss section occupies space up to
      _end.  Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections.
      FIXME: Why do we need it? When there is no .bss section, we don't
      pad the .data section.  */
   . = ALIGN(. != 0 ? 32 / 8 : 1);
  }
  . = ALIGN(32 / 8);
  . = ALIGN(32 / 8);
  _end = .; PROVIDE (end = .);
  . = DATA_SEGMENT_END (.);
  /* Stabs debugging sections.  */
  .stab          0 : { *(.stab) }
  .stabstr       0 : { *(.stabstr) }
  .stab.excl     0 : { *(.stab.excl) }
  .stab.exclstr  0 : { *(.stab.exclstr) }
  .stab.index    0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment       0 : { *(.comment) }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1 */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
  /* GNU DWARF 1 extensions */
  .debug_srcinfo  0 : { *(.debug_srcinfo) }
  .debug_sfnames  0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges  0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2 */
  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
  .debug_abbrev   0 : { *(.debug_abbrev) }
  .debug_line     0 : { *(.debug_line) }
  .debug_frame    0 : { *(.debug_frame) }
  .debug_str      0 : { *(.debug_str) }
  .debug_loc      0 : { *(.debug_loc) }
  .debug_macinfo  0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames  0 : { *(.debug_varnames) }
  /* DWARF 3 */
  .debug_pubtypes 0 : { *(.debug_pubtypes) }
  .debug_ranges   0 : { *(.debug_ranges) }
  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}

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


下面逐句解释。

 

OUTPUT_FORMAT("elf32-i386", "elf32-i386",
	      "elf32-i386")
OUTPUT_ARCH(i386)

OUTPUT_FORMAT 和 OUTPUT_ARCH 都是 ld 脚本的保留字命令。OUTPUT_FORMAT 说明输出二进制文件的格式。OUTPUT_ARCH 说明输出文件系统平台。

ENTRY(_start)

ENTRY 命令的作用是,将后面括号中的符号值设置成入口地址。入口地址(entry point)的定义是这样的──进程执行的第一条用户空间的指令在进程地址空间中的地址。

ld 有多种方法设置进程入口地址,通常它按以下顺序:(编号越前, 优先级越高)
1, ld 命令行的-e选项
2, 连接脚本的 ENTRY(SYMBOL) 命令
3, 如果定义了 start 符号, 使用 start 符号值
4, 如果存在 .text section, 使用 .text section 的第一字节的位置值
5, 使用值 0

 

SEARCH_DIR("/usr/i486-linux-gnu/lib32");

设置链接时搜寻库文件目录.

 

SECTIONS
{

然后,接下来是一大段的 SECTIONS,对应的右大括号直到脚本的末尾。
SECTIONS 命令告诉 ld 如何把输入文件的 sections 映射到输出文件的各个 section:即是如何将输入 section 合为输出 section;如何把输出 section 放入程序地址空间 (VMA) 和进程地址空间 (LMA) 。该命令格式如下:
SECTIONS
{
….
}

/* Read-only sections, merged into text segment: */
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x08048000)); 

PROVIDE 定义的变量 如果源文件中已经定义值 那么用源文件中的,如果没有定义则用脚本中定义的。
并设定该变量的值为0x08048000

. = SEGMENT_START("text-segment", 0x08048000) + SIZEOF_HEADERS;

这句把定位器符号置为 0x08048000 + SIZE_HEADERS(若不指定,则该符号的初始值为 0)。
SIZE_HEADERS为输出文件的文件头.
. 是一个特殊的符号,它是定位器,一个位置指针,指向程序地址空间内的某位置(或某section内的偏移,如果它在SECTIONS命令内的某section描述内),该符号只能在SECTIONS命令内使用。
  .rel.dyn        :
    {
      *(.rel.init)
      *(.rel.text .rel.text.* .rel.gnu.linkonce.t.*)
      *(.rel.fini)
      *(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
      *(.rel.data.rel.ro* .rel.gnu.linkonce.d.rel.ro.*)
      *(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
      *(.rel.tdata .rel.tdata.* .rel.gnu.linkonce.td.*)
      *(.rel.tbss .rel.tbss.* .rel.gnu.linkonce.tb.*)
      *(.rel.ctors)
      *(.rel.dtors)
      *(.rel.got)
      *(.rel.bss .rel.bss.* .rel.gnu.linkonce.b.*)
      *(.rel.ifunc)
    }
  .rel.plt        :
    {
      *(.rel.plt)
      PROVIDE_HIDDEN (__rel_iplt_start = .);
      *(.rel.iplt)
      PROVIDE_HIDDEN (__rel_iplt_end = .);
    }
以上这些段主要用于重定位.具体的内容还需进一步学习.先不做介绍.

  .init           :
  {
    KEEP (*(.init))
  } =0x90909090
  .plt            : { *(.plt) *(.iplt) }
  .text           :
  {
    *(.text.unlikely .text.*_unlikely)
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf32.em.  */
    *(.gnu.warning)
  } =0x90909090

.init将在下文与.fini一起介绍.
.text : 表示text段开始.
*(.text) 将所有(*符号代表任意输入文件)输入文件的.text section合并成一个.text section, 该section的地址由定位器符号的值指定, 即0x08048000.
*(.text.unlikely .text.*_unlikely)
*(.text .stub .text.* .gnu.linkonce.t.*)
/* .gnu.warning sections are handled specially by elf32.em.  */
*(.gnu.warning)
  .fini           :
  {
    KEEP (*(.fini)) #
KEEP()强制连接器保留一些特定的section  
  } =0x90909090

ELF文件中定义了 .init 和 .fini 两个特殊的段,其中 .init 段中的代码会在main之前被执行,.fini 段中的代码会在main退出之后被执行.默认用NOP(0x90)字段进行填充.
  PROVIDE (__etext = .);
  PROVIDE (_etext = .);
  PROVIDE (etext = .);

__etext=.  _etext = .  etext=.; 我们看到,很多变量都定义成等于这个 . 符,实际上这个符号所代表的值是在变化的,随着越往后走,值越增加,根据前面填充的多少自动往后加。即指定当前地址值为代码段结束位置.
这里就定义了一个 etext 符号,当目标文件内引用了 etext 符号,却没有定义它时,etext 符号对应的地址被定义为 .text section 之后的第一个字节的地址。
  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
  .rodata1        : { *(.rodata1) }
 
数据段终于来到了,意思很容易理解的了(用于只读数据段)。
 /* Adjust the address for the data segment.  We want to adjust up to
     the same address within the page on the next page up.  */
  . = ALIGN (CONSTANT (MAXPAGESIZE)) - ((CONSTANT (MAXPAGESIZE) - .) & (CONSTANT (MAXPAGESIZE) - 1));
 . = DATA_SEGMENT_ALIGN (CONSTANT (MAXPAGESIZE), CONSTANT (COMMONPAGESIZE));
如注释所述用于地址调整,为数据段作相应的地址对齐.
  /* Thread Local Storage sections  */
  .tdata      : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
  .tbss          : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
正如注释所描述,这两个段用于线程的局部数据段(包括data及bss)

CONSTRUCTORS 是一个保留字命令。与 c++ 内的(全局对象的)构造函数和(全局对像的)析构函数相关。
  .ctors          :
  {
    /* gcc uses crtbegin.o to find the start of
       the constructors, so we make sure it is
       first.  Because this is a wildcard, it
       doesn't matter if the user does not
       actually link against crtbegin.o; the
       linker won't look for a file to match a
       wildcard.  The wildcard also means that it
       doesn't matter which directory crtbegin.o
       is in.  */
    KEEP (*crtbegin.o(.ctors))
    KEEP (*crtbegin?.o(.ctors))
    /* We don't want to include the .ctor section from
       the crtend.o file until after the sorted ctors.
       The .ctor section from the crtend file contains the
       end of ctors marker and it must be last */
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*(.ctors))
  }
  .dtors          :
  {
    KEEP (*crtbegin.o(.dtors))
    KEEP (*crtbegin?.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*(.dtors))
  }

对于支持任意section名的目标文件格式,比如COFF、ELF格式,GNU C++将全局构造和全局析构信息分别放入 .ctors section 和 .dtors section 内
当连接器生成的目标文件格式不支持任意section名字时,比如说ECOFF、XCOFF格式,连接器将通过名字来识别全局构造和全局析构,对于这些文件格式,连接器把与全局构造和全局析构的相关信息放入出现 CONSTRUCTORS 关键字的输出section内。

一般来说,GNU C++在函数__main内安排全局构造代码的运行,而__main函数被初始化代码(在main函数调用之前执行)调用。

  .got            : { *(.got) *(.igot) }
  . = DATA_SEGMENT_RELRO_END (12, .);
  .got.plt        : { *(.got.plt)  *(.igot.plt) }

意义不明,需进一步探究.
_edata = .; PROVIDE (edata = .);

意义与前面的 etext 类似。edata 符号也较为重要。
  __bss_start = .;
  .bss            :
  {
   *(.dynbss)
   *(.bss .bss.* .gnu.linkonce.b.*)
   *(COMMON)
   /* Align here to ensure that the .bss section occupies space up to
      _end.  Align after .bss to ensure correct alignment even if the
      .bss section disappears because there are no input sections.
      FIXME: Why do we need it? When there is no .bss section, we don't
      pad the .data section.  */
   . = ALIGN(. != 0 ? 32 / 8 : 1);
  }
  . = ALIGN(32 / 8);
  . = ALIGN(32 / 8);
  _end = .; PROVIDE (end = .);
  . = DATA_SEGMENT_END (.);

BSS段开始了。
COMMON 这个保留字的意义:
通用符号(common symbol)的输入section:
在许多目标文件格式中,通用符号并没有占用一个section。连接器认为:输入文件的所有通用符号在名为COMMON的section内。
上例中将所有输入文件的所有通用符号放入输出.bss section内。

这里,定义了几个重要的符号
__bss_start = .;
_end = .;
end = .;
在代码中可能会用到的。

  /* Stabs debugging sections.  */
  .stab          0 : { *(.stab) }
  .stabstr       0 : { *(.stabstr) }
  .stab.excl     0 : { *(.stab.excl) }
  .stab.exclstr  0 : { *(.stab.exclstr) }
  .stab.index    0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment       0 : { *(.comment) }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1 */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
  /* GNU DWARF 1 extensions */
  .debug_srcinfo  0 : { *(.debug_srcinfo) }
  .debug_sfnames  0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges  0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2 */
  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
  .debug_abbrev   0 : { *(.debug_abbrev) }
  .debug_line     0 : { *(.debug_line) }
  .debug_frame    0 : { *(.debug_frame) }
  .debug_str      0 : { *(.debug_str) }
  .debug_loc      0 : { *(.debug_loc) }
  .debug_macinfo  0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames  0 : { *(.debug_varnames) }
  /* DWARF 3 */
  .debug_pubtypes 0 : { *(.debug_pubtypes) }
  .debug_ranges   0 : { *(.debug_ranges) }
  .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }

  这里主要包含代码的调试信息,用于调试时使用.包括一些行号信息及符号表等.
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
DISCARD关键字用于将指定段舍弃,不出现在输出文件中.

余下的这几个意义也类似,看英文注释应该能明白。

对初步编译出来的一个二进制文件进行 nm 解析(nm -n a.out),得到如下内容:

         U _IO_getc@@GLIBC_2.0
         w _Jv_RegisterClasses
         U __ctype_b_loc@@GLIBC_2.3
         U __errno_location@@GLIBC_2.0
         w __gmon_start__
         U __isoc99_sscanf@@GLIBC_2.7
         U __libc_start_main@@GLIBC_2.0
         U __stack_chk_fail@@GLIBC_2.4
         U exit@@GLIBC_2.0
         U fclose@@GLIBC_2.1
         U feof@@GLIBC_2.0
         U fgets@@GLIBC_2.0
         U fopen@@GLIBC_2.1
         U printf@@GLIBC_2.0
         U sprintf@@GLIBC_2.0
         U strerror@@GLIBC_2.0
         U strtol@@GLIBC_2.0
         U vfprintf@@GLIBC_2.0
080485a8 T _init
08048700 T _start
08048730 t __do_global_dtors_aux
08048790 t frame_dummy
080487b4 T die
080487e7 T print_name_pid
08048904 T print_maps
08048be3 T parse_pid
08048c3a T main
08048cc0 T __libc_csu_fini
08048cd0 T __libc_csu_init
08048d2a T __i686.get_pc_thunk.bx
08048d30 t __do_global_ctors_aux
08048d5c T _fini
08048d78 R _fp_hw
08048d7c R _IO_stdin_used
08048e80 r __FRAME_END__
08049f0c d __CTOR_LIST__
08049f0c d __init_array_end
08049f0c d __init_array_start
08049f10 d __CTOR_END__
08049f14 d __DTOR_LIST__
08049f18 D __DTOR_END__
08049f1c d __JCR_END__
08049f1c d __JCR_LIST__
08049f20 d _DYNAMIC
08049ff4 d _GLOBAL_OFFSET_TABLE_
0804a044 D __data_start
0804a044 W data_start
0804a048 D __dso_handle
0804a04c A __bss_start
0804a04c A _edata
0804a04c B stderr@@GLIBC_2.0
0804a050 b completed.7021
0804a054 b dtor_idx.7023
0804a058 A _end



可以看到,所有的地址全是从 0x08048000 开始的。

U表示该符号未定义,主要是动态链接库中的符号,在目标文件加载入内存的时候再进行解析.

w表示该符号为弱符号

         U _IO_getc@@GLIBC_2.0
         w _Jv_RegisterClasses
         U __ctype_b_loc@@GLIBC_2.3
         U __errno_location@@GLIBC_2.0


         w __gmon_start__


         U __isoc99_sscanf@@GLIBC_2.7
         U __libc_start_main@@GLIBC_2.0
         U __stack_chk_fail@@GLIBC_2.4
         U exit@@GLIBC_2.0
         U fclose@@GLIBC_2.1
         U feof@@GLIBC_2.0
         U fgets@@GLIBC_2.0
         U fopen@@GLIBC_2.1
         U printf@@GLIBC_2.0
         U sprintf@@GLIBC_2.0
         U strerror@@GLIBC_2.0
         U strtol@@GLIBC_2.0
         U vfprintf@@GLIBC_2.0

 

两个个起始符号(T表示在text段中)

080485a8 T _init
08048700 T _start

 

几个函数都在代码段内

08048730 t __do_global_dtors_aux
08048790 t frame_dummy
080487b4 T die
080487e7 T print_name_pid
08048904 T print_maps
08048be3 T parse_pid
08048c3a T main

 

栈底地址果然是在 start 下方 0x4000 处
800fc000 T stack

(A 表示绝对不变)

0804a04c A __bss_start
0804a04c A _edata


0804a058 A _end

 

全局构造和析构变量段(D表示在已初始化过的数据段中)

08049f0c d __CTOR_LIST__
08049f0c d __init_array_end
08049f0c d __init_array_start
08049f10 d __CTOR_END__
08049f14 d __DTOR_LIST__
08049f18 D __DTOR_END__
08049f1c d __JCR_END__
08049f1c d __JCR_LIST__
08049f20 d _DYNAMIC
08049ff4 d _GLOBAL_OFFSET_TABLE_
0804a044 D __data_start
0804a044 W data_start
0804a048 D __dso_handle

 

还有一个是用 B 标记的,表示在未初始化的数据段中

0804a04c B stderr@@GLIBC_2.0
0804a050 b completed.7021
0804a054 b dtor_idx.7023

上面符号类型可以查看nm命令的man帮助文档.

结合这些数据,去理解前面的 ld.script 的讲解,会有一个清晰的印象。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多