Как избавиться от "dereferencing ... will break strict-aliasing rules" варнингов в библиотеке UIPEthernet?

При попытке скомпилировать любой пример из библиотеки UIPEthernet library, компилятор сыплет сотни варнингов о неверном преобразовании типов:

/home/dmit/Arduino/libraries/UIPEthernet/utility/uip.c: In function 'uip_process':
/home/dmit/Arduino/libraries/UIPEthernet/utility/uip_arp.c:116:24: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
 #define IPBUF ((struct ethip_hdr *)&uip_buf[0])
                        ^
/home/dmit/Arduino/libraries/UIPEthernet/utility/uip_arp.c:253:20: note: in expansion of macro 'IPBUF'
     uip_arp_update(IPBUF->srcipaddr, &(IPBUF->ethhdr.src));
                    ^
/home/dmit/Arduino/libraries/UIPEthernet/utility/uip.c:226:22: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
 #define BUF ((struct uip_tcpip_hdr *)&uip_buf[UIP_LLH_LEN])
                      ^
/home/dmit/Arduino/libraries/UIPEthernet/utility/uip.c:755:6: note: in expansion of macro 'BUF'
      BUF->flags = TCP_RST | TCP_ACK;
      ^
/home/dmit/Arduino/libraries/UIPEthernet/utility/uip_arp.c:116:24: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
 #define IPBUF ((struct ethip_hdr *)&uip_buf[0])
                        ^
/home/dmit/Arduino/libraries/UIPEthernet/utility/uip_arp.c:253:40: note: in expansion of macro 'IPBUF'
     uip_arp_update(IPBUF->srcipaddr, &(IPBUF->ethhdr.src));
                                        ^
/home/dmit/Arduino/libraries/UIPEthernet/utility/uip.c:226:22: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
 #define BUF ((struct uip_tcpip_hdr *)&uip_buf[UIP_LLH_LEN])
                      ^
/home/dmit/Arduino/libraries/UIPEthernet/utility/uip.c:781:6: note: in expansion of macro 'BUF'
      BUF->flags = 0;

Собственно, это совершенно неудивительно, так как макрос BUF описан в либе следующим образом:

u8_t uip_buf[UIP_BUFSIZE + 2];   /* The packet buffer that contains
				    incoming packets. */

#define BUF ((struct uip_tcpip_hdr *)&uip_buf[UIP_LLH_LEN])
#define FBUF ((struct uip_tcpip_hdr *)&uip_reassbuf[0])
#define ICMPBUF ((struct uip_icmpip_hdr *)&uip_buf[UIP_LLH_LEN])
#define UDPBUF ((struct uip_udpip_hdr *)&uip_buf[UIP_LLH_LEN])

то есть автор берет и самым беспардонным образом обьявляет указатель на символьный буфер указателем на структуру. А это и есть “нарушение правил приведения типов” в самом грубом виде.
Интересно тут другое - ошибки валятся только при сборке на 32-битных камнях, например на Дуе. А на 8битной Меге ошибок нет. Почему так?

Это связано с разными настройками компилятора или этот варнинг как-то зависит от архитектуры и на 8битках не проявляется?

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

А если объявить свой тип из структур с помощью typedef?

Это же не ошибка, а предупреждение. Смысл его тебе понятен. Скажи авторам “спасибо” и забей. Зачем ты на него внимание обращаешь?

Я не пишу - вообще игнорить ворнинги, но тут-то ты знаешь и причину и смысл. И да, это пустое предупреждение.

Так ли пустое?
Автор берет и присваивает произвольный адрес в памяти указателю на структуру. А я слышал, что в некоторых 32битных архитектурах ты просто не можешь положить в память 4-байтный инт без выравнивания на границу слова.
То есть если присвоить указателю на инт значение, не кратное четырем - выйдет фигня, или говоря по научному “undefined behavior”
Я не прав?

Да, было такое, я сам сталкивался как раз в таком преобразовании и на АРМ архитектуре.
Но тут же вроде наоборот? из буфера в структуру переводят.
Ну и не станешь же ты библиотеку переписывать?

ТОка блин! Я сталкивался с этим 20 лет назад!!! Мне кажется, что АРМ архитектура немного развилась с тех пор. Нет?

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

Кстати, наверно именно поэтому варнинги вылазят на 32битным Дуе и “молчат” на Меге.

1 лайк

Ну… при выделении памяти под буфер выровнять сразу, да и маллок наверное сам выравнивает. А от ворнинга как избавиться - сходе не соображу. Это тебе к нашему “гуру”. Есть несколько старых способов, типа того, что char * преобразуется к чему угодно. Но не знаю, работают ли они сейчас.

Вернее так: если не работает, то придется реально подумать о выравнивании по 4 байтам. Если работает и просто “ноет”, то попробуй всякую тяжелую артиллерию типа (reinterpret_cast)

вроде опять же наоборот
По смыслу что угодно можно преобразовать к char* , а наоборот нет

Только не подумай, что я спорю.

да, это верно. Не подумал, что тут наоборот…

Короче смотри, как я выше написал, это ворнинг по делу или нет. Если нет - то понятно, если по делу, то нужно озаботится выравниванием по 4.

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

ааа! Ну если это работает - то и ладно, если нет - придется переписывать на memcpy() … Если не критично по времени, конечно.

Это понятно.
Только ведь именно из-за того, что у них эта структура “скользящим окном” работает - каждый раз копировать ее в другое место дико накладно выходит.
Что-то не соображу, как сделать эффективнее и при этом варнинг убрать.

Не, не про это варнинг. А про оптимизацию. Забей. Скорее всего там начало буфера выровнено так или иначе на 4 байта.

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

Преобразование типов нормальное - указатель преобразовывается к указателю. Эка невидаль.

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

От Вы оба головы!

1 лайк

что-то не могу найти, где она была описана…
Но там точно есть что выравнивать - например IPv4 адрес, описанный как uint32

struct __attribute__((may_alias)) MyStruct {
    int a;
    float b;
};
struct MyStruct *ptr = (struct MyStruct *)(buffer + offset);

ВОт еще. Это не я сам про такой атрибут узнал. Это чатЖПТ, поэтому проверять надо - работает ли такой способ. Он врёт очень часто. Пишет, что такое есть в GCC.

А вот так уже на грабли наступить можно. Не надо так этот варнинг затыкать.

Почитайте в описании GCC про strict aliasing, и как он помогает в оптимизации. По умолчанию этот варнинг вообще выключен. Не устаю повторять, что набор флагов компилятора Arduino IDE вызывает изумление.

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

Вы считаете, что указателю на uint32 можно присвоить произвольное значение, например не кратное четырем? На любой архитектуре?

Если нет, то это не " просто предупреждение", а потенциальный баг, причем довольно хитрый