为什么从 DLL 加载一块内存仅在第二次调用 memmove 时崩溃?

Why does loading a block of memory from a DLL only crash at the second call to memmove?

这是有问题的class(只有与这个问题相关的功能)及其依赖的一切(都是我自己写的)。它提供了一个 DLL 接口。

  struct MemRegion {
    const uint64_t address;
    const uint64_t size;
  };

  enum Version {
    VERSION_US,
    VERSION_JP
  };

  const struct MemRegion SEGMENTS[2][2] = {
      {{1302528, 2836576},
       {14045184, 4897408}},
      {{1294336, 2406112},
       {13594624, 4897632}},
  };
  
  using Slot = array<vector<uint8_t>, 2>;
  class Game {
  private:
    Version m_version;
    HMODULE m_dll;
    const MemRegion* m_regions;

  public:
    Game(Version version, cstr dll_path) {
      m_version = version;
      m_dll = LoadLibraryA(dll_path);

      if (m_dll == NULL) {
        unsigned int lastError = GetLastError();
        cerr << "Last error is " << lastError << endl;
        exit(-2);
      }
      // this is a custom macro which calls a function in the dll
      call_void_fn(m_dll, "sm64_init");
      m_regions = SEGMENTS[version];
    }

    ~Game() {
      FreeLibrary(m_dll);
    }

    void advance() {
      call_void_fn(m_dll, "sm64_update");
    }

    Slot alloc_slot() {
      Slot buffers = {
        vector<uint8_t>(m_regions[0].size),
        vector<uint8_t>(m_regions[1].size)
      };
      return buffers;
    }

    void save_slot(Slot& slot) {
      for (int i = 0; i < 2; i++) {
        const MemRegion& region = m_regions[i];
        vector<uint8_t>& buffer = slot[i];
        cerr << "before memmove for savestate" << endl;
        memmove(buffer.data(), reinterpret_cast<void* const>(m_dll + region.address), region.size);
        cerr << "after memmove for savestate" << endl;
      }
    }
  };

当我调用 save_slot() 时,它应该将两个内存块复制到几个 vector<uint8_t> 中。不过,情况似乎并非如此。该函数完成第一个副本,但在第二个 memcpy 处抛出分段错误。为什么它只发生在第二个副本中,我该如何解决此类问题?

编辑 1:这是程序终止时 GDB 给我的信息:

Thread 1 received signal SIGSEGV, Segmentation fault.
0x00007ffac2164452 in msvcrt!memmove () from C:\Windows\System32\msvcrt.dll

编辑 2:我尝试单独访问这些片段。它有效,但由于某种原因,我无法访问同一程序中的两个段。

我发现 HMODULE 等同于 void*。由于您不能真正在 void* 上使用指针算法,因此您必须将其转换为 uint8_t* 或等价物才能正确获得偏移量。

实际情况如下:

    void save_state(Slot& slot) {
      uint8_t* const _dll = (uint8_t*)((void*)m_dll);
      for (int i = 0; i < 2; i++) {
        MemRegion segment = m_regions[i];
        std::vector<uint8_t>& buffer = slot[i];
        memmove(&buffer[0], _dll + segment.address, segment.size);
      }
    }