简体   繁体   中英

Why is this struct literal getting corrupted when passed by address in VS2013 but not gcc/clang?

I'm putting together a Visual Studio 2013 solution for a library I maintain . The library primarily uses arena allocation, so we have an allocator interface:

allocator.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

We also implement a wrapper around malloc , realloc and free :

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,
};

The system_allocator global is declared as extern in internal.h (which #include s allocator.h ), and it is exported as a symbol (in the .def file). However, apparently the struct is never initialized, because when my unit tests try to pass system_allocator by address to a function that dereferences the alloc member, they segfault with "Unhandled exception at 0x000007FEFAD3EB6D (hammer.dll) in hammer-test.exe: 0xC0000005: Access violation reading location 0xFFFFFFFFFFFFFFFF."

Inspecting the passed-in pointer in the debugger suggests that something is definitely not right:

  • 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 *) *

Particularly since, when I inspect the original struct literal, everything looks reasonable:

  • 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}
    • free = 0x000007fefad310d2 {hammer.dll!system_free}

I tried putting breakpoints on both the declaration and definition of system_allocator , and VS2013 informs me that "no executable code of the debugger's target code type is associated with this line." Does that imply that system_allocator isn't actually being initialised? (If so, then what do those 0x000007fefad31... addresses mean?)

I've never encountered this problem with gcc or clang, and this is my first time using VS. What am I missing?

EDIT: per chux's comment, the test which is failing is actually failing in setup. system_allocator gets passed like this:

HBitWriter *w = h_bit_writer_new(&system_allocator);

The line of code that's failing is the first line of HBitWriter *h_bit_writer_new(HAllocator* mm__) :

HBitWriter *writer = h_new(HBitWriter, 1);

where h_new is #defined as

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

I bet it is DLL related. You might have to put together the system_allocater member in the executable instead of passing a structure from the DLL containing the function addresses as the DLL sees them.

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

The problem with breakpoints is fairly easily explained. The Visual C++ debugger works by putting breakpoints in functions. You're trying to put a breakpoint outside a function. That's not supported.

At machine code level, global initializers are run before main in an executable, and from DllMain in case of a DLL. You've probably commented out the actual initializers for MSVC++, as the code is not valid C++. Yes, I know the question is tagged C, but MSVC++ doesn't support modern C.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM