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;
}
}
阅读(714) | 评论(0) | 转发(0) |