Выравнивание памяти в структурах gcc

Я переношу приложение на платформу ARM на C, приложение также работает на процессоре x86 и должно быть обратно совместимо.

У меня возникают некоторые проблемы с выравниванием переменных. Я прочитал руководство gcc для __attribute__((aligned(4),packed)) Я интерпретирую то, что сказано, когда начало структуры выровнено по 4 байтовой границе, а внутренняя часть остается нетронутой из-за упакованного оператора.

Первоначально у меня было это, но иногда он помещался без привязки с 4-байтовой границей.
typedef struct  
{  
 unsigned int code;  
 unsigned int length;  
 unsigned int seq;  
 unsigned int request;  
 unsigned char nonce[16];  
 unsigned short  crc;  
} __attribute__((packed)) CHALLENGE;

поэтому я изменяю его на это.

typedef struct  
{  
 unsigned int code;  
 unsigned int length;  
 unsigned int seq;  
 unsigned int request;  
 unsigned char nonce[16];  
 unsigned short  crc;  
} __attribute__((aligned(4),packed)) CHALLENGE;

Понятное, что я сказал ранее, кажется неправильным, поскольку и структура теперь выровнена с границей 4 байта, а внутренние данные теперь выровнены с границей из четырех байтов, но из-за энтианны размер структуры увеличился в размере от 42 до 44 байт. Этот размер имеет решающее значение, так как у нас есть другие приложения, которые зависят от структуры, составляющей 42 байта.

Может ли кто-нибудь описать мне, как выполнить требуемую операцию. Любая помощь очень ценится.

Ответы

Ответ 1

Если вы зависите от sizeof(yourstruct), равного 42 байтам, вы собираетесь быть укушенным миром неперспективных предположений. Вы не сказали, для чего это необходимо, но, похоже, имеет значение и консистенция содержимого структуры, поэтому вы также можете иметь несоответствие с x86 там.

В этой ситуации я считаю, что единственный верный способ справиться - использовать unsigned char[42] в тех частях, где это важно. Начните с написания точной спецификации того, какие поля есть где в этом 42-байтовом блоке и какой эндиан, затем используйте это определение для написания некоторого кода для перевода между этим и структурой, с которой вы можете взаимодействовать. Код, скорее всего, будет либо кодом сериализации "все-в-одном" (он же маршаллинг), либо группой геттеров и сеттеров.

Ответ 2

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

В этом случае упаковка плюс выравнивание по 4 означает, что будет два байта заполнения. Это происходит потому, что размер должен быть совместим для хранения типа в массиве со всеми элементами, все еще выровненными на 4.

Я предполагаю, что у вас есть что-то вроде:

read(fd, &obj, sizeof obj)

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

read(fd, &obj, 42)

Что вы можете сохранить:

typedef struct {
  //...
  enum { read_size = 42 };
} __attribute__((aligned(4),packed)) CHALLENGE;

// ...

read(fd, &obj, obj.read_size)

Или, если вы не можете использовать некоторые функции С++ в своем C:

typedef struct {
  //...
} __attribute__((aligned(4),packed)) CHALLENGE;
enum { CHALLENGE_read_size = 42 };

// ...

read(fd, &obj, CHALLENGE_read_size)

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

Ответ 3

Какова ваша истинная цель?

Если это касается данных, которые в файле или на проводе в определенном формате, что вам нужно сделать, это написать некоторые процедуры маршалинга/сериализации, которые перемещают данные между структурой компилятора, которая представляет, как вы хотите иметь дело с данных внутри программы и массива char, который имеет дело с тем, как данные смотрят на провод/файл.

Тогда все, что нужно тщательно обрабатывать и, возможно, иметь конкретный код платформы, - это процедуры маршалинга. И вы можете написать несколько хороших n-мерных модульных тестов, чтобы гарантировать, что маршализированные данные попадают в структуру и из нее должным образом независимо от того, какую платформу вы могли бы переносить на сегодняшний день и в будущем.

Ответ 4

Я бы предположил, что проблема в том, что 42 не делится на 4, и поэтому они выходят из выравнивания, если вы поместите несколько из этих структур обратно в спину (например, выделите память для нескольких из них, определив размер с помощью sizeof). Имея размер как 44, вы выравниваете выравнивание в этих случаях по мере того, как вы просили. Однако, если внутреннее смещение каждого элемента структуры остается неизменным, вы можете обрабатывать 44-байтовую структуру, как если бы она составляла 42 байта (если вы позаботились о выравнивании любых следующих данных на правильной границе).

Один трюк, который можно попробовать, может помещать обе эти структуры внутри одного типа объединения и использовать только 42-байтовую версию из каждого такого объединения.

Ответ 5

Я перемещал структуры назад и вперед из Linux, Windows, Mac, C, Swift, Assembly и т.д.

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

Я не понимаю, почему вы не можете использовать:

typedef struct  
{  
 unsigned int code;  
 unsigned int length;  
 unsigned int seq;  
 unsigned int request;  
 unsigned char nonce[16];  
 unsigned short  crc;  
} __attribute__((packed)) CHALLENGE;

Вы можете использовать его, и он не требует специального или умного кода. Я пишу много кода, который связывается с ARM. Структуры - это то, что заставляет вещи работать. __attribute__ ((packed)) мой друг.

Вероятность оказаться в "мире боли" равна нулю, если вы понимаете, что происходит с обоими.

Наконец, я не могу на всю жизнь понять, как вы получаете 42 или 44. Int - это либо 4 наших 8 байта (в зависимости от компилятора). Это ставит число в 16 + 16 + 2 = 34 или 32 + 16 + 2 = 50 - при условии, что оно действительно упаковано.

Как я уже сказал, знание ваших инструментов является частью вашей проблемы.

Ответ 6

Поскольку я использую linux, я обнаружил, что в echo 3 > /proc/cpu/alignment он выдаст мне предупреждение и исправлю проблему выравнивания. Это работа, но это очень полезно для определения того, где структуры не могут быть смещены.