为啥这个结构文字在 VS2013 中通过地址而不是 gcc/clang 时会损坏?

Posted

技术标签:

【中文标题】为啥这个结构文字在 VS2013 中通过地址而不是 gcc/clang 时会损坏?【英文标题】:Why is this struct literal getting corrupted when passed by address in VS2013 but not gcc/clang?为什么这个结构文字在 VS2013 中通过地址而不是 gcc/clang 时会损坏? 【发布时间】:2015-01-12 00:10:33 【问题描述】:

我正在为a library I maintain 整合一个 Visual Studio 2013 解决方案。该库主要使用竞技场分配,因此我们有一个分配器接口:

分配器.h

#define HAMMER_ALLOCATOR__H__
#include <sys/types.h>

#ifdef __cplusplus
extern "C" 
#endif

typedef struct HAllocator_ 
    void* (*alloc)(struct HAllocator_* allocator, size_t size);
    void* (*realloc)(struct HAllocator_* allocator, void* ptr, size_t size);
    void (*free)(struct HAllocator_* allocator, void* ptr);
 HAllocator;
[... API functions ... ]
#ifdef __cplusplus

#endif
#endif

我们还在mallocreallocfree 周围实现了一个包装器:

system_allocator.c

#include <string.h>
#include <stdlib.h> 
#include "internal.h"

void* system_alloc(HAllocator *allocator, size_t size)  
    void* ptr = malloc(size + sizeof(size_t));
    *(size_t*)ptr = size;
    return (uint8_t*)ptr + sizeof(size_t);


void* system_realloc(HAllocator *allocator, void* ptr, size_t size) 
    if (ptr == NULL)
        return system_alloc(allocator, size);
    ptr = realloc((uint8_t*)ptr - sizeof(size_t), size + sizeof(size_t));
    *(size_t*)ptr = size;
    return (uint8_t*)ptr + sizeof(size_t);


void system_free(HAllocator *allocator, void* ptr) 
    if (ptr != NULL)
        free((uint8_t*)ptr - sizeof(size_t));


HAllocator system_allocator = 
    .alloc = &system_alloc,
    .realloc = &system_realloc,
    .free = &system_free,
;

system_allocator 全局在 internal.h(其中 #includes allocator.h)中声明为 extern,并作为符号导出(在 .def 文件中)。但是,显然该结构从未初始化,因为当我的单元测试尝试通过地址将system_allocator 传递给取消引用alloc 成员的函数时,它们在hammer-test 中出现“0x000007FEFAD3EB6D (hammer.dll) 处的未处理异常”的段错误.exe: 0xC0000005: 访问冲突读取位置 0xFFFFFFFFFFFFFFFF。"

在调试器中检查传入的指针表明某些事情肯定是不正确的:

mm__ 0x000000013fb0a094 hammer-test.exe!HAllocator_ system_allocator alloc=0x25ff00019ff625ff realloc=... HAllocator_ * alloc 0x25ff00019ff625ff void * (HAllocator_ *, unsigned __int64) * realloc 0x9ffa25ff00019ff8 void * (HAllocator_ *, void *, unsigned __int64) * free 0x00019ffc25ff0001 void (HAllocator_ *, void *) *

特别是因为当我检查原始结构文字时,一切看起来都很合理:

system_allocator = alloc=0x000007fefad31410 hammer.dll!system_alloc realloc=0x000007fefad313f7 hammer.dll!system_realloc ... alloc = 0x000007fefad31410 hammer.dll!system_alloc realloc = 0x000007fefad313f7 hammer.dll!system_realloc 免费 = 0x000007fefad310d2 hammer.dll!system_free

我尝试在system_allocator 的声明和定义上都设置断点,VS2013 告诉我“调试器的目标代码类型的可执行代码与此行无关。”这是否意味着 system_allocator 实际上并没有被初始化? (如果是这样,那么那些 0x000007fefad31... 地址是什么意思?)

我从来没有遇到过 gcc 或 clang 的这个问题,这是我第一次使用 VS。我错过了什么?

编辑:根据 chux 的评论,失败的测试实际上是设置失败。 system_allocator 像这样传递:

HBitWriter *w = h_bit_writer_new(&system_allocator);

失败的代码行是HBitWriter *h_bit_writer_new(HAllocator* mm__)的第一行:

HBitWriter *writer = h_new(HBitWriter, 1);

其中h_new 被#定义为

#define h_new(type, count) ((type*)(mm__->alloc(mm__, sizeof(type)*(count))))

【问题讨论】:

这是一个有趣的问题,希望你能得到答案。它编译好吗?没有警告? 有一些大小转换的警告,但仅此而已,都与分配器无关。 迂腐建议:return (char*)ptr + sizeof(size_t);. system_realloc( x, y, 0)system_free(x,y) 的工作方式不同。可能还想评论system_alloc(x, 0) 建议发布“当我的单元测试尝试通过 system_allocator”的代码。 【参考方案1】:

我敢打赌它与 DLL 有关。您可能必须将 system_allocater 成员放在可执行文件中,而不是从 DLL 传递一个结构,该结构包含 DLL 看到的函数地址。

http://msdn.microsoft.com/en-us/library/windows/desktop/ms683212%28v=vs.85%29.aspx

【讨论】:

不。 Win32 地址在进程范围内是有效的,并且 DLL 是在进程中加载​​的。【参考方案2】:

断点的问题很容易解释。 Visual C++ 调试器通过在函数中放置断点来工作。您试图在函数之外放置断点。不支持。

在机器代码级别,全局初始化程序在可执行文件中在 main 之前运行,在 DLL 的情况下从 DllMain 开始。您可能已经注释掉了 MSVC++ 的实际初始化程序,因为代码不是有效的 C++。是的,我知道问题标记为 C,但 MSVC++ 不支持现代 C。

【讨论】:

以上是关于为啥这个结构文字在 VS2013 中通过地址而不是 gcc/clang 时会损坏?的主要内容,如果未能解决你的问题,请参考以下文章

使用 IP 地址而不是 localhost 浏览网站

为啥VS2013告诉我使用scanf_s?

软件中断VS系统调用

此 SQL 代码在 SMMS 中执行时有效,但在 .Net 中通过 ExecuteNonQuery() 调用时无效,为啥?

为啥复制构造函数应该在 C++ 中通过引用来接受它的参数?

在 Linux 中通过 Socket 发送数据而没有连接时崩溃