Повреждение памяти / переупорядочение структуры в OSX + llvm/libC++

После получения кросс-платформенного проекта для компиляции у меня возникают самые странные ошибки в OSX. Программа вылетает по-разному (но иногда может выжить, чтобы показать, что это пользовательский интерфейс). Пройдя через отладчик XCode, я вижу несколько мест, где значения подобъектов меняются в зависимости от контекста. Это небольшая проблема у меня:

class third
{
public:
    int some_data;
    void do_something()
    {
    }

};

class second
{
public:
    third * thirdPtr;

    second()
        : thirdPtr(nullptr)
    {
        thirdPtr = new third();
        printf("second = 0x%X, third = 0x%X", this, thirdPtr);
    }

};


class first
{
    second * secondInstance;
    first()
        : secondInstance(nullptr)
    {
        secondInstance = new second();
        printf("second = 0x%X, third = 0x%X", secondInstance, secondInstance->thirdPtr);
        // (maybe) crash
        secondInstance->thirdPtr->do_something();

    }

};

Проходя, я добавил точку наблюдения к третьему указателю, это пример вывода:

Watchpoint 1 hit:
old value: 0x00000001
new value: 0x00000000

Watchpoint 1 hit:
old value: 0x00000000
new value: 0x04821c34

И вот стандартный вывод программы:

second = 0x4821C20, third = 0x4821C34
second = 0x4821C20, third = 0x3404821C

Я никогда не видел ничего подобного. Очевидно, что структуры являются более сложными и используют наследование, но ничто другое не имеет доступа или записи в эти структуры при инициализации. У меня так много странных сбоев и проблем в моем исходном коде, поэтому я предполагаю, что этот сбой является проблемой в нескольких местах. Напоминаем, что исходный код долгое время работал безупречно на других платформах и дает сбой даже во внешнем библиотечном коде (я использую juce для проекта).

Сначала я подозревал, что оператор new является поддельным, но, похоже, что-то связано с составом объектов. Интересно, что две разные трети имеют сильное сходство.

Я действительно в растерянности, я готов заключить, что llvm переставляет компоновку структур между компилируемыми модулями (классы / структуры находятся в отдельных файлах, конечно)... или что-то в этом роде. Я предполагаю, что я не прав - но кто-нибудь когда-либо испытывал что-то подобное?

--------- редактировать ----------:

Поэтому я добавил этот код: (внутри второго конструктора):

    {
        thirdPtr = new third();
        printf("second = 0x%X, third = 0x%X", this, thirdPtr);
        printf("position = %d", offsetof(second, thirdPtr);
    }

(внутри первого конструктора):

    {
        secondInstance = new second();
        printf("second = 0x%X, third = 0x%X", secondInstance, secondInstance->thirdPtr);
        printf("position = %d", offsetof(second, thirdptr));

    }

И это печатает...

13
16

Примечание: это вывод полных структур (т. Е. Не приведенных здесь примеров). НО: Структура структур на самом деле различается в зависимости от того, в каком модуле компиляции / перевода я нахожусь.

--------- редактировать 2 --------:

Поэтому я решил проверить выравнивание, это может быть ключевой проблемой:

stdout изнутри второй конструктор:

second = 0x3649090, third = 0x36490A4
offset of third in second = 16
sizeof second = 232
align of third = 4, align of second = 4

стандартный вывод из первого конструктора:

second = 0x3649090, third = 0xA4036490
offset of second in third = 13
sizeof second = 226
align of third 1, align of second 1

Так что, похоже, выравнивание изменилось в единицах перевода. Что я могу сделать, чтобы обеспечить соблюдение стандарта во всем проекте?

редактировать 3: мне удалось заставить его "работать", так как он не сразу падает, применяя атрибут((упакованный)) ко второму классу. Но это действительно не оставляет меня в безопасности, и почему я должен это делать? Существует ли глобальный параметр, который управляет этим параметром в единицах перевода?

------ изменить 4: РЕШЕНИЕ ----

Включенный заголовок содержал несоответствующую директиву #pragma pack (которую поддерживает llvm) следующим образом:

    #ifdef __MSVC__
        #pragma pack(push, 1)
        #pragma warning(disable:4482)
    #else
        #pragma pack (push, 1)
    #endif

    ....

    #if defined(__MSVC__)
        #pragma pack(pop)
    #endif

Стек упаковки будет изменен любым компилятором, но будет восстановлен только при использовании компилятора msvC++. Это оставило упаковку всего проекта до 1.

1 ответ

Стало очевидным, насколько важно сбросить упаковочный стек при работе с пользовательским выравниванием уплотнения конструкций.

Компилятор Visual Studio и внешний интерфейс clang для llvm поддерживают одинаковый синтаксис с использованием директив #pragma, которые можно изучить здесь:
http://msdn.microsoft.com/en-us/library/2e70t5y1.aspx

В моем случае включенный заголовок содержал несоответствующую директиву #pragma pack примерно так:

    #ifdef __MSVC__
        #pragma pack(push, 1)
    #else
        #pragma pack (push, 1)
    #endif

    ....

    #if defined(__MSVC__)
        #pragma pack(pop)
    #endif

Стек упаковки будет изменен любым компилятором, но будет восстановлен только при использовании компилятора msvC++. Это оставило выравнивание упаковок блоков перевода, которые включали этот файл, в отличие от тех, которые этого не делали, даже если оба блока перевода видели одно и то же определение структуры. Для полноты вот (в моем случае) исправленные директивы #pragma:

    #if defined(__MSVC__) || defined (__LLVM__)
        #pragma pack (push, 1)
    #endif

    #if defined(__MSVC__) || defined (__LLVM__)
        #pragma pack(pop)
    #endif  
Другие вопросы по тегам