Перейти к содержанию
    

Снова о gcc и невыровненном доступе к памяти

Знаю что такие темы уже были, тем не менее я так и не разобрался как сделать лучше и проще всего

Есть Atmel AT91RM9200, на котором крутится Linux, и на который надо портировать софтинку, в которой есть необходимость обращаться к элементам структуры типа:

 

struct blabla {

char ch;

int that;

} blabla_t

 

При обращении к that и происходит, собсно, невыровненный доступ к памяти.

При этом объявить структуру с параметром __attribute__ ((__aligned__(4))), или дополнить паддингом я не могу, ибо по сети приходит пакет, на который собсно и накладывается структура.

Да и отловить все подобные обращения в сотне исходный файлов не представляется оптимальным путём.

 

Собственно, вопрос, как быть? Неужели нет простой возможности обойти проблему в банальнейшей операции обращения к полю структуры?

 

И еще одна непонятка. Вроде бы бит A в CP15 включен, однако никаких исключений при таких обращения не происходит. Поэтому я даже не могу узнать о таком обращении. ЧЯДНТ? Может, надо еще что то где то включить? Или в линухе реализована обработка подобного исключения но у меня она почему то не работает?

 

Всем спасибо!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

При этом объявить структуру с параметром __attribute__ ((__aligned__(4))), или дополнить паддингом я не могу, ибо по сети приходит пакет, на который собсно и накладывается структура.

Так по умолчанию и будет выравнивание! Его надо для этой структуры отключать. (если конечно у вас не выключено выравнивание везде). Если выравнивание будет выключено компилятор для не выравненных членов будет генерировать побайтовый или пословный доступ!

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Знаю что такие темы уже были, тем не менее я так и не разобрался как сделать лучше и проще всего

Лучше и проще всего писать ВНЯТНЫЕ исходники. В этом случае Вы будете правильно поняты компилятором и компилятор выполнит все необходимые дополнительные действия по доступу к элементам такой структуры.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Или в линухе реализована обработка подобного исключения но у меня она почему то не работает?

У меня на работе стоит компьютер Sparc 64-bit с линуксом. Там логи засыпаны сообщениями о невыравненном доступе к памяти. Виновник - модуль cifs, т.е. часть ядра линукс. Очевидно, ядро перехватывает невыравненные обращения к памяти и прозрачным образом их обрабатывает. Ну а пейсатели модуля cifs, очевидно, не парятся и полагаются на эту фичу ядра.

Мне кажется, у Вас должна быть та же фича.

 

Update:

Кстати, в исходниках модуля cifs можете и подсмотреть, как они объявляют такие структуры:

cifspdu.h

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ребят, по факту у меня ЕСТЬ невыровненное обращение, и по умолчанию компилятор ничего не выравнивает. Т. е. типа __attribute__((packed))

Я очень долго искал в чем же бага, потому что у меня не было ни одного сообщения о невыровненности, (опять же, почему их нет?)

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

 

Так вот вопрос в этом и состоит, что как и где сказать компилятору, что обращения к этой структуре будут невыровненны. так чтобы внести минимальное кол-во исправлений в код. Но не пользуясь атрибутом aligned, потому что это изменяет физический размер структуры, а я привязан к формату кадра.

Или как заставить линукс эти невыровненные обращения подменять сложными чтениями/сдвигами, чтобы для меня это вообще прозрачно было. Ведь не валится ничего!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ребят, по факту у меня ЕСТЬ невыровненное обращение, и по умолчанию компилятор ничего не выравнивает. Т. е. типа __attribute__((packed))

Откуда инфа? Может быть, Вам показалось?

 

Я очень долго искал в чем же бага, потому что у меня не было ни одного сообщения о невыровненности, (опять же, почему их нет?)

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

Опять же, может Вам показалось? Напишите подробнее.

 

Так вот вопрос в этом и состоит, что как и где сказать компилятору, что обращения к этой структуре будут невыровненны. так чтобы внести минимальное кол-во исправлений в код. Но не пользуясь атрибутом aligned, потому что это изменяет физический размер структуры, а я привязан к формату кадра.

Неприлично не читать ответы на вопросы. См. выше: содержимое файла cifspdu.h.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так вот вопрос в этом и состоит, что как и где сказать компилятору, что обращения к этой структуре будут невыровненны.

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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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 */
}

 

как-то так =)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Неприлично не читать ответы на вопросы. См. выше: содержимое файла cifspdu.h.

Было бы неприлично, если бы не читал. Ан нет :) Вы же сами сказали что у вас валятся ошибки этого модуля. Т.е. он заведомо написан неверно. Я посмотрел, там действительно все структуры объявляются с атрбиутом packed. НО этот атрибут делает заведомо наоборот от того что мне нужно, поэтому там все так и работает.

 

В качестве док-ва прилагаю тестовую програмку и ЛОГ, в котором видно что работает она неправильно, можете сравнить ее вывод с тем, что даст вам хостовый компутер. Видно так же, почему мне не подходят все эти манипуляции с атрибутами структуры

К сожалению загружать файлы мне запрещено, вставляю в тело

 

И, отвечая частично на свой вопрос - сообщения не вываливались, потому что есть такой файл как /proc/cpu/alignment, По дефолту ничего не делается. Если записать туда 1 - будет лог. Если 2 - то будет фиксить. В логе видно, что это действительно работает.

 

Програмка:

#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;
}

 

ЛОГ:

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

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вы же сами сказали что у вас валятся ошибки этого модуля. Т.е. он заведомо написан неверно.

Срочно сообщите Линусу! Пусть пофиксит :-)

А если серьёзно, то не вижу проблемы в том, чтобы полагаться на линуксовый обработчик невыравненного доступа. Можете называть это предупреждениями, а не ошибками.

 

В качестве док-ва прилагаю тестовую програмку и ЛОГ, в котором видно что работает она неправильно, можете сравнить ее вывод с тем, что даст вам хостовый компутер. Видно так же, почему мне не подходят все эти манипуляции с атрибутами структуры

Многабукаф. Ниасилил.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

char *incomming_message;
.....
struct blabla *b;
b = (struct blabla *) incomming_message; /* Так делать нельзя, т.к. компилятор не может ничего узнать будет ли то, куда указывает incomming_message выровнено в памяти! */

Зато можно сделать typedef с опцией packed, тогда все будет ок! и код будет понятный.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Зато можно сделать typedef с опцией packed, тогда все будет ок! и код будет понятный.

1) И чем это поможет?

2) Всяческие атрибуты выходят за стандарт Си. Когда однажды потом откомпилируете свой код с подобными атрибутами другим компилятором (или под другую архитектуру). Или даже если в рамках одной архитектуры и компилятора будет изменена endianness, то результат будет непредсказуемым.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Програмка:

Не сочли нужным объявить, что структура BPDU_BODY_Y пакованная. Что написали, то и получили.

 

 

как-то так =)

Совершенно дикие действия :( с трудноуловимым смыслом :(.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2) Всяческие атрибуты выходят за стандарт Си. Когда однажды потом откомпилируете свой код с подобными атрибутами другим компилятором (или под другую архитектуру). Или даже если в рамках одной архитектуры и компилятора будет изменена endianness, то результат будет непредсказуемым.

Ну #pragma pack уже практически стандартно поддерживается всеми компилерами и GCC в том числе.

endianness тоже можно указать для структуры.

А при помощи С99 и #define _Pragma() можно сделать правильное определение структуры для всех адекватных компиляторов!

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Совершенно дикие действия :( с трудноуловимым смыслом :(.

Что в них "дикого"?

Если смысл непонятен, поясню:

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

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

Так понятней?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...