Environment Variables in Linux

Last week, one of my friend who's new to Linux asked me some quesitons about environment variables in Linux.  It was not too difficult for me to answer her questions, but it trigged me to think how does Linux handles environment variables.  Where does the system store the environment variables?

According to the Wikipedia page, "in all Unix and Unix-like systems, as well as on Windows, each process has its own separate set of environment variables. By default, when a process is created, it inherits a duplicate run-time environment of its parent process, except for explicit changes made by the parent when it creates the child. At the API level, these changes must be done between running fork and exec."

Hey, this gave us some hit that the handling of environment variables is related with function call fork and exec.  Thanks to the people who created the manual page for the exec function call, I noticed it mentioned some external variable named environ as part of the SYNOPSIS section.

EXEC(3)                                                                                   Linux Programmer's Manual                                                                                   EXEC(3)


NAME

       execl, execlp, execle, execv, execvp, execvpe - execute a file


SYNOPSIS

       #include <unistd.h>

       extern char **environ;

       int execl(const char *pathname, const char *arg, ...

                       /* (char  *) NULL */);

The SEE ALSO section also mentioned there is a manual for environ in chapter 7 of the manuals.

SEE ALSO

       sh(1), execve(2), execveat(2), fork(2), ptrace(2), fexecve(3), system(3), environ(7)

The environ(7) man page provides more detailed information about the variable.

ENVIRON(7)                                                                                Linux Programmer's Manual                                                                                ENVIRON(7)


NAME

       environ - user environment


SYNOPSIS

       extern char **environ;


DESCRIPTION

       The variable environ points to an array of pointers to strings called the "environment".  The last pointer in this array has the value NULL.  (This variable must be declared in the user program, but is declared in the header file <unistd.h> if the _GNU_SOURCE feature test macro is defined.)  This array of strings is made available to the process by the exec(3) call  that  started  the  process.  When a child process is created via fork(2), it inherits a copy of its parent's environment.

       By convention the strings in environ have the form "name=value". 

As the manual page mentioned unistd.h, I was able to find the .h file and found the location where it's declared.

/* NULL-terminated array of "NAME=VALUE" environment variables.  */

extern char **__environ;

#ifdef __USE_GNU

extern char **environ;

#endif

The actual definition of the variable is in glibc's environ.c file.

/* This must be initialized; we cannot have a weak alias into bss.  */

char **__environ = NULL;

weak_alias (__environ, environ)

But how does it look like when a program is running?  To answer the question, I created a simple C program.

$ cat test.c 

#include <stdio.h>


int main(void)

{

int test_val=0;

printf("Hello World!\n");

return test_val;

}

Then compiled the program with gcc and its -ggdb option.

$ gcc -ggdb -o test test.c

It created the target binary file named test with debug information for gdb.  After that, we can debug the programe with gdb command.

$ gdb -q test

Reading symbols from test...

(gdb) list

1 #include <stdio.h>

2

3 int main(void)

4 {

5 int test_val=0;

6 printf("Hello World!\n");

7 return test_val;

8 }

In order to check the variables and their values, I set a breakpoint on line 6 and let the program run.

(gdb) break 6

Breakpoint 1 at 0x115c: file test.c, line 6.

(gdb) run

Starting program: /home/mrbob/test/test 


Breakpoint 1, main () at test.c:6

6 printf("Hello World!\n");

 With gdb, it confirmed that the variable named environ is avaiable in the current context.

(gdb) info address environ

Symbol "environ" is static storage at address 0x7ffff7fa9600.

(gdb) info symbol 0x7ffff7fa9600

environ in section .bss of /lib/x86_64-linux-gnu/libc.so.6

 0x7ffff7fa9600 is the address where the variable exists. According to the memory mapping of the process, it is in the memory space for this process.

(gdb) info proc mappings

process 18924

Mapped address spaces:


          Start Addr           End Addr       Size     Offset objfile

      0x555555554000     0x555555555000     0x1000        0x0 /home/mrbob/test/test

      0x555555555000     0x555555556000     0x1000     0x1000 /home/mrbob/test/test

      0x555555556000     0x555555557000     0x1000     0x2000 /home/mrbob/test/test

      0x555555557000     0x555555558000     0x1000     0x2000 /home/mrbob/test/test

      0x555555558000     0x555555559000     0x1000     0x3000 /home/mrbob/test/test

      0x7ffff7dba000     0x7ffff7ddc000    0x22000        0x0 /usr/lib/x86_64-linux-gnu/libc-2.31.so

      0x7ffff7ddc000     0x7ffff7f54000   0x178000    0x22000 /usr/lib/x86_64-linux-gnu/libc-2.31.so

      0x7ffff7f54000     0x7ffff7fa2000    0x4e000   0x19a000 /usr/lib/x86_64-linux-gnu/libc-2.31.so

      0x7ffff7fa2000     0x7ffff7fa6000     0x4000   0x1e7000 /usr/lib/x86_64-linux-gnu/libc-2.31.so

      0x7ffff7fa6000     0x7ffff7fa8000     0x2000   0x1eb000 /usr/lib/x86_64-linux-gnu/libc-2.31.so

      0x7ffff7fa8000     0x7ffff7fae000     0x6000        0x0 

      0x7ffff7fc9000     0x7ffff7fcd000     0x4000        0x0 [vvar]

      0x7ffff7fcd000     0x7ffff7fcf000     0x2000        0x0 [vdso]

      0x7ffff7fcf000     0x7ffff7fd0000     0x1000        0x0 /usr/lib/x86_64-linux-gnu/ld-2.31.so

      0x7ffff7fd0000     0x7ffff7ff3000    0x23000     0x1000 /usr/lib/x86_64-linux-gnu/ld-2.31.so

      0x7ffff7ff3000     0x7ffff7ffb000     0x8000    0x24000 /usr/lib/x86_64-linux-gnu/ld-2.31.so

      0x7ffff7ffc000     0x7ffff7ffd000     0x1000    0x2c000 /usr/lib/x86_64-linux-gnu/ld-2.31.so

      0x7ffff7ffd000     0x7ffff7ffe000     0x1000    0x2d000 /usr/lib/x86_64-linux-gnu/ld-2.31.so

      0x7ffff7ffe000     0x7ffff7fff000     0x1000        0x0 

      0x7ffffffde000     0x7ffffffff000    0x21000        0x0 [stack]

  0xffffffffff600000 0xffffffffff601000     0x1000        0x0 [vsyscall]

 More over, we can even check the strings defined in the array.

 (gdb) print  (char*)environ[0]

$14 = 0x7fffffffe347 "SHELL=/bin/bash"

(gdb) print  (char*)environ[1]

$15 = 0x7fffffffe357 "SESSION_MANAGER=local/Bob-Desktop:@/tmp/.ICE-unix/2702,unix/Bob-Desktop:/tmp/.ICE-unix/2702"

(gdb) print  (char*)environ[2]

$16 = 0x7fffffffe3b3 "QT_ACCESSIBILITY=1"

 

Comments

Popular posts from this blog

Secure Home Network with OPNsense

Dropping Into "initramfs" Prompt After Installing Ubuntu 22.04 LTS on to Dell VRTX Server

Ubuntu 22.04 Multipath I/O Problem with Dell VRTX