存放环境变量的 glibc 可执行文件的全局变量 char ** __environ 或者 char **(environ) 在函数__libc_start_main() 中赋值。
文件 glibc-2.32/sysdeps/arm/start.S 定义的函数 _start 的主要作用是设置栈,并调用函数__libc_start_main 设置环境变量 __environ:
环境变量 __environ 位于进程的栈空间,下图是环境变量 __environ 在栈中的位置:
存放环境变量全局变量 char ** __environ 与其它全局变量一样,只在本进程的内存地址空间有效,与另一个进程中的全局变量 char ** __environ 无关。
将下面的代码存放到文件 getenv.c 中:
#include <endian.h>
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/syscall.h>
#define gettid() syscall(__NR_gettid)
static char * getenv_mxl (const char *name)
{
size_t len = strlen (name);
char **ep;
uint16_t name_start;
if (__environ == NULL || name[0] == '\0')
return NULL;
if (name[1] == '\0')
{
/* The name of the variable consists of only one character. Therefore
the first two characters of the environment entry are this character
and a '=' character. */
#if __BYTE_ORDER == __LITTLE_ENDIAN || !_STRING_ARCH_unaligned
name_start = ('=' << 8) | *(const unsigned char *) name;
#else
name_start = '=' | ((*(const unsigned char *) name) << 8);
#endif
for (ep = __environ; *ep != NULL; ++ep)
{
#if _STRING_ARCH_unaligned
uint16_t ep_start = *(uint16_t *) *ep;
#else
uint16_t ep_start = (((unsigned char *) *ep)[0]
| (((unsigned char *) *ep)[1] << 8));
#endif
if (name_start == ep_start)
return &(*ep)[2];
}
}
else
{
#if _STRING_ARCH_unaligned
name_start = *(const uint16_t *) name;
#else
name_start = (((const unsigned char *) name)[0]
| (((const unsigned char *) name)[1] << 8));
#endif
len -= 2;
name += 2;
for (ep = __environ; *ep != NULL; ++ep)
{
#if _STRING_ARCH_unaligned
uint16_t ep_start = *(uint16_t *) *ep;
#else
uint16_t ep_start = (((unsigned char *) *ep)[0]
| (((unsigned char *) *ep)[1] << 8));
#endif
if (name_start == ep_start && !strncmp (*ep + 2, name, len)
&& (*ep)[len + 2] == '=')
return &(*ep)[len + 3];
}
}
return NULL;
}
void main(void)
{
char **ep;
char *cp ;
printf("__environ = %p.\n", __environ);
for (ep = __environ; *ep != NULL; ++ep)
{
printf("address of ep= %p , ep = %s\n", ep, *ep);
}
while(1)
{
printf("%s : I am thread ID: LWPID = %ld.\n", __FUNCTION__, (long int)gettid());
printf("__environ = %p.\n", __environ);
for (ep = __environ; *ep != NULL; ++ep)
{
printf("address of ep= %p , ep = %s\n", ep, *ep);
}
cp = getenv_mxl("LOCALDOMAIN");
if( cp == NULL)
perror("get LOCALDOMAIN error:");
else
printf("env LOCALDOMAIN = %s.\n", cp);
sleep(10);
printf("******************************************\n");
}
}
使用 arm32 架构的 RK3128 的工具链编译成可执行文件getenv_unset,并在 rk3128 上执行,左边的窗口设置环境变量 export mxl=linfen,其全局变量 char ** __environ 的地址为 0xbeb5ddbc ,右边的窗口设置环境变量 export linfen=mxl,其全局变量 char ** __environ 的地址为 0xbeabcdbc:
参见《程序员的自我修养》:
glibc可执行文件的入口函数并不是 main,对于arm平台,glibc可执行文件的入口函数是文件 glibc-2.32/sysdeps/arm/start.S 定义的函数 _start ,参见《程序员的自我修养》:
文件 glibc-2.32/sysdeps/arm/start.S 定义的函数 _start 的主要作用是设置栈,并调用函数__libc_start_main 设置环境变量 __environ:
环境变量 __environ 位于进程的栈空间,下图是环境变量 __environ 在栈中的位置:
文件 文件 glibc-2.32/sysdeps/arm/start.S 的实现如下:
/* Startup code for ARM & ELF */
/* This is the canonical entry point, usually the first thing in the text segment.
Note that the code in the .init section has already been run.
This includes _init and _libc_init
At this entry point, most registers' values are unspecified, except:
a1 Contains a function pointer to be registered with `atexit'.
This is how the dynamic linker arranges to have DT_FINI
functions called for shared libraries that have been loaded
before this code runs.
sp The stack contains the arguments and environment:
0(sp) argc
4(sp) argv[0]
...
(4*argc)(sp) NULL
(4*(argc+1))(sp) envp[0]
...
NULL
*/
.text
.globl _start
.type _start,#function
_start:
/* Protect against unhandled exceptions. */
.fnstart
/* Clear the frame pointer and link register since this is the outermost frame. */
mov fp, #0
mov lr, #0
/* Pop argc off the stack and save a pointer to argv */
pop { a2 }
mov a3, sp
/* Push stack limit */
push { a3 }
/* Push rtld_fini */
push { a1 }
/* Fetch address of __libc_csu_fini */
ldr ip, =__libc_csu_fini
//glibc-2.32/csu/elf-init.c
void __libc_csu_init (int argc, char **argv, char **envp)
{
/* For dynamically linked executables the preinit array is executed by
the dynamic linker (before initializing any shared object). */
const size_t size = __init_array_end - __init_array_start;
for (size_t i = 0; i < size; i++)
(*__init_array_start [i]) (argc, argv, envp);
}
/* Push __libc_csu_fini */
push { ip }
/* Set up the other arguments in registers */
ldr a1, =main
ldr a4, =__libc_csu_init
/* __libc_start_main (main, argc, argv, init, fini, rtld_fini, stack_end) */
/* Let the libc call main and exit with its return code. */
bl __libc_start_main
//glibc-2.32/csu/libc-start.c
/* Note: the fini parameter is ignored here for shared library. It
is registered with __cxa_atexit. This had the disadvantage that
finalizers were called in more than one place. */
STATIC int
LIBC_START_MAIN (int (*main) (int, char **, char ** MAIN_AUXVEC_DECL),
int argc, char **argv,
__typeof (main) init,
void (*fini) (void),
void (*rtld_fini) (void), void *stack_end)
{
/* Initialize very early so that tunables can use it. */
__libc_init_secure ();
__tunables_init (__environ)-> //初始环境变量
char **prev_envp = __environ;
while ((envp = get_next_env (envp, &envname, &len, &envval, &prev_envp)) != NULL)
{
char *new_env = tunables_strdup (envname);
if (new_env != NULL)
parse_tunables (new_env + len + 1, envval);
/* Put in the updated envval. */
*prev_envp = new_env;
for (int i = 0; i < sizeof (tunable_list) / sizeof (tunable_t); i++)
{
tunable_t *cur = &tunable_list[i];
const char *name = cur->env_alias;
if (__libc_enable_secure)
{
/* Erase the environment variable. */
char **ep = prev_envp;
envp = prev_envp;
}
tunable_initialize (cur, envval);
break;
}
}
if (init)
(*init) (argc, argv, __environ MAIN_AUXVEC_PARAM);
result = main (argc, argv, __environ MAIN_AUXVEC_PARAM);
glibc可执行文件反汇编观察入口函数
将下面的代码保存在文件 function_entry.c 中:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char*argv)
{
printf("test glibc function entry.\n");
return 0;
}
使用 arm32 架构的 RK3128 的工具链编译成可执行文件,再使用 objdump -S 命令反汇编可执行文件 function_entry,输出结果如下,可以看出入口函数是 _start :
function_entry: file format elf32-littlearm
Disassembly of section .init:
000102c8 <_init>:
102c8: e92d4008 push {r3, lr}
102cc: eb000020 bl 10354 <call_weak_fn>
102d0: e8bd8008 pop {r3, pc}
Disassembly of section .plt:
000102d4 <.plt>:
102d4: e52de004 push {lr} ; (str lr, [sp, #-4]!)
102d8: e59fe004 ldr lr, [pc, #4] ; 102e4 <.plt+0x10>
102dc: e08fe00e add lr, pc, lr
102e0: e5bef008 ldr pc, [lr, #8]!
102e4: 00010d1c .word 0x00010d1c
000102e8 <puts@plt>:
102e8: e28fc600 add ip, pc, #0, 12
102ec: e28cca10 add ip, ip, #16, 20 ; 0x10000
102f0: e5bcfd1c ldr pc, [ip, #3356]! ; 0xd1c
000102f4 <__libc_start_main@plt>:
102f4: e28fc600 add ip, pc, #0, 12
102f8: e28cca10 add ip, ip, #16, 20 ; 0x10000
102fc: e5bcfd14 ldr pc, [ip, #3348]! ; 0xd14
00010300 <__gmon_start__@plt>:
10300: e28fc600 add ip, pc, #0, 12
10304: e28cca10 add ip, ip, #16, 20 ; 0x10000
10308: e5bcfd0c ldr pc, [ip, #3340]! ; 0xd0c
0001030c <abort@plt>:
1030c: e28fc600 add ip, pc, #0, 12
10310: e28cca10 add ip, ip, #16, 20 ; 0x10000
10314: e5bcfd04 ldr pc, [ip, #3332]! ; 0xd04
Disassembly of section .text:
00010318 <_start>:
10318: e3a0b000 mov fp, #0
1031c: e3a0e000 mov lr, #0
10320: e49d1004 pop {r1} ; (ldr r1, [sp], #4)
10324: e1a0200d mov r2, sp
10328: e52d2004 push {r2} ; (str r2, [sp, #-4]!)
1032c: e52d0004 push {r0} ; (str r0, [sp, #-4]!)
10330: e59fc010 ldr ip, [pc, #16] ; 10348 <_start+0x30>
10334: e52dc004 push {ip} ; (str ip, [sp, #-4]!)
10338: e59f000c ldr r0, [pc, #12] ; 1034c <_start+0x34>
1033c: e59f300c ldr r3, [pc, #12] ; 10350 <_start+0x38>
10340: ebffffeb bl 102f4 <__libc_start_main@plt>
10344: ebfffff0 bl 1030c <abort@plt>
10348: 0001049c .word 0x0001049c
1034c: 00010408 .word 0x00010408
10350: 00010438 .word 0x00010438
00010354 <call_weak_fn>:
10354: e59f3014 ldr r3, [pc, #20] ; 10370 <call_weak_fn+0x1c>
10358: e59f2014 ldr r2, [pc, #20] ; 10374 <call_weak_fn+0x20>
1035c: e08f3003 add r3, pc, r3
10360: e7932002 ldr r2, [r3, r2]
10364: e3520000 cmp r2, #0
10368: 012fff1e bxeq lr
1036c: eaffffe3 b 10300 <__gmon_start__@plt>
10370: 00010c9c .word 0x00010c9c
10374: 0000001c .word 0x0000001c
00010378 <deregister_tm_clones>:
10378: e59f0018 ldr r0, [pc, #24] ; 10398 <deregister_tm_clones+0x20>
1037c: e59f3018 ldr r3, [pc, #24] ; 1039c <deregister_tm_clones+0x24>
10380: e1530000 cmp r3, r0
10384: 012fff1e bxeq lr
10388: e59f3010 ldr r3, [pc, #16] ; 103a0 <deregister_tm_clones+0x28>
1038c: e3530000 cmp r3, #0
10390: 012fff1e bxeq lr
10394: e12fff13 bx r3
10398: 00021028 .word 0x00021028
1039c: 00021028 .word 0x00021028
103a0: 00000000 .word 0x00000000
000103a4 <register_tm_clones>:
103a4: e59f0024 ldr r0, [pc, #36] ; 103d0 <register_tm_clones+0x2c>
103a8: e59f1024 ldr r1, [pc, #36] ; 103d4 <register_tm_clones+0x30>
103ac: e0413000 sub r3, r1, r0
103b0: e1a01fa3 lsr r1, r3, #31
103b4: e0811143 add r1, r1, r3, asr #2
103b8: e1b010c1 asrs r1, r1, #1
103bc: 012fff1e bxeq lr
103c0: e59f3010 ldr r3, [pc, #16] ; 103d8 <register_tm_clones+0x34>
103c4: e3530000 cmp r3, #0
103c8: 012fff1e bxeq lr
103cc: e12fff13 bx r3
103d0: 00021028 .word 0x00021028
103d4: 00021028 .word 0x00021028
103d8: 00000000 .word 0x00000000
000103dc <__do_global_dtors_aux>:
103dc: e92d4010 push {r4, lr}
103e0: e59f4018 ldr r4, [pc, #24] ; 10400 <__do_global_dtors_aux+0x24>
103e4: e5d43000 ldrb r3, [r4]
103e8: e3530000 cmp r3, #0
103ec: 18bd8010 popne {r4, pc}
103f0: ebffffe0 bl 10378 <deregister_tm_clones>
103f4: e3a03001 mov r3, #1
103f8: e5c43000 strb r3, [r4]
103fc: e8bd8010 pop {r4, pc}
10400: 00021028 .word 0x00021028
00010404 <frame_dummy>:
10404: eaffffe6 b 103a4 <register_tm_clones>
00010408 <main>:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char*argv)
{
10408: e92d4800 push {fp, lr}
1040c: e28db004 add fp, sp, #4
10410: e24dd008 sub sp, sp, #8
10414: e50b0008 str r0, [fp, #-8]
10418: e50b100c str r1, [fp, #-12]
printf("test glibc function entry.\n");
1041c: e300053c movw r0, #1340 ; 0x53c
10420: e3400001 movt r0, #1
10424: ebffffaf bl 102e8 <puts@plt>
return 0;
10428: e3a03000 mov r3, #0
1042c: e1a00003 mov r0, r3
10430: e24bd004 sub sp, fp, #4
10434: e8bd8800 pop {fp, pc}
00010438 <__libc_csu_init>:
10438: e92d47f0 push {r4, r5, r6, r7, r8, r9, sl, lr}
1043c: e1a07000 mov r7, r0
10440: e59f604c ldr r6, [pc, #76] ; 10494 <__libc_csu_init+0x5c>
10444: e1a08001 mov r8, r1
10448: e59f5048 ldr r5, [pc, #72] ; 10498 <__libc_csu_init+0x60>
1044c: e1a09002 mov r9, r2
10450: e08f6006 add r6, pc, r6
10454: ebffff9b bl 102c8 <_init>
10458: e08f5005 add r5, pc, r5
1045c: e0466005 sub r6, r6, r5
10460: e1b06146 asrs r6, r6, #2
10464: 08bd87f0 popeq {r4, r5, r6, r7, r8, r9, sl, pc}
10468: e2455004 sub r5, r5, #4
1046c: e3a04000 mov r4, #0
10470: e5b53004 ldr r3, [r5, #4]!
10474: e1a02009 mov r2, r9
10478: e1a01008 mov r1, r8
1047c: e1a00007 mov r0, r7
10480: e2844001 add r4, r4, #1
10484: e12fff33 blx r3
10488: e1560004 cmp r6, r4
1048c: 1afffff7 bne 10470 <__libc_csu_init+0x38>
10490: e8bd87f0 pop {r4, r5, r6, r7, r8, r9, sl, pc}
10494: 00010ab4 .word 0x00010ab4
10498: 00010aa8 .word 0x00010aa8
0001049c <__libc_csu_fini>:
1049c: e12fff1e bx lr
Disassembly of section .fini:
000104a0 <_fini>:
104a0: e92d4008 push {r3, lr}
104a4: e8bd8008 pop {r3, pc}