Пишу небольшую сишную софтину. Использую в ней библиотеку uthash. Потом под своим уютным Linux кросскомпилирую под винду. В итоге что я получаю? Под Wine всё работает, под виндой падает. Запустил под виндовым GDB - увидел, что падает всегда на вызовах uthash. Точнее не просто на uthash, а на вызовах calloc из msvcrt. Начал грешить на обращения за пределами выделенной памяти, которые разрушают структуры менеджера памяти из libc (как известно, информация о блоке памяти обычно хранится около самого выделенного блока). Прогнал под valgrind под Wine на Linux - никаких ошибок не выявлено (ни утечек, ни некорректных обращений). Под винду valgrind не нашёл, однако нашёл некий drmemory - и он обнаружил кучу обращений за пределами выделенной памяти. Причём все - внутри кода uthash.
Вот всё обращения к API uthash, которые есть в моём коде:
typedef struct WaitingObjectInfo {
HANDLE handle;
WaitingObjectCallback callback;
void *data;
UT_hash_handle hh;
} WaitingObjectInfo;
WaitingObjectInfo *waiting_object_list = NULL;
size_t waiting_object_count = 0;
... блок 1 ...
HANDLE handle_list[waiting_object_count];
WaitingObjectInfo *info, *tmp_info;
size_t i = 0;
HASH_ITER(hh, waiting_object_list, info, tmp_info) {
handle_list[i++] = info->handle;
}
assert(i == waiting_object_count);
... блок 2 ...
if ((result >= WAIT_OBJECT_0) && (result < (WAIT_OBJECT_0 + waiting_object_count))) {
HANDLE handle = handle_list[result - WAIT_OBJECT_0];
HASH_FIND_INT(waiting_object_list, &handle, info);
assert(info != NULL);
if (info->callback) {
info->callback(info->data, info->handle);
}
}
... блок 3 ...
WaitingObjectInfo *info, *tmp_info;
HASH_ITER(hh, waiting_object_list, info, tmp_info) {
HASH_DEL(waiting_object_list, info);
free(info);
}
waiting_object_count = 0;
... блок 4 ...
WaitingObjectInfo *info;
HASH_FIND_INT(waiting_object_list, &handle, info);
if (!info) {
info = malloc(sizeof(WaitingObjectCallback));
assert((info != NULL) && "malloc for WaitingObjectInfo failed");
info->handle = handle;
HASH_ADD_INT(waiting_object_list, handle, info);
waiting_object_count++;
}
info->callback = callback;
info->data = data;
... блок 5 ...
WaitingObjectInfo *info;
HASH_FIND_INT(waiting_object_list, &handle, info);
if (info) {
HASH_DEL(waiting_object_list, info);
free(info);
waiting_object_count--;
} else {
fprintf(stderr, "[WARNING] remove_waiting_object: handle %i not in waiting list!\n", handle);
}
Код строго однопоточный (во всяком случае нигде в своём приложении я не создаю потоки), блок 1 и блок 2 находятся в одной функции и выполняются строго последовательно (остальные блоки не могут быть вызваны между ними).
Никогда не падает на HASH_FIND_INT, некорректные обращения находятся в блоке 3 и 4 (блок 5 пока нигде не используется), при этом внутри макросов добавления/удаления элекментов. Падает либо внутри функции добавления на calloc, либо при удалении, либо на free(info) после удаления.
Кросскомпилирую командой:
i686-w64-mingw32-gcc -c -o имя-файла.o -O2 -ggdb -Iuthash/src имя-файла.c
Затем линкую командой:
i686-w64-mingw32-gcc -o имя-файла.exe имя-файла.o -lm -static
Я бы мог грешить на баг в uthash, но почему под Linux некорректных обращений к памяти не происходит (под данным valgrind)?
Linux тут при том, что Wine я запускаю под Linux.