首页 > 解决方案 > 调用 std::make_unique 时出现分段错误

问题描述

我打电话时似乎遇到了分段错误std::make_unique<uint8_t []>。我很确定我知道分段错误发生在哪里,因为我使用了 gdb;但是,我不知道如何解决它。这是过滤掉不相关部分的源代码。

加载器.cc:

#include "loader.h"
... // Other code that would make this post unnecessarily long
Section::Section(Binary& bin, bfd *bfd_h, asection *as) : binary(bin) {
    int bfd_flags = bfd_section_flags(as);
    const char *secname;
    Section::SectionType sectype;

    sectype = Section::SectionType::NONE;
    if (bfd_flags & SEC_CODE) {
        sectype = Section::SectionType::CODE;
    } else if (bfd_flags & SEC_DATA) {
        sectype = Section::SectionType::DATA;
    }

    this->type = sectype;
    this->vma = bfd_section_vma(as);
    this->size = bfd_section_size(as);

    secname = bfd_section_name(as);
    this->name = std::string { secname != nullptr ? secname : "<unnamed>" };

    this->bytes = std::make_unique<uint8_t []>(this->size); // SEGFAULT RIGHT HERE
    if (!bfd_get_section_contents(bfd_h, as, this->bytes.get(), 0, this->size)) {
        throw std::runtime_error("Failed to read section");
    }

}

int Binary::load_binary(std::string& fname) {
    int ret;
    const bfd_arch_info_type *bfd_info;
    std::unique_ptr<bfd, bfd_deleter> bfd_h;

    try {
        bfd_h = open_bfd(fname);
    } catch (bfd_exception& be) {
        std::cerr << be.fname() << ": " << be.what() << " (" << be.errormsg() << ")" << std::endl;
        goto fail;
    }

    this->filename = std::string (fname);
    this->entry = bfd_get_start_address(bfd_h.get());

    this->type_str = std::string (bfd_h->xvec->name);

    switch (bfd_h->xvec->flavour) {
        case bfd_target_elf_flavour:
            this->type = Binary::BinaryType::ELF;
            break;
        case bfd_target_coff_flavour:
            this->type = Binary::BinaryType::PE;
            break;
        case bfd_target_unknown_flavour:
        default:
            std::cerr << "unsupported binary type ("
                  << bfd_h->xvec->name << ")" << std::endl;
            goto fail;
    }

    bfd_info = bfd_get_arch_info(bfd_h.get());
    this->arch_str = std::string{bfd_info->printable_name};

    switch (bfd_info->mach) {
        case bfd_mach_i386_i386:
            this->arch = Binary::BinaryArch::X86;
            this->bits = 32;
            break;
        case bfd_mach_x86_64:
            this->arch = Binary::BinaryArch::X86;
            this->bits = 64;
            break;
        default:
            std::cerr << "unsupported architecture (" << bfd_info->printable_name·
                      << bfd_info->printable_name << ")" << std::endl;
            goto fail;
    }

    this->load_symbols(bfd_h.get());
    this->load_dynsym(bfd_h.get());

    if (this->load_sections(bfd_h.get()) < 0) goto fail;

    ret = 0;
    goto success;

    fail:
        ret = -1;

    success:
        return ret;
}

... // Other code that would make this post unnecessarily long

装载机.h:

#include <memory>
#include <vector>
#include <bfd.h>
#include <inttype.h>
... // Other code that would make this post unnecessarily long
class Section {
    public:
        enum class SectionType { NONE, CODE, DATA };
        Section(Binary& bin);
        Section(Binary& bin, bfd *bfd_h, asection *as);
        bool contains(uint64_t addr);
        void info();

    private:
        friend class Binary;
        Binary& binary;
        std::string name;
        SectionType type;
        uint64_t vma;
        uint64_t size;
        std::unique_ptr<uint8_t []> bytes;

};

class Binary {
    public:
        enum class BinaryType { AUTO, ELF, PE };
        enum class BinaryArch { NONE, X86 };

        Binary();
        Section* get_section_text();
        int load_binary(std::string &fname);
        void info();

    private:
        std::string filename;
        BinaryType type;
        std::string type_str;
        BinaryArch arch;
        std::string arch_str;
        unsigned bits;
        uint64_t entry;
        std::vector<Section> sections;
        std::vector<Symbol> symbols;

        int load_symbols(bfd *bfd_h);
        int load_dynsym(bfd *bfd_h);
        int load_sections(bfd *bfd_h);
... // Other code that would make this post unnecessarily long
};

这是 gdb 返回的回溯:

(gdb) bt
#0  0x00007fffff335b9f in unlink_chunk (p=p@entry=0x802b7d0, av=0x7fffff46cb80 <main_arena>) at malloc.c:1453
#1  0x00007fffff338881 in _int_malloc (av=av@entry=0x7fffff46cb80 <main_arena>, bytes=bytes@entry=36) at malloc.c:4041        #2  0x00007fffff339a84 in __GI___libc_malloc (bytes=36) at malloc.c:3058
#3  0x00007fffff546045 in operator new(unsigned long) () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x0000000008003b94 in std::make_unique<unsigned char []> (__num=36) at /usr/include/c++/10/bits/unique_ptr.h:966
#5  Section::Section (this=0x7ffffffedd80, bin=..., bfd_h=0x801b730, as=0x801dbf8) at loader.cc:66
#6  0x0000000008003d53 in Binary::load_sections (this=this@entry=0x7ffffffedec0, bfd_h=bfd_h@entry=0x801b730)
    at loader.cc:122
#7  0x000000000800487e in Binary::load_binary (this=0x7ffffffedec0, fname=...) at loader.cc:185
#8  0x0000000008002451 in main (argc=<optimized out>, argv=<optimized out>) at loader_demo.cc:13

此代码基于“实用二进制分析”中的代码,因此请原谅 goto。我也在使用 bfd 库。

标签: c++segmentation-faultbfd

解决方案


TLDR:我使用的这本书有一个函数 A 和另一个与之相似的函数 B。我将代码输入到我的文本编辑器中,并在我的编辑器中编写了函数 A 的代码。然后我复制并粘贴 A 并将其修改为函数 B。除了我忘记进行一项更改,这就是导致段错误的原因。

我终于发现了为什么我的程序会出现段错误……有点。事实证明,即使在 make_unique 附近,任何代码都没有问题;但是,我认为是这种情况,因为 gdb 输出的堆栈跟踪表明错误出现在 make_unique。

该错误实际上来自复制代码。书中有一个名为 load_symbols_bfd 的函数的代码片段。我将代码输入到我的文本编辑器中,并将代码修改为更现代的 C++。有一个类似的函数称为 load_dynsym_bfd。我想我可以复制 load_symbols_bfd 的修改版本并将其更改为我自己的 load_dynsym_bfd 版本。但是,我忘记更改 load_dynsym_bfd 的修改版本中的一个函数名称,该函数名称已在 load_symbols_bfd 中;我忘记将 bfd_canonicalize_symtab 更改为 bfd_canonicalize_dynamic_symtab。当包含 bfd_canonicalize_symtab 的行在我的 load_dynsym_bfd 修改版本中被注释掉时,段错误消失了。


推荐阅读