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

2014年(2)

2012年(7)

2010年(40)

2009年(5)

2008年(8)

2007年(2)

分类: LINUX

2010-05-12 21:33:16

ld.so分析8 dl_main->process_envars处理环境变量

dl_main函数是ld.so的真实主体,很大很复杂,想读懂它必须选择一条主线或情景.我们就看hello world程序如何被动态链接的吧。

1.准备例子

hello.c

#include

int main()
{
printf("Hello World!\n");
return 0;
}

gcc hello.c -o hello

显示完整的elf信息
[zws@mail ~/glibc-2.3/build/elf]$readelf -a hello
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Intel 80386
  Version:                           0x1
  Entry point address:               0x8048278
  Start of program headers:          52 (bytes into file)
  Start of section headers:          7460 (bytes into file)
  Flags:                             0x0
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         6
  Size of section headers:           40 (bytes)
  Number of section headers:         35
  Section header string table index: 32

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        080480f4 0000f4 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048108 000108 000020 00   A  0   0  4
  [ 3] .hash             HASH            08048128 000128 000028 04   A  4   0  4
  [ 4] .dynsym           DYNSYM          08048150 000150 000050 10   A  5   1  4
  [ 5] .dynstr           STRTAB          080481a0 0001a0 00004c 00   A  0   0  1
  [ 6] .gnu.version      VERSYM          080481ec 0001ec 00000a 02   A  4   0  2
  [ 7] .gnu.version_r    VERNEED         080481f8 0001f8 000020 00   A  5   1  4
  [ 8] .rel.dyn          REL             08048218 000218 000008 08   A  4   0  4
  [ 9] .rel.plt          REL             08048220 000220 000010 08   A  4  11  4
  [10] .init             PROGBITS        08048230 000230 000017 00  AX  0   0  4
  [11] .plt              PROGBITS        08048248 000248 000030 04  AX  0   0  4
  [12] .text             PROGBITS        08048278 000278 000160 00  AX  0   0  4
  [13] .fini             PROGBITS        080483d8 0003d8 00001b 00  AX  0   0  4
  [14] .rodata           PROGBITS        080483f4 0003f4 000016 00   A  0   0  4
  [15] .eh_frame         PROGBITS        0804840c 00040c 000004 00   A  0   0  4
  [16] .ctors            PROGBITS        08049410 000410 000008 00  WA  0   0  4
  [17] .dtors            PROGBITS        08049418 000418 000008 00  WA  0   0  4
  [18] .jcr              PROGBITS        08049420 000420 000004 00  WA  0   0  4
  [19] .dynamic          DYNAMIC         08049424 000424 0000c8 08  WA  5   0  4
  [20] .got              PROGBITS        080494ec 0004ec 000004 04  WA  0   0  4
  [21] .got.plt          PROGBITS        080494f0 0004f0 000014 04  WA  0   0  4
  [22] .data             PROGBITS        08049504 000504 00000c 00  WA  0   0  4
  [23] .bss              NOBITS          08049510 000510 000004 00  WA  0   0  4
  [24] .comment          PROGBITS        00000000 000510 000132 00      0   0  1
  [25] .debug_aranges    PROGBITS        00000000 000648 000078 00      0   0  8
  [26] .debug_pubnames   PROGBITS        00000000 0006c0 000025 00      0   0  1
  [27] .debug_info       PROGBITS        00000000 0006e5 000a84 00      0   0  1
  [28] .debug_abbrev     PROGBITS        00000000 001169 000138 00      0   0  1
  [29] .debug_line       PROGBITS        00000000 0012a1 00027c 00      0   0  1
  [30] .debug_frame      PROGBITS        00000000 001520 000014 00      0   0  4
  [31] .debug_str        PROGBITS        00000000 001534 0006ba 01  MS  0   0  1
  [32] .shstrtab         STRTAB          00000000 001bee 000134 00      0   0  1
  [33] .symtab           SYMTAB          00000000 00229c 0006a0 10     34  88  4
  [34] .strtab           STRTAB          00000000 00293c 0003ee 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)

There are no section groups in this file.

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x000c0 0x000c0 R E 0x4
  INTERP         0x0000f4 0x080480f4 0x080480f4 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x00410 0x00410 R E 0x1000
  LOAD           0x000410 0x08049410 0x08049410 0x00100 0x00104 RW  0x1000
  DYNAMIC        0x000424 0x08049424 0x08049424 0x000c8 0x000c8 RW  0x4
  NOTE           0x000108 0x08048108 0x08048108 0x00020 0x00020 R   0x4

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp
   02     .interp .note.ABI-tag .hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .text .fini .rodata .eh_frame
   03     .ctors .dtors .jcr .dynamic .got .got.plt .data .bss
   04     .dynamic
   05     .note.ABI-tag

Dynamic section at offset 0x424 contains 20 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000000c (INIT)                       0x8048230
 0x0000000d (FINI)                       0x80483d8
 0x00000004 (HASH)                       0x8048128
 0x00000005 (STRTAB)                     0x80481a0
 0x00000006 (SYMTAB)                     0x8048150
 0x0000000a (STRSZ)                      76 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x80494f0
 0x00000002 (PLTRELSZ)                   16 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x8048220
 0x00000011 (REL)                        0x8048218
 0x00000012 (RELSZ)                      8 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x6ffffffe (VERNEED)                    0x80481f8
 0x6fffffff (VERNEEDNUM)                 1
 0x6ffffff0 (VERSYM)                     0x80481ec
 0x00000000 (NULL)                       0x0

Relocation section '.rel.dyn' at offset 0x218 contains 1 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
080494ec  00000106 R_386_GLOB_DAT    00000000   __gmon_start__

Relocation section '.rel.plt' at offset 0x220 contains 2 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
080494fc  00000207 R_386_JUMP_SLOT   00000000   __libc_start_main
08049500  00000407 R_386_JUMP_SLOT   00000000   printf

There are no unwind sections in this file.

Symbol table '.dynsym' contains 5 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
     2: 00000000   251 FUNC    GLOBAL DEFAULT  UND __libc_start_main@GLIBC_2.0 (2)
     3: 080483f8     4 OBJECT  GLOBAL DEFAULT   14 _IO_stdin_used
     4: 00000000    57 FUNC    GLOBAL DEFAULT  UND printf@GLIBC_2.0 (2)

Symbol table '.symtab' contains 106 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
     0: 00000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 080480f4     0 SECTION LOCAL  DEFAULT    1
     2: 08048108     0 SECTION LOCAL  DEFAULT    2
     3: 08048128     0 SECTION LOCAL  DEFAULT    3
     4: 08048150     0 SECTION LOCAL  DEFAULT    4
     5: 080481a0     0 SECTION LOCAL  DEFAULT    5
     6: 080481ec     0 SECTION LOCAL  DEFAULT    6
     7: 080481f8     0 SECTION LOCAL  DEFAULT    7
     8: 08048218     0 SECTION LOCAL  DEFAULT    8
     9: 08048220     0 SECTION LOCAL  DEFAULT    9
    10: 08048230     0 SECTION LOCAL  DEFAULT   10
    11: 08048248     0 SECTION LOCAL  DEFAULT   11
    12: 08048278     0 SECTION LOCAL  DEFAULT   12
    13: 080483d8     0 SECTION LOCAL  DEFAULT   13
    14: 080483f4     0 SECTION LOCAL  DEFAULT   14
    15: 0804840c     0 SECTION LOCAL  DEFAULT   15
    16: 08049410     0 SECTION LOCAL  DEFAULT   16
    17: 08049418     0 SECTION LOCAL  DEFAULT   17
    18: 08049420     0 SECTION LOCAL  DEFAULT   18
    19: 08049424     0 SECTION LOCAL  DEFAULT   19
    20: 080494ec     0 SECTION LOCAL  DEFAULT   20
    21: 080494f0     0 SECTION LOCAL  DEFAULT   21
    22: 08049504     0 SECTION LOCAL  DEFAULT   22
    23: 08049510     0 SECTION LOCAL  DEFAULT   23
    24: 00000000     0 SECTION LOCAL  DEFAULT   24
    25: 00000000     0 SECTION LOCAL  DEFAULT   25
    26: 00000000     0 SECTION LOCAL  DEFAULT   26
    27: 00000000     0 SECTION LOCAL  DEFAULT   27
    28: 00000000     0 SECTION LOCAL  DEFAULT   28
    29: 00000000     0 SECTION LOCAL  DEFAULT   29
    30: 00000000     0 SECTION LOCAL  DEFAULT   30
    31: 00000000     0 SECTION LOCAL  DEFAULT   31
    32: 00000000     0 FILE    LOCAL  DEFAULT  ABS
    33: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/src/build/231499-i38
    34: 00000000     0 FILE    LOCAL  DEFAULT  ABS
    35: 00000000     0 FILE    LOCAL  DEFAULT  ABS
    36: 00000000     0 FILE    LOCAL  DEFAULT  ABS abi-note.S
    37: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/src/build/231499-i38
    38: 00000000     0 FILE    LOCAL  DEFAULT  ABS abi-note.S
    39: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/src/build/231499-i38
    40: 00000000     0 FILE    LOCAL  DEFAULT  ABS abi-note.S
    41: 00000000     0 FILE    LOCAL  DEFAULT  ABS
    42: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/src/build/231499-i38
    43: 00000000     0 FILE    LOCAL  DEFAULT  ABS
    44: 00000000     0 FILE    LOCAL  DEFAULT  ABS
    45: 00000000     0 FILE    LOCAL  DEFAULT  ABS abi-note.S
    46: 00000000     0 FILE    LOCAL  DEFAULT  ABS init.c
    47: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/src/build/231499-i38
    48: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/src/build/231499-i38
    49: 00000000     0 FILE    LOCAL  DEFAULT  ABS initfini.c
    50: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/src/build/231499-i38
    51: 00000000     0 FILE    LOCAL  DEFAULT  ABS
    52: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/src/build/231499-i38
    53: 00000000     0 FILE    LOCAL  DEFAULT  ABS
    54: 00000000     0 FILE    LOCAL  DEFAULT  ABS
    55: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/src/build/231499-i38
    56: 0804829c     0 FUNC    LOCAL  DEFAULT   12 call_gmon_start
    57: 00000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    58: 08049410     0 OBJECT  LOCAL  DEFAULT   16 __CTOR_LIST__
    59: 08049418     0 OBJECT  LOCAL  DEFAULT   17 __DTOR_LIST__
    60: 0804840c     0 OBJECT  LOCAL  DEFAULT   15 __EH_FRAME_BEGIN__
    61: 08049420     0 OBJECT  LOCAL  DEFAULT   18 __JCR_LIST__
    62: 0804950c     0 OBJECT  LOCAL  DEFAULT   22 p.0
    63: 08049510     1 OBJECT  LOCAL  DEFAULT   23 completed.1
    64: 080482c0     0 FUNC    LOCAL  DEFAULT   12 __do_global_dtors_aux
    65: 080482fc     0 FUNC    LOCAL  DEFAULT   12 frame_dummy
    66: 00000000     0 FILE    LOCAL  DEFAULT  ABS crtstuff.c
    67: 08049414     0 OBJECT  LOCAL  DEFAULT   16 __CTOR_END__
    68: 0804941c     0 OBJECT  LOCAL  DEFAULT   17 __DTOR_END__
    69: 0804840c     0 OBJECT  LOCAL  DEFAULT   15 __FRAME_END__
    70: 08049420     0 OBJECT  LOCAL  DEFAULT   18 __JCR_END__
    71: 080483b4     0 FUNC    LOCAL  DEFAULT   12 __do_global_ctors_aux
    72: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/src/build/231499-i38
    73: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/src/build/231499-i38
    74: 00000000     0 FILE    LOCAL  DEFAULT  ABS initfini.c
    75: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/src/build/231499-i38
    76: 00000000     0 FILE    LOCAL  DEFAULT  ABS
    77: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/src/build/231499-i38
    78: 00000000     0 FILE    LOCAL  DEFAULT  ABS
    79: 00000000     0 FILE    LOCAL  DEFAULT  ABS
    80: 00000000     0 FILE    LOCAL  DEFAULT  ABS /usr/src/build/231499-i38
    81: 00000000     0 FILE    LOCAL  DEFAULT  ABS 1.c
    82: 08049410     0 NOTYPE  LOCAL  HIDDEN   16 __fini_array_end
    83: 080494f0     0 OBJECT  LOCAL  HIDDEN   21 _GLOBAL_OFFSET_TABLE_
    84: 08049410     0 NOTYPE  LOCAL  HIDDEN   16 __fini_array_start
    85: 08049410     0 NOTYPE  LOCAL  HIDDEN   16 __init_array_end
    86: 08049410     0 NOTYPE  LOCAL  HIDDEN   16 __init_array_start
    87: 08049424     0 OBJECT  LOCAL  HIDDEN   19 _DYNAMIC
    88: 08049504     0 NOTYPE  WEAK   DEFAULT   22 data_start
    89: 08048380    52 FUNC    GLOBAL DEFAULT   12 __libc_csu_fini
    90: 08048278     0 FUNC    GLOBAL DEFAULT   12 _start
    91: 00000000     0 NOTYPE  WEAK   DEFAULT  UND __gmon_start__
    92: 00000000     0 NOTYPE  WEAK   DEFAULT  UND _Jv_RegisterClasses
    93: 080483f4     4 OBJECT  GLOBAL DEFAULT   14 _fp_hw
    94: 080483d8     0 FUNC    GLOBAL DEFAULT   13 _fini
    95: 00000000   251 FUNC    GLOBAL DEFAULT  UND __libc_start_main@@GLIBC_
    96: 080483f8     4 OBJECT  GLOBAL DEFAULT   14 _IO_stdin_used
    97: 08049504     0 NOTYPE  GLOBAL DEFAULT   22 __data_start
    98: 08049508     0 OBJECT  GLOBAL HIDDEN   22 __dso_handle
    99: 08048350    48 FUNC    GLOBAL DEFAULT   12 __libc_csu_init
   100: 00000000    57 FUNC    GLOBAL DEFAULT  UND printf@@GLIBC_2.0
   101: 08049510     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
   102: 08049514     0 NOTYPE  GLOBAL DEFAULT  ABS _end
   103: 08049510     0 NOTYPE  GLOBAL DEFAULT  ABS _edata
   104: 08048328    39 FUNC    GLOBAL DEFAULT   12 main
   105: 08048230     0 FUNC    GLOBAL DEFAULT   10 _init

Histogram for bucket list length (total of 3 buckets):
 Length  Number     % of total  Coverage
      0  0          (  0.0%)
      1  2          ( 66.7%)     50.0%
      2  1          ( 33.3%)    100.0%

Version symbols section '.gnu.version' contains 5 entries:
 Addr: 00000000080481ec  Offset: 0x0001ec  Link: 4 (.dynsym)
  000:   0 (*local*)       0 (*local*)       2 (GLIBC_2.0)     1 (*global*)   
  004:   2 (GLIBC_2.0)  

Version needs section '.gnu.version_r' contains 1 entries:
 Addr: 0x00000000080481f8  Offset: 0x0001f8  Link to section: 5 (.dynstr)
  000000: Version: 1  File: libc.so.6  Cnt: 1
  0x0010:   Name: GLIBC_2.0  Flags: none  Version: 2

Notes at offset 0x00000108 with length 0x00000020:
  Owner         Data size       Description
  GNU           0x00000010      NT_GNU_ABI_TAG (ABI version tag)


我们发现有上三个重定位项
Relocation section '.rel.dyn' at offset 0x218 contains 1 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
080494ec  00000106 R_386_GLOB_DAT    00000000   __gmon_start__

Relocation section '.rel.plt' at offset 0x220 contains 2 entries:
 Offset     Info    Type            Sym.Value  Sym. Name
080494fc  00000207 R_386_JUMP_SLOT   00000000   __libc_start_main
08049500  00000407 R_386_JUMP_SLOT   00000000   printf


2. dl_main 局部变量定义


static void
dl_main (const ElfW(Phdr) *phdr,
     ElfW(Word) phnum,
     ElfW(Addr) *user_entry)
{
  const ElfW(Phdr) *ph;
  enum mode mode;
  struct link_map **preloads;
  unsigned int npreloads;
  size_t file_size;
  char *file;
  bool has_interp = false;
  unsigned int i;
  bool prelinked = false;
  bool rtld_is_main = false;
//#ifndef HP_TIMING_NONAVAIL
  hp_timing_t start;
  hp_timing_t stop;
  hp_timing_t diff;
//#endif
//#ifdef USE_TLS
 // void *tcbp;
//#endif

  /* Process the environment variable which control the behaviour.  */
  process_envvars (&mode);//处理环境变量

  /* Set up a flag which tells we are just starting.  */
  INTUSE(_dl_starting_up) = 1;

3.先处理环境变量 process_envvars

static void
process_envvars (enum mode *modep)
{
  char **runp = _environ;//指向_environ
  char *envline;
  enum mode mode = normal;
  char *debug_output = NULL;

  /* This is the default place for profiling data file.  */
  GL(dl_profile_output)
    = &"/var/tmp\0/var/profile"[INTUSE(__libc_enable_secure) ? 9 : 0];//根据libc_enable_secure的值不同取/var/tmp 或 /var/profile

  while ((envline = _dl_next_ld_env_entry (&runp)) != NULL)

4.process_envvars->_dl_next_ld_env_entry(sysdeps/generic/dl-environ.c)

/* Walk through the environment of the process and return all entries
遍历环境变量,返回所有以LD_开始的项目
   starting with `LD_'.  
有效的LD_环境变量有
       LD_LIBRARY_PATH
       LD_PRELOAD
       LD_TRACE_LOADED_OBJECTS
       LD_BIND_NOW
       LD_WARN
       LD_DEBUG
       LD_DEBUG_OUTPUT
       LD_VERBOSE
在man ld.so中有详细说明
*/
char *
//internal_function
_dl_next_ld_env_entry (char ***position)
{
  char **current = *position;
  char *result = NULL;

  while (*current != NULL)
    {
      if (__builtin_expect ((*current)[0] == 'L', 0)
      && (*current)[1] == 'D' && (*current)[2] == '_')
    {
      result = &(*current)[3];//返回LD_后面的字符串

      /* Save current position for next visit.  */
      *position = ++current;//为下次访问保存下一个位置

      break;
    }

      ++current;
    }

  return result;
}

5返回process_envvars
    {
      size_t len = 0;

      while (envline[len] != '\0' && envline[len] != '=')//查找=
    ++len;

      if (envline[len] != '=')//不是key=value格式
    /* This is a "LD_" variable at the end of the string without
这个LD_变量在字符串末尾没有=字符
       a '=' character.  Ignore it since otherwise we will access
为了避免后面访问无效内存,忽略它
       invalid memory below.  */
    continue;

      switch (len)
    {
    case 4:
      /* Warning level, verbose or not.  警告级别,详细或没有*/
      if (memcmp (envline, "WARN", 4) == 0)
        GL(dl_verbose) = envline[5] != '\0';//=号后面是否为空串,例如LD_WARN=1会详细显示
      break;

    case 5:
      /* Debugging of the dynamic linker?  */
      if (memcmp (envline, "DEBUG", 5) == 0)
        process_dl_debug (&envline[6]);//进一步处理
      break;

    case 7:
      /* Print information about versions.  */
      if (memcmp (envline, "VERBOSE", 7) == 0)
        {
          version_info = envline[8] != '\0';//=号后面是否为空串
          break;
        }

      /* List of objects to be preloaded.  */
      if (memcmp (envline, "PRELOAD", 7) == 0)
        {
          preloadlist = &envline[8];
          break;
        }

      /* Which shared object shall be profiled.  */
      if (memcmp (envline, "PROFILE", 7) == 0 && envline[8] != '\0')
        GL(dl_profile) = &envline[8];
      break;

    case 8:
      /* Do we bind early?  */
      if (memcmp (envline, "BIND_NOW", 8) == 0)
        {
          GL(dl_lazy) = envline[9] == '\0';//LD_BIND_NOW=1,立即bind,LD_BIND_NOW=,lazy bind
          break;
        }
      if (memcmp (envline, "BIND_NOT", 8) == 0)//LD_BIND_NOT,作用未知?
        GL(dl_bind_not) = envline[9] != '\0';
      break;

    case 9:
      /* Test whether we want to see the content of the auxiliary
         array passed up from the kernel.  */
      if (memcmp (envline, "SHOW_AUXV", 9) == 0)//显示AUXV数组
        _dl_show_auxv ();
      break;

    case 10:
      /* Mask for the important hardware capabilities.  */
      if (memcmp (envline, "HWCAP_MASK", 10) == 0)
        GL(dl_hwcap_mask) = __strtoul_internal (&envline[11], NULL, 0, 0);
      break;

    case 11:
      /* Path where the binary is found.  */
      if (!INTUSE(__libc_enable_secure)
          && memcmp (envline, "ORIGIN_PATH", 11) == 0)
        GL(dl_origin_path) = &envline[12];
      break;

    case 12:
      /* The library search path.  */
      if (memcmp (envline, "LIBRARY_PATH", 12) == 0)
        {
          library_path = &envline[13];
          break;
        }

      /* Where to place the profiling data file.  */
      if (memcmp (envline, "DEBUG_OUTPUT", 12) == 0)
        {
          debug_output = &envline[13];
          break;
        }

      if (memcmp (envline, "DYNAMIC_WEAK", 12) == 0)
        GL(dl_dynamic_weak) = 1;
      break;

    case 14:
      /* Where to place the profiling data file.  */
      if (!INTUSE(__libc_enable_secure)
          && memcmp (envline, "PROFILE_OUTPUT", 14) == 0
          && envline[15] != '\0')
        GL(dl_profile_output) = &envline[15];
      break;

    case 16:
      /* The mode of the dynamic linker can be set.  */
      if (memcmp (envline, "TRACE_PRELINKING", 16) == 0)
        {
          mode = trace;
          GL(dl_verbose) = 1;
          GL(dl_debug_mask) |= DL_DEBUG_PRELINK;
          GL(dl_trace_prelink) = &envline[17];
        }
      break;

    case 20:
      /* The mode of the dynamic linker can be set.  */
      if (memcmp (envline, "TRACE_LOADED_OBJECTS", 20) == 0)
        mode = trace;
      break;

      /* We might have some extra environment variable to handle.  This
         is tricky due to the pre-processing of the length of the name
         in the switch statement here.  The code here assumes that added
         environment variables have a different length.  */
#ifdef EXTRA_LD_ENVVARS
      EXTRA_LD_ENVVARS
#endif
    }
    }

上面的处理流程很清晰,各个参数什么用,后面会涉及到

查看生成的汇编代码发现gcc对switch做了优化,使用数组索引各个case.memcmp虽然没有定义但是也因为使用-O参数而被被优化成内联

    subl    $4, %eax//%eax为len,减去4
    cmpl    $16, %eax//和16比较
    ja    .L718//如果大于16,则进入下一轮循环
    movl    .L770@GOTOFF(%ebx,%eax,4), %eax//取各个case的地址,%ebx为GOT基址,%eax为索引,4为元素大小,.L770@GOTOFF为.L770相对于GOT偏移
    addl    %ebx, %eax//GOT加上case的地址相对于GOT偏移
    jmp    *%eax//跳到该case处
    .section    .rodata//case数组存入.rodata
    .align 4
    .align 4
.L770:
    .long    .L728@GOTOFF//len=4
    .long    .L730@GOTOFF//len=5
    .long    .L718@GOTOFF//len=6,没有,置为下一轮循环地址
    .long    .L732@GOTOFF//len=7
    .long    .L736@GOTOFF//len=8
    .long    .L739@GOTOFF//len=9
    .long    .L741@GOTOFF//len=10
    .long    .L743@GOTOFF//len=11
    .long    .L745@GOTOFF//len=12
    .long    .L755@GOTOFF//len=13
    .long    .L749@GOTOFF//len=14
    .long    .L766@GOTOFF//len=15
    .long    .L751@GOTOFF//len=16
    .long    .L718@GOTOFF//len=17没有,置为下一轮循环地址
    .long    .L718@GOTOFF//len=18没有,置为下一轮循环地址
    .long    .L718@GOTOFF//len=19没有,置为下一轮循环地址
    .long    .L753@GOTOFF//len=20
    .text
.L728:
    .loc 1 1775 0
    movl    -40(%ebp), %esi
    leal    .LC62@GOTOFF(%ebx), %edi
    movl    $4, %eax
    cld //memcmp被内联
    movl    %eax, %ecx
    repz
    cmpsb
    seta    %dl
    setb    %al
    cmpb    %al, %dl
    jne    .L718
    .loc 1 1776 0
    movl    -40(%ebp), %eax
    cmpb    $0, 5(%eax)
    setne    %al
    movzbl    %al, %eax
    movl    %eax, 80+_rtld_local@GOTOFF(%ebx)
    .loc 1 1777 0
    jmp    .L718

6.process_envvars->process_dl_debug 分析debug选项

/* Nonzero if any of the debugging options is enabled.  */
static int any_debug;

/* Process the string given as the parameter which explains which debugging
   options are enabled.  */

static void
process_dl_debug (const char *dl_debug)
{
  /* When adding new entries make sure that the maximal length of a name
     is correctly handled in the LD_DEBUG_HELP code below.  */
//定义LD_DEBUG=value的value有哪些
  static const struct
  {
    unsigned char len;
    const char name[10];
    const char helptext[41];
    unsigned short int mask;
  }
debopts[] =
    {
#define LEN_AND_STR(str) sizeof (str) - 1, str
      { LEN_AND_STR ("libs"), "display library search paths",
    DL_DEBUG_LIBS | DL_DEBUG_IMPCALLS },
      { LEN_AND_STR ("reloc"), "display relocation processing",
    DL_DEBUG_RELOC | DL_DEBUG_IMPCALLS },
      { LEN_AND_STR ("files"), "display progress for input file",
    DL_DEBUG_FILES | DL_DEBUG_IMPCALLS },
      { LEN_AND_STR ("symbols"), "display symbol table processing",
    DL_DEBUG_SYMBOLS | DL_DEBUG_IMPCALLS },
      { LEN_AND_STR ("bindings"), "display information about symbol binding",
    DL_DEBUG_BINDINGS | DL_DEBUG_IMPCALLS },
      { LEN_AND_STR ("versions"), "display version dependencies",
    DL_DEBUG_VERSIONS | DL_DEBUG_IMPCALLS },
      { LEN_AND_STR ("all"), "all previous options combined",
    DL_DEBUG_LIBS | DL_DEBUG_RELOC | DL_DEBUG_FILES | DL_DEBUG_SYMBOLS
    | DL_DEBUG_BINDINGS | DL_DEBUG_VERSIONS | DL_DEBUG_IMPCALLS },
      { LEN_AND_STR ("statistics"), "display relocation statistics",
    DL_DEBUG_STATISTICS },
      { LEN_AND_STR ("help"), "display this help message and exit",
    DL_DEBUG_HELP },
    };
#define ndebopts (sizeof (debopts) / sizeof (debopts[0]))

  /* Skip separating white spaces and commas.  跳过分界符空格和逗号*/
  while (*dl_debug != '\0')//value未结束
    {
      if (*dl_debug != ' ' && *dl_debug != ',' && *dl_debug != ':')//跳过多余的空格,逗号,冒号,如果有的话
    {
      size_t cnt;
      size_t len = 1;

      while (dl_debug[len] != '\0' && dl_debug[len] != ' '
         && dl_debug[len] != ',' && dl_debug[len] != ':')
        ++len;//查找本value后的分隔符

      for (cnt = 0; cnt < ndebopts; ++cnt)//在debopts中查找
        if (debopts[cnt].len == len
        && memcmp (dl_debug, debopts[cnt].name, len) == 0)
          {
        GL(dl_debug_mask) |= debopts[cnt].mask;//置位
        any_debug = 1;//有调试选项
        break;
          }

      if (cnt == ndebopts)//未找到,无效
        {
          /* Display a warning and skip everything until next 显示警告,在找到下一个分隔符之前跳过所有字符
         separator.  */
          char *copy = strndupa (dl_debug, len);
          _dl_error_printf ("\
warning: debug option `%s' unknown; try LD_DEBUG=help\n", copy);
        }

      dl_debug += len;
      continue;
    }

      ++dl_debug;
    }

  if (GL(dl_debug_mask) & DL_DEBUG_HELP)//显示help
    {
      size_t cnt;

      _dl_printf ("\
Valid options for the LD_DEBUG environment variable are:\n\n");

      for (cnt = 0; cnt < ndebopts; ++cnt)
    _dl_printf ("  %.*s%s%s\n", debopts[cnt].len, debopts[cnt].name,
            "         " + debopts[cnt].len - 3,
            debopts[cnt].helptext);

      _dl_printf ("\n\
To direct the debugging output into a file instead of standard output\n\
a filename can be specified using the LD_DEBUG_OUTPUT environment variable.\n");
      _exit (0);
    }
}

举例
[zws@mail ~/glibc-2.3/build/elf]$LD_DEBUG=help ls
Valid options for the LD_DEBUG environment variable are:

  libs        display library search paths
  reloc       display relocation processing
  files       display progress for input file
  symbols     display symbol table processing
  bindings    display information about symbol binding
  versions    display version dependencies
  all         all previous options combined
  statistics  display relocation statistics
  help        display this help message and exit

To direct the debugging output into a file instead of standard output
a filename can be specified using the LD_DEBUG_OUTPUT environment variable.

其他的选项大家自己试验一下看看

7.process_envvars->process_dl_debug->strndupa (string/string.h)

/* Return an alloca'd copy of at most N bytes of string.  */
# define strndupa(s, n)                                  \
  (__extension__                                  \
    ({                                          \
      __const char *__old = (s);                          \
      size_t __len = strnlen (__old, (n));                      \
      char *__new = (char *) __builtin_alloca (__len + 1);              \
      __new[__len] = '\0';                              \
      (char *) memcpy (__new, __old, __len);                      \
    }))

对应的汇编代码是
.LBB78:
    subl    $8, %esp
    pushl    -16(%ebp) //参数len
    pushl    8(%ebp) //参数dl_debug
.LCFI78:
    call    __strnlen@PLT//调用__strnlen,strnlen是__strnlen的weark_alias
    addl    $16, %esp//平栈,8+4+4
    leal    16(%eax), %edx//16+__len->%edx
    andl    $-16, %edx//%edx向地地址方向对齐到16字节边界
    subl    %edx, %esp//__builtin_alloca 在栈上分配空间,实际分配的大小>=__len+16
    movl    %esp, %edx//__new->%edx
    movb    $0, (%esp,%eax)//__new[__len] = '\0';
    subl    $4, %esp
    pushl    %eax//参数__len
    pushl    8(%ebp)//__old
    pushl    %edx//__new
    call    memcpy@PLT
    addl    $12, %esp//平栈
.LBE78:
    .loc 1 1710 0
    pushl    %eax//copy
    leal    .LC56@GOTOFF(%ebx), %eax//warning: debug option `%s' unknown; try LD_DEBUG=help\n
    pushl    %eax
    pushl    $2//STDERR_FILENO
    call    _dl_dprintf
.LBE77:
    addl    $16, %esp//平栈,12+前面的subl $4,%esp的4字节
.L700:
    .loc 1 1714 0
    movl    -16(%ebp), %eax
    addl    %eax, 8(%ebp)//dl_debug += len;
    .loc 1 1715 0
    jmp    .L685


还要指出多次调用__builtin_alloca会不断的在栈上分配空间,即%esp向低地址方向增长。但是随着函数的返回,这些空间自然全部被释放。


8.process_envvars->_dl_show_auxv(sysdeps/generic/dl-sysdep.c)
显示AUXV信息

void
//internal_function
_dl_show_auxv (void)
{
  char buf[64];
  ElfW(auxv_t) *av;

  /* Terminate string.  */
  buf[63] = '\0';

  /* The following code assumes that the AT_* values are encoded
下面的代码假定AT_*值从0(AT_NULL)开始编码,1代表AT_IGNORE,其他值?
  starting from 0 with AT_NULL, 1 for AT_IGNORE, and all other values
  close by (otherwise the array will be too large).  In case we have
为避免必须支持不同平台,必须采用可定制实现
  to support a platform where these requirements are not fulfilled
  some alternative implementation has to be used.  */
  for (av = _dl_auxv; av->a_type != AT_NULL; ++av)
    {
      static const struct
      {
    const char label[20];
    enum { dec, hex, str } form;
      }
      auxvars[] =
    {
      [AT_EXECFD - 2] =        { "AT_EXECFD:      ", dec },
      [AT_PHDR - 2] =        { "AT_PHDR:        0x", hex },
      [AT_PHENT - 2] =        { "AT_PHENT:       ", dec },
      [AT_PHNUM - 2] =        { "AT_PHNUM:       ", dec },
      [AT_PAGESZ - 2] =        { "AT_PAGESZ:      ", dec },
      [AT_BASE - 2] =        { "AT_BASE:        0x", hex },
      [AT_FLAGS - 2] =        { "AT_FLAGS:       0x", hex },
      [AT_ENTRY - 2] =        { "AT_ENTRY:       0x", hex },
      [AT_NOTELF - 2] =        { "AT_NOTELF:      ", hex },
      [AT_UID - 2] =        { "AT_UID:         ", dec },
      [AT_EUID - 2] =        { "AT_EUID:        ", dec },
      [AT_GID - 2] =        { "AT_GID:         ", dec },
      [AT_EGID - 2] =        { "AT_EGID:        ", dec },
      [AT_PLATFORM - 2] =        { "AT_PLATFORM:    ", str },
      [AT_HWCAP - 2] =        { "AT_HWCAP:       ", hex },
      [AT_CLKTCK - 2] =        { "AT_CLKTCK:      ", dec },
      [AT_FPUCW - 2] =        { "AT_FPUCW:       ", hex },
      [AT_DCACHEBSIZE - 2] =    { "AT_DCACHEBSIZE: 0x", hex },
      [AT_ICACHEBSIZE - 2] =    { "AT_ICACHEBSIZE: 0x", hex },
      [AT_UCACHEBSIZE - 2] =    { "AT_UCACHEBSIZE: 0x", hex }
    };
      unsigned int idx = (unsigned int) (av->a_type - 2);// -2忽略0,和1

      assert (AT_NULL == 0);
      assert (AT_IGNORE == 1);
      if (idx < sizeof (auxvars) / sizeof (auxvars[0]))
    {
      if (av->a_type != AT_HWCAP || _dl_procinfo (av->a_un.a_val) < 0)//不是AT_HWCAP,或者是AT_HWCAP就调用_dl_procinfo,代码自己看
        {
          const char *val = av->a_un.a_ptr;

          if (__builtin_expect (auxvars[idx].form, dec) == dec)//十进制数据
        val = _itoa ((unsigned long int) av->a_un.a_val,
                 buf + sizeof buf - 1, 10, 0);//调用_itoa,代码自己看
          else if (__builtin_expect (auxvars[idx].form, hex) == hex)
        val = _itoa ((unsigned long int) av->a_un.a_val,
                 buf + sizeof buf - 1, 16, 0);

          _dl_printf ("%s%s\n", auxvars[idx].label, val);
        }
    }
    }
}

举例
[zws@mail elf]$ LD_SHOW_AUXV=1 ls
AT_SYSINFO:     0xffffe000
AT_HWCAP:    fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe
AT_PAGESZ:      4096
AT_CLKTCK:      100
AT_PHDR:        0x8048034
AT_PHENT:       32
AT_PHNUM:       7
AT_BASE:        0x40000000
AT_FLAGS:       0x0
AT_ENTRY:       0x8049690
AT_UID:         503
AT_EUID:        503
AT_GID:         504
AT_EGID:        504
AT_PLATFORM:    i686

9.process_envvars->EXTRA_LD_ENVVARS (sysdeps/unix/sysv/linux/i386/dl-librecron.c)
额外的环境变量
#define EXTRA_LD_ENVVARS \
  case 13:                                      \
    if (memcmp (envline, "ASSUME_KERNEL", 13) == 0)/*指定内核版本*/                  \
      {                                          \
    unsigned long int i, j, osversion = 0;                      \
    char *p = &envline[14], *q;                          \
                                          \
    for (i = 0; i < 3; i++, p = q + 1)/*i代表x.y.z中数的个数,共三个*/                      \
      {                                      \
        j = __strtoul_internal (p, &q, 0, 0);/*版本字符串转换成数x.y.z*/                  \
        if (j >= 255/*j不能>=255*/ || p == q /*该字符串不是数*/|| (i < 2 && *q && *q != '.')/*前两个数且后面还有字符,且该字符不为'.'*/)          \
          {                                      \
        osversion = 0;                              \
        break;                                  \
          }                                      \
        osversion |= j << (16 - 8 * i);/*i==0,j<<16;i=-1;j<<8;i==2,j<<0,说明每个数占一个字节*/                      \
        if (!*q)                                  \
          break;                                  \
      }                                      \
    if (osversion)                                  \
      GL(dl_osversion) = osversion;                          \
    break;                                      \
      }                                          \
                                          \
  case 15:                                      \
    if (memcmp (envline, "LIBRARY_VERSION", 15) == 0)                  \
      {                                          \
    GL(dl_correct_cache_id) = envline[16] == '5' ? 2 : 3;/*值为5,则dl_correct_cache_id=2;否则为dl_correct_cache_id=3*/          \
    break;                                      \
      }

10.返回process_envvars

  /* The caller wants this information.  */
  *modep = mode;

  /* Extra security for SUID binaries.  Remove all dangerous environment
因为是SUID程序,移除所有危险环境变量
     variables.  */
  if (__builtin_expect (INTUSE(__libc_enable_secure), 0))
    {
      static const char unsecure_envvars[] =
//#ifdef EXTRA_UNSECURE_ENVVARS
/*该宏定义在sysdeps/unix/sysv/linux/i386/dl-librecon.h
/*
 Extra unsecure variables.  The names are all stuffed in a single
特别不安全变量。这些名字被组合成单个字符串,因此需要显示使用'\0' 结束这些名字
   string which means they have to be terminated with a '\0' explicitly.  * /
#define EXTRA_UNSECURE_ENVVARS \
  "LD_AOUT_LIBRARY_PATH\0"                              \
  "LD_AOUT_PRELOAD\0"
*/
    EXTRA_UNSECURE_ENVVARS
//#endif
/*该宏定义在sysdeps/generic/unsecvars.h
/*
 Environment variable to be removed for SUID programs.  The names are
   all stuffed in a single string which means they have to be terminated
   with a '\0' explicitly.  * /
#define UNSECURE_ENVVARS \
  "LD_PRELOAD\0"                                  \
  "LD_LIBRARY_PATH\0"                                  \
  "LD_ORIGIN_PATH\0"                                  \
  "LD_DEBUG_OUTPUT\0"                                  \
  "LD_PROFILE\0"                                  \
  "GCONV_PATH\0"                                  \
  "HOSTALIASES\0"                                  \
  "LOCALDOMAIN\0"                                  \
  "LOCPATH\0"                                      \
  "MALLOC_TRACE\0"                                  \
  "NLSPATH\0"                                      \
  "RESOLV_HOST_CONF\0"                                  \
  "RES_OPTIONS\0"                                  \
  "TMPDIR\0"                                      \
  "TZDIR\0"

*/
    UNSECURE_ENVVARS;
      const char *nextp;

      nextp = unsecure_envvars;
      do
    {
      unsetenv (nextp);//注销该环境变量,其实就是将后面的环境变量指针往前移,覆盖掉该变量指针
      /* We could use rawmemchr but this need not be fast.  */
      nextp = (char *) (strchr) (nextp, '\0') + 1;
    }
      while (*nextp != '\0');

      if (__access ("/etc/suid-debug", F_OK) != 0)//不存在文件/etc/suid-debug
    unsetenv ("MALLOC_CHECK_");//注销MALLOC_CHECK_
    }
  /* If we have to run the dynamic linker in debugging mode and the
不是SUID程序,如果我们必须运行dl在调试模式下,并且存在环境变量LD_DEBUG_OUTPUT
     LD_DEBUG_OUTPUT environment variable is given, we write the debug
则将调试信息写入这个文件
     messages to this file.  */
  else if (any_debug && debug_output != NULL)
    {
//#ifdef O_NOFOLLOW
      const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW;
//#else
//      const int flags = O_WRONLY | O_APPEND | O_CREAT;
//#endif
      size_t name_len = strlen (debug_output);
      char buf[name_len + 12];//在栈上动态分配
      char *startp;

      buf[name_len + 11] = '\0';
      startp = _itoa (__getpid (), &buf[name_len + 11], 10, 0);//存入buf的最后
      *--startp = '.';//前面加.
      startp = memcpy (startp - name_len, debug_output, name_len);//复制debug_output,组成x.y格式

      GL(dl_debug_fd) = __open (startp, flags, DEFFILEMODE);
      if (GL(dl_debug_fd) == -1)
    /* We use standard output if opening the file failed.  */
    GL(dl_debug_fd) = STDOUT_FILENO;
    }
}


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

慕冬亮2015-01-17 20:24:38

楼主写的我看的不是很明白?
请问一下楼主,到了dl_main 这个函数的参数是什么意义?
Program received signal SIGSEGV, Segmentation fault.
0x00459b39 in dl_main (phdr=0x8057000, phnum=11, user_entry=0xbffff2bc, auxv=0xbffff3f0) at rtld.c:1272
1272       && strcmp (GL(dl_rtld_map).l_libname->name,

不知道楼主能不能看出来这个错误是什么意思??