1. Overview

In this tutorial, we’ll see how to set environment variables in gdb (GNU Debugger). We can use gdb to check programs for errors or optimize them. We’ll understand the setup and see different ways to define these environment variables inside gdb and directly from the command line.

As an initial note, we should consider if we truly need environment variables. If we plan to use them just to pass some information to a program, it may be easier to use command-line arguments. This might not be possible in all cases though.

2. Understanding the Setup

Before talking about environment variables, let’s see the code we’ll use, its compilation, and the debugger itself.

2.1. Sample Code

We’ll use C code to discuss environment variables and debugging with gdb:

$ cat main.c 
#include <stdlib.h> // for the getenv function
#include <stdio.h>  // for the printf function
 
int main(int argc, char** argv)
{
  if (argc >= 2) {
    printf("argv[1] = %s\n", argv[1]);
  } else { 
    printf("No arguments are provided!\n");
  }
  
  char* env_var = getenv("ENV_VAR");
  if (env_var != NULL) {
    printf("ENV_VAR = %s\n\n", env_var);
  } else { 
    printf("ENV_VAR is not defined!\n\n");
  }
  
  return 0; 
}

The program starts by importing the required headers. Then we perform two checks and print some values on the screen before returning.

First, we check the number of command-line arguments provided when calling the program. If we receive two or more (one of them being the program itself), we print the first argument. Otherwise, we print a message stating that there are no arguments.

Then, we get the environment variable named ENV_VAR. If it exists, we print it; if it doesn’t, we print a message saying that it doesn’t exist. Finally, we return from the main() function.

2.2. Code Compilation and Execution

C is a compiled language so we need gcc (or another compiler) to compile our program. This process takes the code and generates a binary file that we can execute:

$ gcc main.c -g -o program.out

We’re using the -g flag to request gcc to generate extra information that the debugger can later use. We also want the output to be named program.out.

Once we’ve got our binary file, we can run it and see what it returns:

$ ./program.out
No arguments are provided!
ENV_VAR is not defined!

Providing an argument changes the output we receive:

$ ./program.out cli_arg_value
argv[1] = cli_arg_value
ENV_VAR is not defined!

We’ll use the second check, the one for the presence of the environment variable, in the remainder of the discussion.

2.3. gdb: the GNU Debugger

Finally, let’s look at the debugger itself. gdb is the GNU debugger developed to run on many systems (including Linux) and which supports numerous programming languages (including Assembly, C, C++, Go, Rust, and many others).

When we call gdb (regardless of using a program as an argument or not), we’ll see an interactive console for us to debug:

$ gdb ./program.out
GNU gdb (GDB) 13.2
Copyright (C) 2023 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".
(gdb) 

*Thus, the commands that we have to write in the gdb will be preceded by (gdb) to differentiate them from the Linux shell (preceded by $).*

3. Setting Environment Variables Within gdb

Environments are a set of variables and values. The shell for Linux has our environment variables defined and we inherit this environment in all the programs we run within the shell. gdb is no exception. But when we invoke gdb, we might want a different environment (or at least some different variables).

3.1. Basic Manipulation of Environment Variables

In general, we can access the environment of a process with PID through the file located in /proc//environ. Within gdb, we can also ask for all the variables in the environment:

(gdb) show environment
SHELL=/bin/bash
WINDOWID=29360138
COLORTERM=rxvt-xpm
...
LINES=52
COLUMNS=225

We can shorten environment as env in gdb. To avoid showing all the environment, we can check the value of a single environment variable:

(gdb) show env EDITOR
EDITOR=vim

We can also add and remove environment variables within gdb, but these changes won’t update the environment once we exit gdb. All environment variables are simple strings. We can define a variable:

(gdb) set env VAR_NAME_1 = var_name_value
(gdb) show env VAR_NAME_1
VAR_NAME_1 = var_name_value

For clarity, we show the spaces around = but they aren’t required. If we don’t provide the value, the variable is created but set to null:

(gdb) set env VAR_NAME_2
Setting environment variable "VAR_NAME_2" to null value.
(gdb) show env VAR_NAME_2
VAR_NAME_2 = 

We can also remove a variable from the environment:

(gdb) unset env VAR_NAME_1
(gdb) show env VAR_NAME_1
Environment variable "VAR_NAME_1" not defined.

Removing a variable from the environment is different than assigning an empty value to it.

We should be careful not to define environment variables in files that are called in non-interactive shells. This can conflict with variables whose value we change inside gdb. Thus, we might consider moving these environment variables to files run only on login (such as .profile).

3.2. The PATH Environment Variable

There are some environment variables more relevant than others. For example, the PATH environment variable contains a list of the paths that Linux searches in when looking for executables. The PATH environment variable is important for gdb, so there are some inbuilt commands specially for it. We can directly see the current PATH variable:

(gdb) show paths
Executable and object file path: /usr/local/bin:/usr/local/sbin:/usr/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl

We can modify the PATH variable within gdb as well:

(gdb) path /path/to/libraries
Executable and object file path: /path/to/libraries:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/lib/jvm/default/bin:/usr/bin/site_perl

We’ve added the directory /path/to/libraries in front of the PATH variable. Changing the PATH environment variable inside gdb doesn’t change the PATH that gdb uses and neither does it change the PATH of the shell once we exit gdb. We can give several directories separated by spaces or colons. If the directory was already in the PATH, it’s moved to the front.

4. Setting Environment Variables From the Command Line

Now that we know more about environment variables inside gdb, we’ll discuss how to define an environment variable directly from the command line.

4.1. Environment Variable Definition Before gdb Execution

We can simply define ENV_VAR (the variable name we’ve used in our code) in the shell before running gdb:

$ ENV_VAR="env_var_value" gdb ./program.out 
...
(gdb) r
Starting program: ./program.out 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
No arguments are provided!
ENV_VAR = env_var_value
 
[Inferior 1 (process 11325) exited normally]
(gdb)

Once we get the gdb shell, we can use r to run the program. We can see in the output of our program that we provided no arguments but our ENV_VAR was processed.

However, the best approach is to use env to modify the environment we’re running gdb in. We can get the same results as before by using env:

$ env ENV_VAR="env_var_value" gdb ./program.out 
...
(gdb) r cli_arg_value
Starting program: ./program.out 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
argv[1] = cli_arg_value
ENV_VAR = env_var_value
 
[Inferior 1 (process 11460) exited normally]
(gdb)

In this case, we pass an argument (cli_arg_value) to the program. Using env is a better solution because it provides more flexibility. For example, we can use the -i flag to start with an empty environment:

$ env -i ENV_VAR="env_var_value" gdb ./program.out 
...
(gdb) show env
ENV_VAR=env_var_value
LINES=53
COLUMNS=112
(gdb)

We only have our ENV_VAR defined (together with two variables for LINES and COLUMNS defined by gdb). When using env, we’ll have our original environment variables restored once the gdb finishes execution.

4.2. Environment Variable Through gdb Arguments

gdb has the option to provide the arguments directly from the command line. We can embed our program with command-line arguments and env:

$ gdb --args env ENV_VAR="env_var_value" ./program.out cli_arg_value
...
(gdb) r
Starting program: /usr/bin/env ENV_VAR=env_var_value ./program.out cli_arg_value
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
process 12065 is executing new program: ./program.out
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
argv[1] = cli_arg_value
ENV_VAR = env_var_value
 
[Inferior 1 (process 12065) exited normally]
(gdb) show env ENV_VAR
Environment variable "ENV_VAR" not defined.
(gdb)

This approach may look similar to the previous one but it’s different. When using the –args approach combined with env, only the program receives this special environment. That is why the variable ENV_VAR isn’t shown in gdb when we request the environment.

5. Conclusion

In this article, we’ve learned several methods to set up environment variables inside gdb. We looked at a sample code and two main ways we can proceed: we can define the environment variables once we’re inside the gdb shell or directly in the Linux shell.