Chinaunix首页 | 论坛 | 博客
  • 博客访问: 362624
  • 博文数量: 64
  • 博客积分: 2975
  • 博客等级: 少校
  • 技术积分: 831
  • 用 户 组: 普通用户
  • 注册时间: 2007-01-14 10:59
文章存档

2014年(2)

2012年(7)

2010年(40)

2009年(5)

2008年(8)

2007年(2)

分类: LINUX

2010-05-08 16:39:28

ld.so 分析之6 _dl_start_final

1.setup hash

/* This is the second half of _dl_start (below).  It can be inlined safely
这是_dl_start的第二部分.
   under DONT_USE_BOOTSTRAP_MAP, where it is careful not to make any GOT
在DONT_USE_BOOTSTRAP_MAP下它能被安全内联,在DONT_USE_BOOTSTRAP_MAP下不允许引用GOT
   references.  When the tools don't permit us to avoid using a GOT entry
   for _dl_rtld_global (no attribute_hidden support), we must make sure
   this function is not inlined (see below).  
当编译器允许我们使用GOT访问_dl_rtld_global,我们必须让f该函数不内联
*/

//#ifdef DONT_USE_BOOTSTRAP_MAP
static inline Elf32_Addr//
//ElfW(Addr) __attribute__ ((always_inline)) 总是内联
_dl_start_final (void *arg)
//#else
//static ElfW(Addr) __attribute__ ((noinline))
//_dl_start_final (void *arg, struct dl_start_final_info *info)
//#endif
{
  ElfW(Addr) start_addr;

  if (HP_TIMING_AVAIL)// 1
    {
      /* If it hasn't happen yet record the startup time.  */
//      if (! HP_TIMING_INLINE)// 1
//    HP_TIMING_NOW (start_time);
//#if !defined DONT_USE_BOOTSTRAP_MAP && !defined HP_TIMING_NONAVAIL
//      else
//    start_time = info->start_time;
//#endif

      /* Initialize the timing functions.  */
      HP_TIMING_DIFF_INIT ();
    }

  /* Transfer data about ourselves to the permanent link_map structure.  */
/*
#ifndef DONT_USE_BOOTSTRAP_MAP
  GL(dl_rtld_map).l_addr = info->l.l_addr;
  GL(dl_rtld_map).l_ld = info->l.l_ld;
  memcpy (GL(dl_rtld_map).l_info, info->l.l_info,
      sizeof GL(dl_rtld_map).l_info);
  GL(dl_rtld_map).l_mach = info->l.l_mach;
#endif
*/
  _dl_setup_hash (&GL(dl_rtld_map));//  _dl_setup_hash (&_rtld_local._dl_rtld_map);
  GL(dl_rtld_map).l_opencount = 1;

2._dl_start_final->_dl_setup_hash

/* Cache the location of MAP's hash table.  */

void
//internal_function
_dl_setup_hash (struct link_map *map)
{
  Elf_Symndx *hash;//typedef uint32_t Elf_Symndx;
  Elf_Symndx nchain;

  if (!map->l_info[DT_HASH])// 例如ld.so的0x00000004 (HASH)                       0x94
    return;
  hash = (void *) D_PTR (map, l_info[DT_HASH]);//map->l_info[DT_HASH]->d_un.d_ptr,取hash表地址

  map->l_nbuckets = *hash++;
  nchain = *hash++;
  map->l_buckets = hash;
  hash += map->l_nbuckets;
  map->l_chain = hash;
}

ld.so hash表的内容是
[zws@mail ~/glibc-2.3/build/elf]$ objdump -sj .hash ld.so

ld.so:     file format elf32-i386

Contents of section .hash:
 0094 25000000 2b000000 0d000000 21000000  %...+.......!...
 00a4 28000000 00000000 06000000 22000000  (..........."...
 00b4 00000000 00000000 00000000 08000000  ................
 00c4 1e000000 00000000 1a000000 23000000  ............#...
 00d4 26000000 0e000000 1d000000 17000000  &...............
 00e4 25000000 24000000 00000000 13000000  %...$...........
 00f4 00000000 0b000000 18000000 14000000  ................
 0104 27000000 1b000000 00000000 15000000  '...............
 0114 00000000 29000000 1c000000 00000000  ....)...........
 0124 0c000000 2a000000 19000000 00000000  ....*...........
 0134 00000000 00000000 00000000 00000000  ................
 0144 00000000 00000000 00000000 00000000  ................
 0154 00000000 00000000 00000000 00000000  ................
 0164 05000000 02000000 03000000 07000000  ................
 0174 00000000 10000000 00000000 00000000  ................
 0184 00000000 00000000 00000000 00000000  ................
 0194 00000000 00000000 00000000 11000000  ................
 01a4 01000000 0a000000 00000000 0f000000  ................
 01b4 00000000 09000000 20000000 04000000  ........ .......
 01c4 00000000 16000000 12000000 00000000  ................
 01d4 00000000 1f000000                    ........        


hash表的作用是加快链接速度。当在动态链接库中查找是否有需要被外部链接的函数时,
如果直接线性搜索库的动态符号表且表比较大,速度很慢。采用散列的方法查找就比较好。

这里l_nbuckets值为0x25=37,nchain 值为0x2b=43,l_buckets存放散列表入口,l_chain用于将散列值相同的符号连接成单链表。
nchain其实就是动态符号数。该链表中第一个符号索引值A存在l_buckets中,下一个符号的索引值B存放在索引值A在l_chain中的偏移处等等。

问题1.hash表大小是如何计算的?

由binutils 1.18 的bfd/elflink.c文件中compute_bucket_count 计算

/* Array used to determine the number of hash table buckets to use
   based on the number of symbols there are.  If there are fewer than
定义一个数组用于根据符号数来计算hash表大小
   3 symbols we use 1 bucket, fewer than 17 symbols we use 3 buckets,
少于3个符号使用一个桶,少于17个符号使用3个桶
   fewer than 37 we use 17 buckets, and so forth.  We never use more
少于37个符号使用17个桶,等等
   than 32771 buckets.  
我们从不使用超过32771个桶的hash
*/

static const size_t elf_buckets[] =
{
  1, 3, 17, 37, 67, 97, 131, 197, 263, 521, 1031, 2053, 4099, 8209,
  16411, 32771, 0
};

/* Compute bucket count for hashing table.  We do not use a static set
   of possible tables sizes anymore.  Instead we determine for all
   possible reasonable sizes of the table the outcome (i.e., the
   number of collisions etc) and choose the best solution.  The
   weighting functions are not too simple to allow the table to grow
   without bounds.  Instead one of the weighting factors is the size.
   Therefore the result is always a good payoff between few collisions
   (= short chain lengths) and table size.  */
static size_t
compute_bucket_count (struct bfd_link_info *info, unsigned long int *hashcodes,
              unsigned long int nsyms, int gnu_hash)
{
  size_t dynsymcount = elf_hash_table (info)->dynsymcount;
  size_t best_size = 0;
  unsigned long int i;
  bfd_size_type amt;

  /* We have a problem here.  The following code to optimize the table
     size requires an integer type with more the 32 bits.  If
     BFD_HOST_U_64_BIT is set we know about such a type.  */

#ifdef BFD_HOST_U_64_BIT
。。。忽略64位系统
#endif /* defined (BFD_HOST_U_64_BIT) * /

    {
      /* This is the fallback solution if no 64bit type is available or if we
     are not supposed to spend much time on optimizations.  We select the
     bucket count using a fixed set of numbers.  */
      for (i = 0; elf_buckets[i] != 0; i++)//循环查找elf_buckets数组
    {
      best_size = elf_buckets[i];//取桶大小
      if (nsyms < elf_buckets[i + 1])//如果符号数小于适用的符号数
        break;//找到
    }
      if (gnu_hash && best_size < 2)
    best_size = 2;
    }

  return best_size;
}

由于本例中符号数是43,根据计算得桶大小是37,和前面的桶值相等。


问题2:符号的hash值如何计算?

同样在elflink.c中,由bfd_elf_hash计算

/* Standard ELF hash function.  Do not change this function; you will
   cause invalid hash tables to be generated.  */

unsigned long
bfd_elf_hash (const char *namearg)
{
  const unsigned char *name = (const unsigned char *) namearg;
  unsigned long h = 0;
  unsigned long g;
  int ch;

  while ((ch = *name++) != '\0')
    {
      h = (h << 4) + ch;
      if ((g = (h & 0xf0000000)) != 0)
    {
      h ^= g >> 24;
      /* The ELF ABI says `h &= ~g', but this is equivalent in
         this case and on some machines one insn instead of two.  */
      h ^= g;
    }
    }
  return h & 0xffffffff;
}

一个符号的hash值%桶大小即得其在hash表中的索引.

问题3.动态符号表的内容是什么?

动态符号表中存放的是全局符号,包括本地和外地。
本地提供给其他模块使用,本地符号的Ndx是数,指出该符号所在的节。外地符号的Ndx是UND,需要动态链接。

例如
readelf -s ld.so

Symbol table '.dynsym' contains 43 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 000126d0     4 OBJECT  GLOBAL DEFAULT   15 __libc_internal_tsd_set@@GLIBC_PRIVATE
     2: 00012140   980 OBJECT  GLOBAL DEFAULT   14 _rtld_global@@GLIBC_PRIVATE
     3: 00009f6b    44 FUNC    GLOBAL DEFAULT    9 _dl_debug_printf@@GLIBC_PRIVATE
     4: 0000a372  1066 FUNC    GLOBAL DEFAULT    9 _dl_check_map_versions@@GLIBC_PRIVATE
     5: 00006808   757 FUNC    GLOBAL DEFAULT    9 _dl_lookup_versioned_symb@@GLIBC_PRIVATE
     6: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __pthread_mutex_lock
     7: 000126d4     4 OBJECT  GLOBAL DEFAULT   15 __libc_stack_end@@GLIBC_PRIVATE
     8: 000096e0   307 FUNC    GLOBAL DEFAULT    9 _dl_init@@GLIBC_PRIVATE
     9: 0000bdc4   220 FUNC    WEAK   DEFAULT    9 __libc_memalign@@GLIBC_2.0
    10: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __pthread_mutex_init
    11: 0000bea0    34 FUNC    WEAK   DEFAULT    9 malloc@@GLIBC_2.0
    12: 00006189   605 FUNC    GLOBAL DEFAULT    9 _dl_lookup_symbol_skip@@GLIBC_PRIVATE
    13: 00000000     0 OBJECT  GLOBAL DEFAULT  ABS GLIBC_2.1
    14: 000063e6  1058 FUNC    GLOBAL DEFAULT    9 _dl_lookup_versioned_symb@@GLIBC_PRIVATE
    15: 00012514     4 OBJECT  GLOBAL DEFAULT   14 __libc_enable_secure@@GLIBC_PRIVATE
    16: 00005ec4   709 FUNC    GLOBAL DEFAULT    9 _dl_lookup_symbol@@GLIBC_PRIVATE
    17: 000126e4     4 OBJECT  GLOBAL DEFAULT   15 __libc_internal_tsd_get@@GLIBC_PRIVATE
    18: 0000bec2    61 FUNC    WEAK   DEFAULT    9 calloc@@GLIBC_2.0
    19: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __pthread_mutex_unlock
    20: 000126ec     4 OBJECT  GLOBAL DEFAULT   15 __libc_internal_tsd_addre@@GLIBC_PRIVATE
    21: 00009b86     5 FUNC    GLOBAL DEFAULT    9 _dl_debug_state@@GLIBC_PRIVATE
    22: 00012120     4 OBJECT  GLOBAL DEFAULT   14 _dl_argv@@GLIBC_PRIVATE
    23: 000030db   272 FUNC    GLOBAL DEFAULT    9 _dl_dst_substitute@@GLIBC_PRIVATE
    24: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __pthread_mutex_destroy
    25: 00000000     0 OBJECT  GLOBAL DEFAULT  ABS GLIBC_2.0
    26: 00000000     0 OBJECT  GLOBAL DEFAULT  ABS GLIBC_PRIVATE
    27: 0000bf25   139 FUNC    WEAK   DEFAULT    9 realloc@@GLIBC_2.0
    28: 0000b43c   373 FUNC    GLOBAL DEFAULT    9 _dl_get_origin@@GLIBC_PRIVATE
    29: 0000a868  1981 FUNC    GLOBAL DEFAULT    9 _dl_start_profile@@GLIBC_PRIVATE
    30: 00007ca0  1072 FUNC    GLOBAL DEFAULT    9 _dl_relocate_object@@GLIBC_PRIVATE
    31: 00003056   133 FUNC    GLOBAL DEFAULT    9 _dl_dst_count@@GLIBC_PRIVATE
    32: 00012124     4 OBJECT  GLOBAL DEFAULT   14 _dl_starting_up@@GLIBC_PRIVATE
    33: 00005b89    75 FUNC    GLOBAL DEFAULT    9 _dl_unload_cache@@GLIBC_PRIVATE
    34: 0000f460    14 OBJECT  GLOBAL DEFAULT   10 _dl_out_of_memory@@GLIBC_PRIVATE
    35: 0000b025   562 FUNC    GLOBAL DEFAULT    9 _dl_mcount@@GLIBC_2.1
    36: 00004b84  1917 FUNC    GLOBAL DEFAULT    9 _dl_map_object@@GLIBC_PRIVATE
    37: 000091cc   433 FUNC    GLOBAL DEFAULT    9 _dl_signal_error@@GLIBC_PRIVATE
    38: 000126f8    20 OBJECT  GLOBAL DEFAULT   15 _r_debug@@GLIBC_2.0
    39: 0000940d   318 FUNC    GLOBAL DEFAULT    9 _dl_catch_error@@GLIBC_PRIVATE
    40: 00000000     0 OBJECT  GLOBAL DEFAULT  ABS GLIBC_2.3
    41: 0000beff    38 FUNC    WEAK   DEFAULT    9 free@@GLIBC_2.0
    42: 00008255  3291 FUNC    GLOBAL DEFAULT    9 _dl_map_object_deps@@GLIBC_PRIVATE

符号表的第一个符号总是被保留的.因此实际可用的符号数是42

上面是通过节表来显示动态符号表的,我们也可以通过动态节来显示动态符号表

[zws@mail ~/glibc-2.3/build/elf]$readelf -Ds ld.so

Symbol table for image:
  Num Buc:    Value  Size   Type   Bind Vis      Ndx Name
   13   0: 00000000     0  OBJECT GLOBAL DEFAULT ABS GLIBC_2.1
    5   0: 00006808   757    FUNC GLOBAL DEFAULT   9 _dl_lookup_versioned_symbol_skip
   33   1: 00005b89    75    FUNC GLOBAL DEFAULT   9 _dl_unload_cache
   40   2: 00000000     0  OBJECT GLOBAL DEFAULT ABS GLIBC_2.3
    6   4: 00000000     0  NOTYPE   WEAK DEFAULT UND __pthread_mutex_lock
   34   5: 0000f460    14  OBJECT GLOBAL DEFAULT  10 _dl_out_of_memory
    9   5: 0000bdc4   220    FUNC   WEAK DEFAULT   9 __libc_memalign
    8   9: 000096e0   307    FUNC GLOBAL DEFAULT   9 _dl_init
   30  10: 00007ca0  1072    FUNC GLOBAL DEFAULT   9 _dl_relocate_object
   10  10: 00000000     0  NOTYPE   WEAK DEFAULT UND __pthread_mutex_init
   26  12: 00000000     0  OBJECT GLOBAL DEFAULT ABS GLIBC_PRIVATE
   35  13: 0000b025   562    FUNC GLOBAL DEFAULT   9 _dl_mcount
   32  13: 00012124     4  OBJECT GLOBAL DEFAULT  14 _dl_starting_up
   15  13: 00012514     4  OBJECT GLOBAL DEFAULT  14 __libc_enable_secure
    3  13: 00009f6b    44    FUNC GLOBAL DEFAULT   9 _dl_debug_printf
   38  14: 000126f8    20  OBJECT GLOBAL DEFAULT  15 _r_debug
   22  14: 00012120     4  OBJECT GLOBAL DEFAULT  14 _dl_argv
   14  15: 000063e6  1058    FUNC GLOBAL DEFAULT   9 _dl_lookup_versioned_symbol
    2  15: 00012140   980  OBJECT GLOBAL DEFAULT  14 _rtld_global
   29  16: 0000a868  1981    FUNC GLOBAL DEFAULT   9 _dl_start_profile
    1  16: 000126d0     4  OBJECT GLOBAL DEFAULT  15 __libc_internal_tsd_set
   23  17: 000030db   272    FUNC GLOBAL DEFAULT   9 _dl_dst_substitute
   37  18: 000091cc   433    FUNC GLOBAL DEFAULT   9 _dl_signal_error
   36  19: 00004b84  1917    FUNC GLOBAL DEFAULT   9 _dl_map_object
    4  19: 0000a372  1066    FUNC GLOBAL DEFAULT   9 _dl_check_map_versions
   19  21: 00000000     0  NOTYPE   WEAK DEFAULT UND __pthread_mutex_unlock
   11  23: 0000bea0    34    FUNC   WEAK DEFAULT   9 malloc
   24  24: 00000000     0  NOTYPE   WEAK DEFAULT UND __pthread_mutex_destroy
   20  25: 000126ec     4  OBJECT GLOBAL DEFAULT  15 __libc_internal_tsd_address
   39  26: 0000940d   318    FUNC GLOBAL DEFAULT   9 _dl_catch_error
   18  26: 0000bec2    61    FUNC   WEAK DEFAULT   9 calloc
   16  26: 00005ec4   709    FUNC GLOBAL DEFAULT   9 _dl_lookup_symbol
    7  26: 000126d4     4  OBJECT GLOBAL DEFAULT  15 __libc_stack_end
   27  27: 0000bf25   139    FUNC   WEAK DEFAULT   9 realloc
   21  29: 00009b86     5    FUNC GLOBAL DEFAULT   9 _dl_debug_state
   41  31: 0000beff    38    FUNC   WEAK DEFAULT   9 free
   28  32: 0000b43c   373    FUNC GLOBAL DEFAULT   9 _dl_get_origin
   17  32: 000126e4     4  OBJECT GLOBAL DEFAULT  15 __libc_internal_tsd_get
   12  34: 00006189   605    FUNC GLOBAL DEFAULT   9 _dl_lookup_symbol_skip
   42  35: 00008255  3291    FUNC GLOBAL DEFAULT   9 _dl_map_object_deps
   31  35: 00003056   133    FUNC GLOBAL DEFAULT   9 _dl_dst_count
   25  36: 00000000     0  OBJECT GLOBAL DEFAULT ABS GLIBC_2.0

这里Num列显示该符号在符号表中的索引,Buc列显示该符号在Hash表中的索引,索引值相同的符号按照选后顺序显示.
可见索引为0的保留符号是不进入hash表的

可用如下命令显示各种长度桶的直方图,用于分析散列效果.

[zws@mail ~/glibc-2.3/build/elf]$readelf -I ld.so

Histogram for bucket list length (total of 37 buckets):
 Length  Number     % of total  Coverage
      0  10         ( 27.0%)                          //长度为0的桶占总桶的27%
      1  16         ( 43.2%)     38.1%           //长度为1的桶占总桶的43.2%,其总符号数占总符号的38.1%
      2  9          ( 24.3%)     81.0%            //....
      3  0          (  0.0%)     81.0%
      4  2          (  5.4%)    100.0%

length为桶长,number为相同桶长的个数,%  of total为相同桶长的个数占总桶的百分比,coverage为相同桶长的桶中总符号数
占总符号数的百分比。

问题4:.dynsym节和.symtab节的联系和区别?

[zws@mail ~/glibc-2.3/build/elf]$readelf -S ld.so
There are 30 section headers, starting at offset 0x96078:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .hash             HASH            00000094 000094 000148 04   A  2   0  4
  [ 2] .dynsym           DYNSYM          000001dc 0001dc 0002b0 10   A  3   1  4
  [ 3] .dynstr           STRTAB          0000048c 00048c 0002cf 00   A  0   0  1
  [ 4] .gnu.version      VERSYM          0000075c 00075c 000056 02   A  2   0  2
  [ 5] .gnu.version_d    VERDEF          000007b4 0007b4 0000a4 00   A  3   5  4
  [ 6] .rel.dyn          REL             00000858 000858 000070 08   A  2   0  4
  [ 7] .rel.plt          REL             000008c8 0008c8 000048 08   A  2   8  4
  [ 8] .plt              PROGBITS        00000910 000910 0000a0 04  AX  0   0  4
  [ 9] .text             PROGBITS        000009b0 0009b0 00e6ce 00  AX  0   0 16
  [10] .rodata           PROGBITS        0000f080 00f080 002e60 00   A  0   0 32
  [11] .dynamic          DYNAMIC         00012000 012000 0000b0 08  WA  3   0  4
  [12] .got              PROGBITS        000120b0 0120b0 000038 04  WA  0   0  4
  [13] .got.plt          PROGBITS        000120e8 0120e8 000030 04  WA  0   0  4
  [14] .data             PROGBITS        00012120 012120 000408 00  WA  0   0 32
  [15] .bss              NOBITS          00012540 012528 0001cc 00  WA  0   0 32
  [16] .stab             PROGBITS        00000000 012528 0004f8 0c     17   0  4
  [17] .stabstr          STRTAB          00000000 012a20 000276 00      0   0  1
  [18] .comment          PROGBITS        00000000 012c96 0009f6 00      0   0  1
  [19] .debug_aranges    PROGBITS        00000000 01368c 0005e0 00      0   0  1
  [20] .debug_pubnames   PROGBITS        00000000 013c6c 000bd9 00      0   0  1
  [21] .debug_info       PROGBITS        00000000 014845 06722d 00      0   0  1
  [22] .debug_abbrev     PROGBITS        00000000 07ba72 006978 00      0   0  1
  [23] .debug_line       PROGBITS        00000000 0823ea 009e0e 00      0   0  1
  [24] .debug_frame      PROGBITS        00000000 08c1f8 001934 00      0   0  4
  [25] .debug_str        PROGBITS        00000000 08db2c 0083e3 01  MS  0   0  1
  [26] .gnu.warning.llse PROGBITS        00000000 095f20 00003f 00      0   0 32
  [27] .shstrtab         STRTAB          00000000 095f5f 000118 00      0   0  1
  [28] .symtab           SYMTAB          00000000 096528 001f70 10     29 461  4
  [29] .strtab           STRTAB          00000000 098498 00174a 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

strip ld.so后发现
[zws@mail ~/glibc-2.3/build/elf]$readelf -S ldx.so     
There are 19 section headers, starting at offset 0x12ffc:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .hash             HASH            00000094 000094 000148 04   A  2   0  4
  [ 2] .dynsym           DYNSYM          000001dc 0001dc 0002b0 10   A  3   1  4
  [ 3] .dynstr           STRTAB          0000048c 00048c 0002cf 00   A  0   0  1
  [ 4] .gnu.version      VERSYM          0000075c 00075c 000056 02   A  2   0  2
  [ 5] .gnu.version_d    VERDEF          000007b4 0007b4 0000a4 00   A  3   5  4
  [ 6] .rel.dyn          REL             00000858 000858 000070 08   A  2   0  4
  [ 7] .rel.plt          REL             000008c8 0008c8 000048 08   A  2   8  4
  [ 8] .plt              PROGBITS        00000910 000910 0000a0 04  AX  0   0  4
  [ 9] .text             PROGBITS        000009b0 0009b0 00e6ce 00  AX  0   0 16
  [10] .rodata           PROGBITS        0000f080 00f080 002e60 00   A  0   0 32
  [11] .dynamic          DYNAMIC         00012000 012000 0000b0 08  WA  3   0  4
  [12] .got              PROGBITS        000120b0 0120b0 000038 04  WA  0   0  4
  [13] .got.plt          PROGBITS        000120e8 0120e8 000030 04  WA  0   0  4
  [14] .data             PROGBITS        00012120 012120 000408 00  WA  0   0 32
  [15] .bss              NOBITS          00012540 012528 0001cc 00  WA  0   0 32
  [16] .comment          PROGBITS        00000000 012528 0009f6 00      0   0  1
  [17] .gnu.warning.llse PROGBITS        00000000 012f20 00003f 00      0   0 32
  [18] .shstrtab         STRTAB          00000000 012f5f 00009c 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

少了11个节,分别是
  [16] .stab             PROGBITS        00000000 012528 0004f8 0c     17   0  4
  [17] .stabstr          STRTAB          00000000 012a20 000276 00      0   0  1
  [19] .debug_aranges    PROGBITS        00000000 01368c 0005e0 00      0   0  1
  [20] .debug_pubnames   PROGBITS        00000000 013c6c 000bd9 00      0   0  1
  [21] .debug_info       PROGBITS        00000000 014845 06722d 00      0   0  1
  [22] .debug_abbrev     PROGBITS        00000000 07ba72 006978 00      0   0  1
  [23] .debug_line       PROGBITS        00000000 0823ea 009e0e 00      0   0  1
  [24] .debug_frame      PROGBITS        00000000 08c1f8 001934 00      0   0  4
  [25] .debug_str        PROGBITS        00000000 08db2c 0083e3 01  MS  0   0  1
  [28] .symtab           SYMTAB          00000000 096528 001f70 10     29 461  4
  [29] .strtab           STRTAB          00000000 098498 00174a 00      0   0  1

前面9个都是调试信息节。后面一个是.symtab,一个是.symtab的字符表节,他们的符号表的Lk值是29,指向.strtab节.
说明这些信息都不是执行程序必须的。执行程序用的符号表是.dynsym节,以及为其服务的.hash和.dynstr.

.dynsym和.symtab分别占用文件空间,两者磁盘空间不相干。在内容上,.dynsym是.symtab的子集。.dynsym用于执行时动态链接,
.symtab由objdump等分析程序使用.这里也体现了EFL的执行和存储两种视图。

两者侧重点不同,但是相通。链接程序将所有执行时需要的节安排在一起,并安排在最前面。这样程序加载的时候,基址就在文件头处。
在hash节前面的ELF文件头,然后是程序头。
执行时不需要的节放在后面,准确说是.bss后。.bss不占用文件空间,但是占用内存空间。

因此前面的
  [16] .comment          PROGBITS        00000000 012528 0009f6 00      0   0  1
  [17] .gnu.warning.llse PROGBITS        00000000 012f20 00003f 00      0   0 32
  [18] .shstrtab         STRTAB          00000000 012f5f 00009c 00      0   0  1
也可以删掉
还有节表本身,被安排在最后,也可以删掉,不会影响程序的执行。

我们可以作如下实验,编写一个简单的hello world程序,编译执行。

使用readelf -S hello,找到.bss节,假设文件偏移是x.
使用python脚本将其截断。
f=open("hello","r+")
f.seek(x)
f.truncate()
f.close
再执行一下hello,看是否还是可执行.


3.设置map_start和map_end

  GL(dl_rtld_map).l_map_start = (ElfW(Addr)) _begin;// 0
  GL(dl_rtld_map).l_map_end = (ElfW(Addr)) _end;// bss 最后
  /* Copy the TLS related data if necessary.  */
/*#if USE_TLS && !defined DONT_USE_BOOTSTRAP_MAP
//# ifdef HAVE___THREAD
//  assert (info->l.l_tls_modid != 0);
//# else
  if (info->l.l_tls_modid != 0)
//# endif
    {
      GL(dl_rtld_map).l_tls_blocksize = info->l.l_tls_blocksize;
      GL(dl_rtld_map).l_tls_align = info->l.l_tls_align;
      GL(dl_rtld_map).l_tls_initimage_size = info->l.l_tls_initimage_size;
      GL(dl_rtld_map).l_tls_initimage = info->l.l_tls_initimage;
      GL(dl_rtld_map).l_tls_offset = info->l.l_tls_offset;
      GL(dl_rtld_map).l_tls_modid = 1;
      GL(dl_rtld_map).l_tls_tp_initialized
    = info->l.l_tls_tp_initialized;
    }
#endif
*/
//#if HP_TIMING_AVAIL
  HP_TIMING_NOW (GL(dl_cpuclock_offset));
//#endif

查看ld.so的符号表

405: 00000000     0 NOTYPE  LOCAL  DEFAULT  ABS _begin
417: 0001270c     0 NOTYPE  LOCAL  DEFAULT  ABS _end

对比节表可发现

_end正好执行bss结尾(是地址而不是文件偏移)

4._dl_sysdep_start


  /* Call the OS-dependent function to set up life so we can do things like
调用操作系统相关函数,建立操作环境,这样就能执行文件访问等操作
     file access.  It will call `dl_main' (below) to do all the real work
     of the dynamic linker, and then unwind our frame and run the user
这将会调用dl_main完成所有的动态链接工作
     entry point on the same stack we entered on.
最后退出并执行用户入口
 */
  start_addr =  _dl_sysdep_start (arg, &dl_main);//传递dl_main函数,返回用户入口地址

//#ifndef HP_TIMING_NONAVAIL
  if (HP_TIMING_AVAIL)// 1
    {
      hp_timing_t end_time;

      /* Get the current time.  */
      HP_TIMING_NOW (end_time);//记录end_time

      /* Compute the difference.  */
      HP_TIMING_DIFF (rtld_total_time, start_time, end_time);//计算耗时
    }
//#endif

  if (__builtin_expect (GL(dl_debug_mask) & DL_DEBUG_STATISTICS, 0))
    print_statistics ();//如果需要,输出统计信息

  return start_addr;
}

输出的统计信息如下

[zws@mail ~/glibc-2.3/build/elf]$  LD_DEBUG=statistics ls /proc/slabinfo
     30251:
     30251:     runtime linker statistics:
     30251:       total startup time in dynamic loader: 1141112 clock cycles
     30251:                 time needed for relocation: 498188 clock cycles (43.6%)
     30251:                      number of relocations: 103
     30251:           number of relocations from cache: 5
     30251:                time needed to load objects: 377760 clock cycles (33.1%)
/proc/slabinfo
     30251:
     30251:     runtime linker statistics:
     30251:                final number of relocations: 156
     30251:     final number of relocations from cache: 5
[zws@mail ~/glibc-2.3/build/elf]$

阅读(2535) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~