Работа с не выравненными структурами на АРМм9

ARM
Суть: необходимо с контроллера АРМ отправить данные(не важно как хоть по сети, хоть но последовательному порту) на х86 машину, передавать будем такую структуру.
struct test {
unsigned char ch;
unsigned short sh;
unsigned lg;
};
int main (int argc, char** argv )
{
printf("%d\n", sizeof(test));
return 0;
}

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

компилить будем
х86: g++ version 4.4.3
arm: g++ version 3.3.2 — такой в тулчайне стоит
Запуск данной программы на х86 и арм выдал длинну 8 байт — т.е. Структура выравнена на dword.

Вообще-то АРМ не умеет работать с не выравненными данными на DWORD, но если на нем установлено ядро с mmu, то это можно сделать, подмену адресов сделает mmu.
Есть, в теории, два способа заставить компилятор не выравнивать данные:
Но безопасный способ есть только один и только для компиляторов gcc, но так как 99% всего софта под linux собирается gcc — так что можно забить.
1. не безопасный способ, обернем структуру в прагму
#pagma pack (push, 1)
struct test {

};
#pragma pack ( pop)

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

при компиляции под arm имеем:
arm-linux-gcc mmu.c --static -o mmu
mmu.c:3: warning: #pragma pack(push[, id], ) is not supported on this target
mmu.c:9: warning: #pragma pack(pop[, id], ) is not supported on this target

это значит, что сборка gcc под АРМ пропустил не поддерживаемые опции и собрал приложение. Запуск этого приложения на арм выдал длину структуры 8 байт, т.е. выравнил данные. Сл-но гарантировано имеем ошибку приема так как ожидаем не выравненные данные.
Поэтому этот способ не безопасный, ибо при компиляции большого проекта эти вартнинги можно не заметить
2. способ — безопасный
теперь описание структуры выглядит так:
struct test {
unsigned char ch;
unsigned short sh;
unsigned lg;
}__attribute__((packed));

Оба компилятора собрали приложение без ошибок и предупреждений и при запуске прожке вывод сказал, что длинна структуры равна 7, — чего и хотели получить.
Почему я назвал этот способ безопасный, не все компиляторы поймут __attribute__((packed)), у меня есть данные, что это чисто gcc-ная блуда, сл-но в другой компилятор в этом месте вывалится с ошибкой, и вы пойдете читать манул компилятора, как запретить выравнивать данные в нем.
Собственно все по этой теме.

P.S. Мне интересно есть ли у кого нибудь данные ucLinux — может работать с не выравненными данными, подобным образом, потому как в архитектуре ARM, нет поддержки работы с не выравненными данными, а в ucLinux нет mmu, который позволяет это делать на ARM. — Это очень интересный вопрос который можно тут осветить для полноты картины.
  • +1
  • 13 декабря 2011, 12:38
  • Aligous

Комментарии (14)

RSS свернуть / развернуть
не известно в какое место структуры вставят лишний байт.
Сразу не соглашусь. место известно.

Поэтому этот способ не безопасный, ибо при компиляции большого проекта эти вартнинги можно не заметить
(цитата без намека на личности, а то мало ли что подумаете) «Кто тебе олень, что ты не слушаешь советы». Варнингов в правильном проекте быть не должно. если выдаются варнинги, то их надо обязательно просматривать на равне с ошибками.

__attribute__((packed));

Не все компиляторы поддерживают аттрибут, на сколько помню. Это гццшная фуська.

От себя добавлю. При работе с невыровненными данными производительность довольно резко падает, и обрабатывать данные в таком виде не рационально. Гораздо лучше хрнить и обрабатыватьь данные в обычных структурах, а при передаче упаковывать/распаковывать их с помощью той же memcpy. Хотя зависит от задач, конечно.
0
самый надёжный способ — паковать сдвигами и масками в uint32 или int, а потом при отправке прогонять через htonl. во многих сетевых приложениях делается именно так. зачем мучаться? У нас на работе тоже в одном проекте делали прагмой и __attribute__((packed)). Но этого было мало. Они ещё и использовали битовые поля. Потом был очень долгий геморой по переворачиванию всей этой беды при передаче с mips на intel по сети. Прикол в том, что вы не знаете заранее, как разместит компилятор биты из полей по int'ам, и как это будет сидеть при упаковке структуры.
0
Отчего же. Правила упаковки вполне доступны, просто их мало кто изучает :) Но геморой получается знатный это да.
0
GCC для ARM поддерживает #pragma pack, он не поддерживает push/pop. То есть, вот так сработает:
#pragma pack(1)
typedef struct {
    char ch;
    int that;
} blabla_t;
#pragma pack()
0
Да так собралось без предупреждений, но размер структуры все равно 8 сл-но выавнена
0
Очень странно. У меня пакует. Размер вашей структуры — 7 байт.
arm-none-eabi-gcc (Sourcery G++ Lite 2009q1-161) 4.3.3
0
Помнится я для Linux b uСLinux из под GCC использовал в структурах __attribute__((packed)) на ARM9 и ADI Blackfin для связки с x86.
Пакет, тогда помог сильно.
0
я правильно понял ядро ucLinux умеет работать с не выравненными данными?
0
Ядру все равно, выровнено оно или нет, оно работает с структурой как с обычной структурой выделенной в память, как с массивом. Косяки вылазят на разных архитектурах из-за различиях в BIG_ENDIAN или LITTLE_ENDIAN. Просто из за разных еддианов но разному читаются данные.
0
Работа с такими стуктурами не эффективна, и вообще это только одна из проблем, есть ешё как минимум порядок байт, смотрим по слову сериализация.

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

Я бы преобразовавал в текст и парсил обратно если возможно.
+1
ASN1 для передачи данных в системах гетерогенных же формат есть
0
Я в mspgcc прошелся однажды по граблям, когда даже __attribute__((packed)) не помогло.
Только ключ -fpack-struct
С тех пор все критические структуры (типа пакетов данных для обмена) тасую вручную.
Потеря производительности ничтожна, а нервы, ногти и волосы — целее
0
Была у меня проблема со структурой на PIC30F4012. Объявил указатель unsigned char*, нацелел его на первый байт структуры и увеличивая указатель хотел передать структуру по байтам через UART. Поля в струтуре были
struct
{
unsigned char a;
unsigned char b;
unsigned char c;
unsigned short d;
unsigned long e;
}
Все однобайтные переменные передались при увеличении указателя нормально, а вот когда должен идти старший или младший байт двухбайтной переменной(не помню какой уже) то вмето него шёл 0х00 а потом только байты двухбайтной переменной. В доке на компилятор я прочёл что надо указыватьт на против поля структуры которое должно быть упакованно. Т.е. я сделал так:
struct
{
unsigned char a;
unsigned char b;
unsigned char c;
unsigned short d __attribute__((packed));
unsigned long e __attribute__((packed));
}
И всё заработало как надо. Т.е при увеличении указателя в комп приходили байты подряб без лишних нулей.
0
  • avatar
  • MCDFV
  • 14 декабря 2011, 10:41
Что бы подобной херни не происходило, правильнее создавать структуры с учётом выравнивания. Например так:
struct
{
unsigned long e;
unsigned short d;
unsigned char a;
unsigned char b;
unsigned char c;
}

Или резервировать поля
struct
{
unsigned char a;
unsigned char b;
unsigned char c;
unsigned char reserved8;
unsigned short d;
unsigned short reserved16;
unsigned long e;
}

Когда делаешь своё — это лучший вариант. За одно и будущему поколению не надо будет *** с вашим протоколом.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.