Jump to content

    
Sign in to follow this  
vinni-puch

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

Recommended Posts

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

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

 

struct blabla {

char ch;

int that;

} blabla_t

 

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

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

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

 

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

 

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

 

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

Share this post


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

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

 

 

Share this post


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

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

 

Share this post


Link to post
Share on other sites
Или в линухе реализована обработка подобного исключения но у меня она почему то не работает?

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

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

 

Update:

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

cifspdu.h

Share this post


Link to post
Share on other sites

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

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

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

 

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

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

Share this post


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

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

 

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

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

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

 

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

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

Share this post


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

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

 

Share this post


Link to post
Share on other sites
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 */
}

 

как-то так =)

Share this post


Link to post
Share on other sites
Неприлично не читать ответы на вопросы. См. выше: содержимое файла 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

 

Share this post


Link to post
Share on other sites
Вы же сами сказали что у вас валятся ошибки этого модуля. Т.е. он заведомо написан неверно.

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

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

 

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

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

 

Share this post


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

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

 

Share this post


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

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

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

 

Share this post


Link to post
Share on other sites
Програмка:

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

 

 

как-то так =)

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

Share this post


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

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

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

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

 

 

Share this post


Link to post
Share on other sites
Совершенно дикие действия :( с трудноуловимым смыслом :(.

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

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

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

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

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

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this