When I use the function getenv()
from the Standard C Library, my program inherit the environment variables from its parent.
Example:
$ export FOO=42
$ <<< 'int main() {printf("%s\n", getenv("FOO"));}' gcc -w -xc - && ./a.exe
42
In libc, the environ
variable is declared into environ.c
. I am expecting it to be empty at the execution, but I get 42
.
Going a bit further getenv
can be simplified as follow:
char * getenv (const char *name)
{
size_t len = strlen (name);
char **ep;
uint16_t name_start;
name_start = *(const uint16_t *) name;
len -= 2;
name += 2;
for (ep = __environ; *ep != NULL; ++ep)
{
uint16_t ep_start = *(uint16_t *) *ep;
if (name_start == ep_start && !strncmp (*ep + 2, name, len)
&& (*ep)[len + 2] == '=')
return &(*ep)[len + 3];
}
return NULL;
}
libc_hidden_def (getenv)
Here I will just get the content of the __environ
variable. However I never initialized it.
So I get confused because environ
is supposed to be NULL
unless my main function is not the real entry point of my program. Perhaps gcc
is ticking me by adding an _init
function that is part of the standard C library.
Where is environ
initialized?
There is no mystery here.
First, the shell forks. Forked process obviously has the same environment. Then a new program is executed in the child. The syscall in question is
execve
, which amongst other things accepts a pointer to an environment.So there, what environment is set after execing a binary depends entirely on the code which was doing the exec.
All this is can be easily seen by running strace.
EDIT: since the question was edited to ask about
environ
:When you execute a dynamically linked binary, the very first userspace code doing anything comes from the loader. The loader amongst other things sets up variables like
argc
,argv
orenviron
and only then callsmain()
from the binary.Once more, sources for all this are freely available. While glibc's sources are rather hard to read due to atrocious formatting, BSD ones are easy and conceptually equivalent enough.
http://code.metager.de/source/xref/freebsd/libexec/rtld-elf/rtld.c#389