【问题标题】:Segfault on calling a function in C在 C 中调用函数时的段错误
【发布时间】:2018-01-26 07:16:14
【问题描述】:

所以我正在构建一个虚拟机,并试图使其尽可能跨平台,突然遇到一个奇怪的错误。我的机器有一条let 指令,它为机器内存中的一个变量分配内存,并为该变量分配一个值。简而言之,let 函数调用getAddress 来获取变量的地址。 getAddress 检查变量是否已经定义,并返回地址。如果变量未定义,getAddress 调用memallocate 为变量分配内存,并返回地址。这是函数的定义:

static uint16_t memallocate(Machine *m, char *symbol){
    uint16_t allocationAddress = getFirstFree(*m);
    SymbolTable *newSymbol = (SymbolTable *)malloc(sizeof(SymbolTable));
    newSymbol->symbolName = strdup(symbol);
    newSymbol->next = NULL;
    newSymbol->mema = allocationAddress;
    if(m->symbolTable==NULL){
        m->symbolTable = newSymbol;
    }
    else{
        SymbolTable *temp = m->symbolTable;
        while(temp->next!=NULL)
            temp = temp->next;
        temp->next = newSymbol;
    }
    m->memory[allocationAddress].acquired = 1;
    m->memory[allocationAddress].data.value = 0;
    m->occupiedAddress++;
    return allocationAddress;
}

uint16_t getAddress(Machine *m, char *symbol){
    SymbolTable *table = m->symbolTable;
    while(table!=NULL){
        if(strcmp(symbol, table->symbolName)==0){
            return table->mema;
        }
        table = table->next;
    }
    uint16_t address = memallocate(m, symbol); // Here is the segfault happening
    return address;
}

这段代码在 Linux 上编译和运行得很好,但在 Windows 上,我在 memallocate 调用上遇到了段错误。由于memallocate 直接传递了getAddress 的参数,并且参数都是指针,所以它们不应该改变。但是在通过 CLion 进行调试时,我看到 memallocate 调用的参数乱七八糟,这表明某种堆栈违规(可能是)。同样,它只发生在 Windows 中。谁能告诉我我的代码出了什么问题? 该项目的完整代码可以在GitHub找到。

【问题讨论】:

  • 16 位地址?
  • 是的。该机器还模拟了一种类似 RAM 的存储形式。这是机器 RAM 的地址。
  • 调用执行时你检查栈上的内容了吗?
  • 你说的是gdb中的bt吗?那么是的@odin
  • 嗯,我的意思更像是检查堆栈指针周围的区域,看看是否可以在堆栈的任何位置找到正确的参数。这样您就可以确定问题是否在于堆栈指针位于错误的位置。这是在 Windows 上编译为 64 位、32 位还是 16 位?

标签: c windows debugging segmentation-fault virtual-machine


【解决方案1】:

我获取了你的代码并通过 valgrind 在 linux 上运行它:

==13768== Conditional jump or move depends on uninitialised value(s)
==13768==    at 0x109ABE: getAddress (in /home/vonaka/VirtualMachine/machine)
==13768==    by 0x10B714: let (in /home/vonaka/VirtualMachine/machine)
==13768==    by 0x109425: run (in /home/vonaka/VirtualMachine/machine)
==13768==    by 0x109F64: main (in /home/vonaka/VirtualMachine/machine)
==13768==  Uninitialised value was created by a heap allocation
==13768==    at 0x4C2BE7F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd
==13768==    by 0x109C2F: main (in /home/vonaka/VirtualMachine/machine)
==13768== 

所以(对我们来说很幸运)这不是 Windows 特定的问题。诀窍是在第一次调用getAddress 时(当m->symbolTableNULL)你在memallocate 的开头调用getFirstFree(*m),但是看看这个函数:

static uint16_t getFirstFree(Machine m) {
    uint16_t add = 0;
    while(m.memory[add].acquired)
        add++;
    return add;
}

i0number_of_instructions_in_your_input_file - 1 之间的m.memory[i].acquiredwriteInstruction 中初始化它们时都等于1,但m.memory[number_of_instructions_in_your_input_file].acquired 尚未初始化。

所以这样的事情会解决你的问题:

void writeInstruction(Machine *m, uint16_t add, Instruction ins) {
    m->memory[add].acquired = 1;
    m->memory[add].type = INSTRUCTION;
    m->memory[add].data.instruction = ins;
    m->occupiedAddress++;
    if(add + 1 < NUM_MEM)
        m->memory[add + 1].acquired = 0;
}

或者这可能更优雅(如果可行的话):

static uint16_t getFirstFree(Machine m) {
    uint16_t add = 0;
    while (m.memory[add].acquired && add < m.occupiedAddress)
        add++;
    return add;
}

编辑:

首先关于您的评论:

默认情况下,结构的成员初始化为0

这不是真的!

现在谈谈为什么没有malloc 时出现段错误以及它与 valgrind 警告的关系。

你有 Machine 类型的变量 m 和堆栈中的一些其他变量,m 包含 Cell memory[NUM_MEM] 并且每个 Cell 中都有 acquired (未初始化!)。您的输入文件包含 88 条指令,因此前 88 个 acquired 将在 88 次调用 writeInstruction 后正确初始化。然后程序开始通过调用一些函数来执行你的指令,包括memallocategetFirstFree。在这个循环中:

while(m.memory[add].acquired)
    add++;

对于任何add m.memory[add].acquired 很可能不同于0,所以一旦add 等于NUM_MEM 你就有段错误。

为什么malloc 没有发生这种情况?仅仅因为你很幸运(但这不是 运气),你的堆比堆栈“更干净”。为什么它只发生在 Windows 中?因为这次你没那么幸运(我什至在 Windows 中都没有段错误)。

【讨论】:

  • 首先,非常感谢您如此彻底地查看代码。真的很感激。现在事情是这样的:我告诉了一个正确的“段错误”作为错误,而不仅仅是 valgrind 中的警告。它们不会损害程序的执行,事实上,我很清楚它们。在 Windows 中运行它,你就会知道我在说什么。其次,获取的是表示特定Cell是否被占用的标志,并且只有在该单元发生写入时,无论是数据还是指令写入,它的值才会更改为1
  • 默认情况下,结构的成员被初始化为0,这就是为什么我没有明确地初始化它们。这是一个糟糕的黑客,我很快就会转过身来。现在实际的问题是由于结构Machine (奇怪地)太大而无法与4 或5 级函数调用一起保存在堆栈中,这会破坏堆栈。现在当我打电话给memallocate 时,由于空间不足,它的地址被弄乱了,因此出现了段错误。为了解决这个问题,我所做的只是在main() 中为Machine 结构执行malloc,而不是在本地声明它
  • 它解决了这个问题。同样,这只发生在 Windows 中。
  • @Subhranil,我想你不明白。我编辑了我的答案。
  • 我完全明白你在说什么。我要说的是,我通过 CLion 中的可视化调试器运行了程序,并确切地看到了发生了什么:传递给 memallocate 的指针变得一团糟,但在 getAddress 之前它们都很好。因为它们是相同的指针,所以它们不应该改变。此外,memallocate 中没有一行正在执行,段错误发生在调用本身上。您没有收到段错误,因为您在我昨天推送提交后克隆了 repo。我很确定您是否在 main() 本地声明 Machine m,并通过 &amp;m
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多