【问题标题】:Segmentation fault disappear with gdb in ubunutu on windowsWindows 上 ubuntu 中的 gdb 导致分段错误消失
【发布时间】:2018-03-21 01:09:09
【问题描述】:

我的任务是找出以下代码中的错误并修复它:

/* $Id: count-words.c 858 2010-02-21 10:26:22Z tolpin $ */

#include <stdio.h>
#include <string.h>

/* return string "word" if the count is 1 or "words" otherwise */
char *words(int count) {
  char *words = "words";
  if(count==1) 
    words[strlen(words)-1] = '\0';
  return words;
}

/* print a message reportint the number of words */
int print_word_count(char **argv) {
  int count = 0;
  char **a = argv;
  while(*(a++))
    ++count;
  printf("The sentence contains %d %s.\n", count, words(count));
  return count;
}

/* print the number of words in the command line and return the number as the exit code */
int main(int argc, char **argv) {
  return print_word_count(argv+1);
}

该程序适用于给它的每个单词数量,除了一个单词。使用./count-words hey 运行它会导致分段错误。

我正在使用官方 Ubuntu 应用程序在 Windows 10 上的 Linux 子系统上运行我的代码(我理解它至少是这么称呼的......)。

从终端运行程序时,我确实遇到了分段错误,但使用 gdb,由于某种原因程序运行正常:

(gdb) r hey
Starting program: .../Task 0/count-words hey
The sentence contains 1 word.
[Inferior 1 (process 87) exited with code 01]
(gdb)

在第 9 行添加断点并单步执行代码后,我得到了:

(gdb) b 9
Breakpoint 1 at 0x400579: file count-words.c, line 9.
(gdb) r hey
Starting program: /mnt/c/Users/tfrei/Google Drive/BGU/Semester F/Computer Architecture/Labs/Lab 2/Task 0/count-words hey

Breakpoint 1, words (count=1) at count-words.c:9
9         if(count==1)
(gdb) s
10          words[strlen(words)-1] = '\0';
(gdb) s
strlen () at ../sysdeps/x86_64/strlen.S:66
66      ../sysdeps/x86_64/strlen.S: No such file or directory.
(gdb) s
67      in ../sysdeps/x86_64/strlen.S
(gdb) s
68      in ../sysdeps/x86_64/strlen.S
(gdb)

奇怪的是,当我从“真正的”Ubuntu(使用 Windows 10 上的虚拟机)运行相同的东西时,gdb 上确实发生了分段错误。

我倾向于认为这与我的运行时环境(“Windows 上的 Ubuntu”)有关,但找不到任何可以帮助我的东西。

这是我的生成文件:

all: 
    gcc -g -Wall -o count-words count-words.c 

clean: 
    rm -f count-words 

提前致谢

【问题讨论】:

  • char *words = "words"; 是指向字符串文字的指针,修改它是未定义的行为,并且在大多数情况下会导致段错误,因为它是只读内存。你不能修改它。
  • 不要写这样的代码。
  • @Michi 我觉得奇怪的是 OP 的 gdb 没有显示段错误。
  • 那是 UB。不要要求解释为什么错误的代码有时会起作用。
  • 有更好的复数方式,例如char *plural[] = { "", "s" }; printf("%d word%s", count, plural[count != 1]);

标签: c linux windows ubuntu gdb


【解决方案1】:

我在问为什么 gdb 没有发生这种情况

当在真实(或虚拟)UNIX 系统上运行时,它确实发生在 GDB 中。

在奇怪的“Windows 上的 Ubuntu”环境下运行时不会发生这种情况,因为那个环境正在做疯狂的事情。特别是,由于某种原因,Windows 子系统通常映射只读部分(.rodata,可能还有.text)具有可写权限(这就是程序不再崩溃的原因),但仅当您在调试器下运行程序时。

我不知道 Windows 究竟为什么会这样做。

请注意,调试器确实需要写入(只读).text 部分才能插入断点。在真正的 UNIX 系统上,这是通过 ptrace(PTRACE_POKETEXT, ...) 系统调用实现的,它更新只读页面,但将其保留为只读,以供下级(正在调试)进程使用。

我猜测 Windows 没有完美地模拟这种行为(特别是在更新页面后不会对页面进行写保护)。

附:一般来说,使用“Windows 上的 Ubuntu”来学习 Ubuntu 会充满这样的陷阱。改用虚拟机可能会好得多

【讨论】:

  • 谢谢,我明白了。我想我将不得不适应虚拟机(它对我来说工作相对较慢,虽然我给了它足够的内存......不知道为什么它很慢,但我没有多想。这就是我选择在 Windows 上使用 Ubuntu 的原因)。再次感谢
【解决方案2】:

这个函数是错误的

char *words(int count) {
  char *words = "words";
  if(count==1) 
    words[strlen(words)-1] = '\0';
  return words;
}

指针words 指向字符串文字"words"。修改字符串 文字是未定义的行为,在大多数系统中,字符串文字存储在 只读内存,所以这样做

    words[strlen(words)-1] = '\0';

会导致段错误。这就是您在 Ubuntu 中看到的行为。我不知道 其中字符串文字存储在 Windows 可执行文件中,但修改字符串 文字是未定义的行为,任何事情都可能发生,尝试毫无意义 推断为什么有时事情有效,为什么有时事情不工作。那是 未定义行为的性质。

编辑

Pablo 谢谢,但我不是在问错误本身,以及为什么会发生分段错误。我在问为什么 gdb 没有发生这种情况。抱歉,如果不够清楚。

我不知道为什么它没有发生在你身上,但是当我在我的 gdb 上运行你的代码时,我得到:

Reading symbols from ./bug...done.
(gdb) b 8
Breakpoint 1 at 0x6fc: file bug.c, line 8.
(gdb) r hey
Starting program: /tmp/bug hey

Breakpoint 1, words (count=1) at bug.c:8
8       words[strlen(words)-1] = '\0';
(gdb) s

Program received signal SIGSEGV, Segmentation fault.
0x0000555555554713 in words (count=1) at bug.c:8
8       words[strlen(words)-1] = '\0';
(gdb) 

【讨论】:

  • “但修改字符串文字是未定义的行为,任何事情都可能发生。”,也许 OP 很幸运,看看他的 PC 是如何烧毁的。他永远不会那样编码。
  • 那会很有趣;)实际上,我没有编写该代码。它是由我的课程讲师编写的,用作指针和 gdb 调试的练习。其中的一些要点是教导不要编写那样的代码。
猜你喜欢
  • 1970-01-01
  • 2020-12-28
  • 1970-01-01
  • 2013-12-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多