|
Снова о gcc и невыровненном доступе к памяти |
|
|
|
Jun 28 2011, 07:29
|
Группа: Новичок
Сообщений: 4
Регистрация: 6-05-09
Пользователь №: 48 742

|
Знаю что такие темы уже были, тем не менее я так и не разобрался как сделать лучше и проще всего Есть Atmel AT91RM9200, на котором крутится Linux, и на который надо портировать софтинку, в которой есть необходимость обращаться к элементам структуры типа:
struct blabla { char ch; int that; } blabla_t
При обращении к that и происходит, собсно, невыровненный доступ к памяти. При этом объявить структуру с параметром __attribute__ ((__aligned__(4))), или дополнить паддингом я не могу, ибо по сети приходит пакет, на который собсно и накладывается структура. Да и отловить все подобные обращения в сотне исходный файлов не представляется оптимальным путём.
Собственно, вопрос, как быть? Неужели нет простой возможности обойти проблему в банальнейшей операции обращения к полю структуры?
И еще одна непонятка. Вроде бы бит A в CP15 включен, однако никаких исключений при таких обращения не происходит. Поэтому я даже не могу узнать о таком обращении. ЧЯДНТ? Может, надо еще что то где то включить? Или в линухе реализована обработка подобного исключения но у меня она почему то не работает?
Всем спасибо!
|
|
|
|
|
Jun 28 2011, 07:40
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(vinni-puch @ Jun 28 2011, 11:29)  Или в линухе реализована обработка подобного исключения но у меня она почему то не работает? У меня на работе стоит компьютер Sparc 64-bit с линуксом. Там логи засыпаны сообщениями о невыравненном доступе к памяти. Виновник - модуль cifs, т.е. часть ядра линукс. Очевидно, ядро перехватывает невыравненные обращения к памяти и прозрачным образом их обрабатывает. Ну а пейсатели модуля cifs, очевидно, не парятся и полагаются на эту фичу ядра. Мне кажется, у Вас должна быть та же фича. Update: Кстати, в исходниках модуля cifs можете и подсмотреть, как они объявляют такие структуры: cifspdu.h
|
|
|
|
|
Jun 28 2011, 08:04
|
Группа: Новичок
Сообщений: 4
Регистрация: 6-05-09
Пользователь №: 48 742

|
Ребят, по факту у меня ЕСТЬ невыровненное обращение, и по умолчанию компилятор ничего не выравнивает. Т. е. типа __attribute__((packed)) Я очень долго искал в чем же бага, потому что у меня не было ни одного сообщения о невыровненности, (опять же, почему их нет?) потом написал вырожденный случай в 10 строк, который работает неправильно - именно из-за невыровненности.
Так вот вопрос в этом и состоит, что как и где сказать компилятору, что обращения к этой структуре будут невыровненны. так чтобы внести минимальное кол-во исправлений в код. Но не пользуясь атрибутом aligned, потому что это изменяет физический размер структуры, а я привязан к формату кадра. Или как заставить линукс эти невыровненные обращения подменять сложными чтениями/сдвигами, чтобы для меня это вообще прозрачно было. Ведь не валится ничего!
|
|
|
|
|
Jun 28 2011, 08:11
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(vinni-puch @ Jun 28 2011, 12:04)  Ребят, по факту у меня ЕСТЬ невыровненное обращение, и по умолчанию компилятор ничего не выравнивает. Т. е. типа __attribute__((packed)) Откуда инфа? Может быть, Вам показалось? Цитата(vinni-puch @ Jun 28 2011, 12:04)  Я очень долго искал в чем же бага, потому что у меня не было ни одного сообщения о невыровненности, (опять же, почему их нет?) потом написал вырожденный случай в 10 строк, который работает неправильно - именно из-за невыровненности. Опять же, может Вам показалось? Напишите подробнее. Цитата(vinni-puch @ Jun 28 2011, 12:04)  Так вот вопрос в этом и состоит, что как и где сказать компилятору, что обращения к этой структуре будут невыровненны. так чтобы внести минимальное кол-во исправлений в код. Но не пользуясь атрибутом aligned, потому что это изменяет физический размер структуры, а я привязан к формату кадра. Неприлично не читать ответы на вопросы. См. выше: содержимое файла cifspdu.h.
|
|
|
|
|
Jun 28 2011, 08:21
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
QUOTE (vinni-puch @ Jun 28 2011, 10:04)  Так вот вопрос в этом и состоит, что как и где сказать компилятору, что обращения к этой структуре будут невыровненны. Ничего, кроме информации о том, что он работает невыровненной структурой. В Вашем случае Вам удалось написать нечто невнятное, например, с преобразованиями типов и указателей, и компилятор НЕ получил эту информацию.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jun 28 2011, 08:58
|
Профессионал
    
Группа: Свой
Сообщений: 1 453
Регистрация: 23-08-05
Пользователь №: 7 886

|
Цитата(vinni-puch @ Jun 28 2011, 11:29)  Код struct blabla { char ch; int that; } blabla_t При обращении к that и происходит, собсно, невыровненный доступ к памяти. Не верю! либо вы делаете примерно так: Код char *incomming_message; ..... struct blabla *b; b = (struct blabla *) incomming_message; /* Так делать нельзя, т.к. компилятор не может ничего узнать будет ли то, куда указывает incomming_message выровнено в памяти! */
b->that = 100500; /* Если incomming_message не было выровнено, то и на доступ внутри структуры тоже будет ошибка выравнивания */ Один из вариантов решения (ИМХО не лучший): Код char *incomming_message; ..... struct blabla b;
memcpy(&b, incomming_message); /* Таким образом мы создаём копию данных, которые гарантированно выровнены в памяти */
b.that = 100500; /* В этом месте компилятор обязан сгенерировать код без невыровненных обращений */ ИМХО более надёжный путь декодировать данные, приходящие с внешних каналов, не наступая на грабли (aligned/BigEndian): Код int read_u8(uint8_t **message, size_t *len, uint8_t *retval){ if (*len >= sizeof(uint8_t) ){ *retval = **message; *message += sizeof(uint8_t); *len -= sizeof(uint8_t); return 0; /* Ok */ } return (-1); /* Fail */ }
int read_u16_LE(uint8_t **message, size_t *len, uint16_t *retval){ uint8_t msb; uint8_t lsb; if(read_u8(message, len, &lsb) < 0){ return (-1); /* Fail */ }
if(read_u8(message, len, &msb) < 0){ return (-1); /* Fail */ }
*retval = (msb << 8) | lsb;
return (0); /* Ok */ }
int read_u16_BE(uint8_t **message, size_t *len, uint16_t *retval){ uint8_t msb; uint8_t lsb; if(read_u8(message, len, &msb) < 0){ return (-1); /* Fail */ }
if(read_u8(message, len, &lsb) < 0){ return (-1); /* Fail */ }
*retval = (msb << 8) | lsb;
return (0); /* Ok */ } как-то так =)
|
|
|
|
|
Jun 28 2011, 09:04
|
Группа: Новичок
Сообщений: 4
Регистрация: 6-05-09
Пользователь №: 48 742

|
Цитата(scifi @ Jun 28 2011, 11:11)  Неприлично не читать ответы на вопросы. См. выше: содержимое файла cifspdu.h. Было бы неприлично, если бы не читал. Ан нет  Вы же сами сказали что у вас валятся ошибки этого модуля. Т.е. он заведомо написан неверно. Я посмотрел, там действительно все структуры объявляются с атрбиутом packed. НО этот атрибут делает заведомо наоборот от того что мне нужно, поэтому там все так и работает. В качестве док-ва прилагаю тестовую програмку и ЛОГ, в котором видно что работает она неправильно, можете сравнить ее вывод с тем, что даст вам хостовый компутер. Видно так же, почему мне не подходят все эти манипуляции с атрибутами структуры К сожалению загружать файлы мне запрещено, вставляю в тело И, отвечая частично на свой вопрос - сообщения не вываливались, потому что есть такой файл как /proc/cpu/alignment, По дефолту ничего не делается. Если записать туда 1 - будет лог. Если 2 - то будет фиксить. В логе видно, что это действительно работает. Програмка: CODE #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h>
typedef struct bpdu_body_y { unsigned char flags; unsigned char root_id[8]; unsigned char root_path_cost[4]; unsigned char bridge_id[8]; unsigned char port_id[2]; unsigned char message_age[2]; unsigned char max_age[2]; unsigned char hello_time[2]; unsigned char forward_delay[2]; } BPDU_BODY_Y;
typedef struct bpdu_body_t { unsigned char flags; unsigned char root_id[8]; unsigned char root_path_cost[4] __attribute__ ((__aligned__(4))); unsigned char bridge_id[8]; unsigned char port_id[2]; unsigned char message_age[2]; unsigned char max_age[2]; unsigned char hello_time[2]; unsigned char forward_delay[2]; } __attribute__ ((__aligned__(4))) BPDU_BODY_T;
int main(int argc, char *argv[]) { int i; volatile char *memo; volatile u32 *imemo; BPDU_BODY_T *bmemo; BPDU_BODY_Y *ymemo;
printf ("sizeof unaligned struct = %d; sizeof aligned struct = %d\n", sizeof(BPDU_BODY_Y), sizeof(BPDU_BODY_T));
memo = (char *)malloc(sizeof (BPDU_BODY_T));
for (i = 0; i < sizeof (BPDU_BODY_T); i++) { memo[i] = i; }
printf ("------------------------------------------------------------------------------------------------------\n\t"); bmemo = memo; ymemo = memo; printf ("\tMemory contents: \n\n"); for (i = 0; i < sizeof(BPDU_BODY_T); i++) { printf("%02X", memo[i]); if (!((i+1)%4)) { printf ("|"); } } printf ("\n----------------------------------------------------------------\n"); printf ("It should be %02X%02X%02X%02X\n\n", ymemo->root_path_cost[3], ymemo->root_path_cost[2], ymemo->root_path_cost[1], ymemo->root_path_cost[0]); memo = memo + 9; printf ("Addr of unaligned root_path_cost is %p; Addr of aligned root_path_cost is %p; \n", &ymemo->root_path_cost, &bmemo->root_path_cost); printf("\tUnaligned root_path_cost %08X\n\t", *((int *)(ymemo->root_path_cost))); printf("Aligned root_path_cost %08X\n", *((int *)(bmemo->root_path_cost)));
return 0; }
ЛОГ: CODE root@OpenWrt:/# tst1 sizeof unaligned struct = 31; sizeof aligned struct = 36 ------------------------------------------------------------------------------------------------------ Memory contents:
00010203|04050607|08090A0B|0C0D0E0F|10111213|14151617|18191A1B|1C1D1E1F|20212223 | ---------------------------------------------------------------- It should be 0C0B0A09
Addr of unaligned root_path_cost is 0x11011; Addr of aligned root_path_cost is 0x11014; Unaligned root_path_cost 080B0A09 Aligned root_path_cost 0F0E0D0C root@OpenWrt:/# echo 2 > /proc/cpu/alignment root@OpenWrt:/# tst1 sizeof unaligned struct = 31; sizeof aligned struct = 36 ------------------------------------------------------------------------------------------------------ Memory contents:
00010203|04050607|08090A0B|0C0D0E0F|10111213|14151617|18191A1B|1C1D1E1F|20212223 | ---------------------------------------------------------------- It should be 0C0B0A09
Addr of unaligned root_path_cost is 0x11011; Addr of aligned root_path_cost is 0x11014; Unaligned root_path_cost 0C0B0A09 Aligned root_path_cost 0F0E0D0C
|
|
|
|
|
Jun 28 2011, 09:27
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(vinni-puch @ Jun 28 2011, 13:04)  Вы же сами сказали что у вас валятся ошибки этого модуля. Т.е. он заведомо написан неверно. Срочно сообщите Линусу! Пусть пофиксит :-) А если серьёзно, то не вижу проблемы в том, чтобы полагаться на линуксовый обработчик невыравненного доступа. Можете называть это предупреждениями, а не ошибками. Цитата(vinni-puch @ Jun 28 2011, 13:04)  В качестве док-ва прилагаю тестовую програмку и ЛОГ, в котором видно что работает она неправильно, можете сравнить ее вывод с тем, что даст вам хостовый компутер. Видно так же, почему мне не подходят все эти манипуляции с атрибутами структуры Многабукаф. Ниасилил.
|
|
|
|
|
Jun 28 2011, 12:01
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
QUOTE (vinni-puch @ Jun 28 2011, 11:04)  Програмка: Не сочли нужным объявить, что структура BPDU_BODY_Y пакованная. Что написали, то и получили. QUOTE (Petka @ Jun 28 2011, 10:58)  как-то так =) Совершенно дикие действия  с трудноуловимым смыслом  .
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jun 28 2011, 13:46
|
Профессионал
    
Группа: Свой
Сообщений: 1 453
Регистрация: 23-08-05
Пользователь №: 7 886

|
Цитата(zltigo @ Jun 28 2011, 16:01)  Совершенно дикие действия  с трудноуловимым смыслом  . Что в них "дикого"? Если смысл непонятен, поясню: Если информационный пакет пришёл по "сети" или из другого источника, когда не гарантируется что данные будут располагаться в памяти выровнено или когда Endianness отсылающей стороны может не совпасть с получателем. Тогда есть только одна возможность считывать поля структуры из памяти - побайтно, а потом собирать байты в слова. Описанный выше механизм позволяет из произвольно расположенной структуры в памяти вычитывать последовательно с гарантированно одинаковым результатом независимо от архитектуры, компиляторов и всяческих выравниваний. Разумеется, когда структура заполняется и пересылается в рамках одного вычислителя, то этот метод избыточен. Так понятней?
|
|
|
|
|
Jun 28 2011, 14:46
|
Профессионал
    
Группа: Свой
Сообщений: 1 453
Регистрация: 23-08-05
Пользователь №: 7 886

|
Цитата(KRS @ Jun 28 2011, 17:51)  Да все было и так понятно! Не понятно зачем городить этот огород, не проще ли взять современный компилятор и объяснить ему что, где и в каком порядке лежит! Тогда код будет и понятнее и оптимальнее  потому что есть инструкции чтения с переворотом и т.п. Прошу в студию код, как "правильно" объявить структуру blabla у ТС для gcc. Цитата(zltigo @ Jun 28 2011, 18:05)  KRS все уже объяснил, почему делать так, как Вы сделали, неразумно. Только что проверил. Код uint8_t msg[] __attribute__ ((aligned (16))) = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A };
struct blabla_t { char ch; int that __attribute__((packed)); } __attribute__((packed)); struct blabla_t *bla;
int a;
bla=&msg[3]; a = bla->that; printf("a = 0x%08X\n", a);
bla=&msg[0]; a = bla->that; printf("a = 0x%08X\n", a); результат: Код a = 0x08070605 Ошибка шины Итог: никакие "#pragma packed" вам не помогут! Вот теперь понятно? P.S. Я включил в системе контроль alignment. "Ошибка шины" это "SIGBUS" - и есть ошибка невыровненного доступа.
|
|
|
|
|
Jun 28 2011, 15:22
|

Профессионал
    
Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555

|
Цитата(Petka @ Jun 28 2011, 18:46)  Итог: никакие "#pragma packed" вам не помогут! Вот теперь понятно? Ну вам не помогают, а мне помогают Код #include <stdint.h> #include <stdio.h>
#pragma pack(1) typedef struct { char ch; int that; } blabla_t; #pragma pack()
void test(blabla_t *bla) { printf("a = 0x%08X\n", bla->that); }
uint8_t msg[] __attribute__ ((aligned (16))) = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A }; void test2(void) { blabla_t *bla;
int a;
bla= (blabla_t *)&msg[3]; a = bla->that; printf("a = 0x%08X\n", a);
bla=(blabla_t *)&msg[0]; a = bla->that; printf("a = 0x%08X\n", a); } По листингу все читается по байтам, так что все ок! Все таки надо использовать typedef и при присваивании преобразовывать указатели, что бы не плодить warningи, на которые стоит обращать внимание!
|
|
|
|
|
Jun 28 2011, 16:00
|
Профессионал
    
Группа: Свой
Сообщений: 1 453
Регистрация: 23-08-05
Пользователь №: 7 886

|
Цитата(KRS @ Jun 28 2011, 19:22)  Ну вам не помогают, а мне помогают .... По листингу все читается по байтам, так что все ок! Все таки надо использовать typedef и при присваивании преобразовывать указатели, что бы не плодить warningи, на которые стоит обращать внимание! Давайте разбираться: Собрал ваш код gcc (4.5.2, "-O0"). Ошибка доступа ЕСТЬ! Код objdump -d -S ./a.out Вывод: Код 080483e3 <test2>:
uint8_t msg[] __attribute__ ((aligned (16))) = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A };
void test2(void) { 80483e3: 55 push %ebp 80483e4: 89 e5 mov %esp,%ebp 80483e6: 83 ec 28 sub $0x28,%esp blabla_t *bla;
int a;
bla= (blabla_t *)&msg[3]; 80483e9: b8 20 a0 04 08 mov $0x804a020,%eax 80483ee: 83 c0 03 add $0x3,%eax 80483f1: 89 45 f4 mov %eax,-0xc(%ebp) a = bla->that; 80483f4: 8b 45 f4 mov -0xc(%ebp),%eax 80483f7: 8b 40 01 mov 0x1(%eax),%eax 80483fa: 89 45 f0 mov %eax,-0x10(%ebp) printf("a = 0x%08X\n", a); 80483fd: b8 20 85 04 08 mov $0x8048520,%eax 8048402: 8b 55 f0 mov -0x10(%ebp),%edx 8048405: 89 54 24 04 mov %edx,0x4(%esp) 8048409: 89 04 24 mov %eax,(%esp) 804840c: e8 e3 fe ff ff call 80482f4 <printf@plt>
bla=(blabla_t *)&msg[0]; 8048411: c7 45 f4 20 a0 04 08 movl $0x804a020,-0xc(%ebp) a = bla->that; 8048418: 8b 45 f4 mov -0xc(%ebp),%eax 804841b: 8b 40 01 mov 0x1(%eax),%eax 804841e: 89 45 f0 mov %eax,-0x10(%ebp) printf("a = 0x%08X\n", a); 8048421: b8 20 85 04 08 mov $0x8048520,%eax 8048426: 8b 55 f0 mov -0x10(%ebp),%edx 8048429: 89 54 24 04 mov %edx,0x4(%esp) 804842d: 89 04 24 mov %eax,(%esp) 8048430: e8 bf fe ff ff call 80482f4 <printf@plt> } 8048435: c9 leave 8048436: c3 ret И где тут по-байтное обращение?
|
|
|
|
|
Jun 28 2011, 16:18
|
Профессионал
    
Группа: Свой
Сообщений: 1 453
Регистрация: 23-08-05
Пользователь №: 7 886

|
Цитата(KRS @ Jun 28 2011, 20:05)  И при чем здесь ARM? Интел умеет читать по не выравненному адресу, зачем читать по байтам? Может, но не обязан. (См. eFlags, бит 18) Цитата(KRS @ Jun 28 2011, 20:05)  И при чем здесь ARM? Да, проверил на ARM. Действительно читает по-байтно. P.S. Вот бы ещё ссылку на стандарт какой.
|
|
|
|
|
Jun 28 2011, 16:54
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
QUOTE (Petka @ Jun 28 2011, 18:18)  P.S. Вот бы ещё ссылку на стандарт какой. Причем тут стандарт? Что Вы хотите увидеть? Большими красными буквами "Компилятор языка 'C' обязан уметь корректно работать со структурами данных"? Есть структура данных, Вы полно и корректно описали, что она из себя представляет. Все, любой компилятор обязан правильно работать. Зачем указывать отдельно, например, в ГОСТ на автомобиль, то, что должен уметь перемещаться и без запряженной лошади? И уж тем более, не стоит ее в автомобиль запрягать, даже если в ГОСТ на него не написано, что лошадь не требуется.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Jun 28 2011, 17:36
|

Профессионал
    
Группа: Модераторы
Сообщений: 1 951
Регистрация: 27-08-04
Из: Санкт-Петербург
Пользователь №: 555

|
Цитата(Petka @ Jun 28 2011, 20:18)  Да, проверил на ARM. Действительно читает по-байтно. А хороший компилер, в случае с bla= (blabla_t *)&msg[3]; a = bla->that; должен читать сразу слово! (при соотв. уровне оптимизации) потому что в данном случае возможно проследить выравнивание. И, например, RVCT так и делает!
|
|
|
|
|
Jun 28 2011, 17:43
|
Местный
  
Группа: Свой
Сообщений: 475
Регистрация: 14-04-05
Из: Москва
Пользователь №: 4 140

|
Если компилятор сам расположил структуру в памяти, то вне зависимости от директивы pack он должен корректно обращаться к памяти при доступе к любому полю. А вот если с указателями работать вольно, то запросто можно схлопотать ошибку доступа. Или вот так  Код *((int *)0x40000007)=0;
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|