Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does a program inherit environment variables?

Tags:

c

linux

libc

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?

like image 535
nowox Avatar asked Nov 28 '22 10:11

nowox


1 Answers

The environment variables are passed down from the parent process as a third argument to main. The easiest way to discover this is to read the documentation for the system call execve, particularly this bit:

int execve(const char *filename, char *const argv[], char *const envp[]);

Description

execve() executes the program pointed to by filename. [...] argv is an array of argument strings passed to the new program. By convention, the first of these strings should contain the filename associated with the file being executed. envp is an array of strings, conventionally of the form key=value, which are passed as environment to the new program. Both argv and envp must be terminated by a NULL pointer. The argument vector and environment can be accessed by the called program's main function, when it is defined as:

int main(int argc, char *argv[], char *envp[])

The C library copies the envp argument into the environ global variable somewhere in its startup code, before it calls main: for instance, GNU libc does this in _init and musl libc does it in __init_libc. (You may find musl libc's code easier to trace through than GNU libc's.) Conversely, if you start a program using one of the exec wrapper functions that don't take an explicit environment vector, the C library supplies environ as the third argument to execve. Inheritance of environment variables is thus strictly a user-space convention. As far as the kernel is concerned, each program receives two argument vectors, and it doesn't care what's in them.

(Note that three-argument main is an extension to the C language. The C standard only specifies int main(void) and int main(int argc, char **argv) but it permits implementations to define additional forms (C11 Annex J.5.1 Environment Arguments). The three-argument main has been how environment variables work since Unix V7 if not longer, and is documented by Microsoft too — see What should main() return in C and C++?.)

like image 98
zwol Avatar answered Dec 09 '22 10:12

zwol