为什么我在gdb中无法打印出环境变量?

9
#include <unistd.h>
#include <stdio.h>

extern char **environ;
int main(int argc, char *argv[]) { 
  int i = 0;
  while(environ[i]) {
    printf("%s\n", environ[i++]);
  }
  return 0;
}

这是我的运维:

(gdb) n
8       printf("%s\n", environ[i++]);
(gdb) p environ[i]
Cannot access memory at address 0x0
(gdb) n
LOGNAME=root
7     while(environ[i]) {

正如您所看到的,printf 可以打印出 environ[i],但是 p environ[i] 给了我 Cannot access memory at address 0x0 的错误信息,为什么呢?


奇怪。我重现了这个问题。如果我在while循环之前添加ptr = environ,那么我能够使用ptr,但是environ仍然是一个空指针。 - AProgrammer
显然,gdb在调试程序时启动进程时没有环境。 - pmg
1
@pmg,它有一个环境。我经常在 gdb 中使用 set env 来确定这一点。 - AProgrammer
5个回答

10

gdb错误地解析了environ符号。我不知道为什么。请看下面的原因。

但是你可以进行测试。将程序更改为:

#include <unistd.h>
#include <stdio.h>

extern char **environ;
int main(int argc, char *argv[]) {
  int i = 0;
  printf("%p\n", &environ);
  while(environ[i]) {
    printf("%s\n", environ[i++]);
  }
  return 0;
}

现在让我们在调试器中运行它。

(gdb) n
7         printf("%p\n", &environ);
(gdb) n
0x8049760
8         while(environ[i]) {
(gdb) p &environ
$1 = (char ***) 0x46328da0
(gdb)

所以,在链接期间,实际程序已将 environ 解析为地址 0x8049760。 当 gdb 想要访问 environ 符号时,它解析为不同的地址0x46328da0。

编辑。 看起来你的 environ 符号实际上链接到 environ@@GLIBC_2.0 符号。 在 gdb 中键入以下内容:

(gdb) p environ

并按下 Tab 键(两次),它会自动完成符号。这会产生:

(gdb) p environ
environ             environ@@GLIBC_2.0

environ@@GLIBC_2.0 是实际链接到 extern char **environ

打印此内容会产生与程序看到的相同的地址 0x8049760:

(gdb) p &'environ@@GLIBC_2.0'
$9 = ( *) 0x8049760
(gdb) p ((char**)'environ@@GLIBC_2.0')[i]
$10 = 0xbffff6ad "XDG_SESSION_ID=1"

因此,某个时刻 glibc 已将 environ 符号废弃,并添加了新版本。


这是否解释了为什么 printfp 中的 environ 不同? - cpuer
@nos 我遇到了一个反转的情况,即gdb可以完美地打印environ,但是共享库内部的程序不能(得到0x00指针),有什么想法吗? - Terry Wu

0

在C/C++中,可以使用在stdlib.h中定义的getenv()函数来访问环境变量。然而,通过使用主函数的envp参数,您可以使用以下示例迭代环境变量。

#include <stdio.h>

int main(int argc, char *argv[], char *envp[])
{
  char **next = envp;

  while (*next) 
  {
    printf("%s\n", *next);
    next++;
  }
  return 0;


}

在 Mac 上测试通过


GDB可以调用函数。例如:call (char*) getenv("PATH")(它似乎假定返回值为整数,没有进行类型转换)。当然,该函数必须可用,但如果程序关心环境变量,那么它肯定是可用的! - Cascabel
这不是问题,_environ / environ变量并非所有平台都直接支持。 - grundprinzip
我的观点是,通常有一种方法可以查看环境变量,而不必关心是否存在environ_environ - Cascabel

0

像 grundprinzip 所说,使用 getenv(sz) 并记得对返回值进行空值检查

或者,

#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[], char*[] environ) { 
  int i = 0;
  while(environ[i]) {
    printf("%s\n", environ[i++]);
  }
  return 0;
}

0

可能正在调试的进程是以以下方式启动的

execve(binary, NULL, NULL);

而且 extern char **environ 获取到了第二个 NULL,即使有环境可用。

稍作修改,您的程序可以独立运行并在 gdb 下运行。

/* #include <unistd.h> */           /* no more environ */
#include <stdio.h>

/* extern char **environ; */        /* no more environ */
int main(int argc, char *argv[]) { 
  int i = 0;
  char **ptr = argv + argc + 1;     /* points to environment, in Un*x */
  while(ptr[i]) {
    printf("%s\n", ptr[i++]);
  }
  return 0;
}

我不知道为什么和如何在gdb内部将NULL转换为正确的值。


-1

你的代码没有问题。我在我的电脑上试过了,它打印出了预期的环境变量。你不应该需要使用getenv()。

你是从终端运行这个程序吗?如果不是,那么你应该这样做。其他执行应用程序的方法可能会调用你的二进制文件而不传递环境变量。

从终端运行"env"命令时,你的输出是什么?它应该输出与你的程序相同的列表。在我的电脑上就是这样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接