Я унаследовал какой-то сторонний код в проекте. Он состоит из заголовочного файла и двоичной библиотеки без исходных текстов, поэтому я не могу просто перекомпилировать его с другими настройками.
Заголовок использует трюк во время компиляцииtypedef
использование массива отрицательного размера, если выражение не выполнено) для обеспечения правильного выравнивания структуры, но, похоже, оно не работает в 32-битном режиме.
Вот отдельная небольшая программа, которая охватывает всю проблему:
#include <stdio.h>
#include <stddef.h>
#pragma pack(push, 8)
struct test {char _; long long a;};
typedef char pack_test[(offsetof(test, a) == 8) ? 1 : -1];
#pragma pack(pop)
int main(int argc, char *argv[])
{
printf("%d\n", offsetof(test, a));
return 0;
}
Приведенный выше код не компилируется как на clang 3.0, так и на любой недавней версии gcc, на которую я мог быстро наложить руку (4.5 до 4.7): прагмы упаковки просто не имеют никакого эффекта. Компилятор продолжает выравнивать элемент a
до 4 байтов (вы можете проверить это, комментируя typedef
из).
Это почему? Как я могу исправить код так, чтобы это утверждение не проваливалось и поддерживало его совместимость с ABI, при этом не нужно проходить через все структуры в заголовке и закреплении attribute
на их хвосты?
В 32-битной системе long long
может не потребоваться выравнивание восьми байтов, четырех должно быть достаточно. В конце концов, вы все равно извлекаете два целых слова из памяти, независимо от того, является ли первое из них выровненным по восьми байтам или четырьмя.
Вы можете попытаться принудительно выровнять восемь байтов с атрибутом
struct test { char _; long long a __attribute__ ((aligned(8))); };
(вероятно, неправильно, я не знаком с атрибутами gcc)
Конечно, вы также можете добавить фиктивного члена к struct
чтобы обеспечить желаемое выравнивание
struct test { char _; char dummy[7]; long long a; }
Это должно работать на практике.
Других решений пока нет …