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

0

После окончательного создания кросс-платформенного проекта для компиляции у меня возникают самые странные ошибки в OSX. Программа разбивается по-разному (но иногда может выжить, чтобы показать ее ui). Пройдя через отладчик 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 для проекта).

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

Я действительно в затруднении здесь, я готов заключить, что 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

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

--------- edit 2 --------:

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

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

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

stdout изнутри первого конструктора:

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

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

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

------ edit 4: РЕШЕНИЕ ----

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

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

  • 0
    Вы работаете на 64-битной ОС? Если это так, то printf() может быть ненадежным, поскольку вы передаете 64-битные указатели, но %X будет ожидать 32-битных значений (я думаю - я не уверен, использует ли OS X модель данных LP64)
  • 0
    Это на 64-битной ОС, но это 32-битная сборка. Отладчик показывает правильные адреса.
Показать ещё 10 комментариев
Теги:
macos
llvm
memory-corruption

1 ответ

2

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

Компилятор visual studio и clang front end для 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  
  • 0
    Возможно, вы захотите рассмотреть вопрос о том, действительно ли вообще нужна упаковка структурных макетов.
  • 1
    Я сделал это только в первом случае, потому что это было необходимо :) Это часть некоторых общих C-структур между разными языками.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню