repstosw 18 21 августа, 2021 Опубликовано 21 августа, 2021 · Жалоба Добрый день! Использую тулчейн gcc arm-none-eabi последней версии. Можно ли средствами gcc обращаться к невыровненным данным полу-словами или словами? Какие есть директивы или ключевые слова? В Keil и IAR есть __packed, например: char pool[32768]; char *p1=(char*)pool +5; char *p2=(char*)pool +7; *(__packed int*)p1 = *(__packed int*)p2; //не приведёт к ошибке. Будут сформированы инструкции для невыровненного доступа, или побайтное копирование *(int*)p1 = *(int*)p2; //будет ошибка Можно ли подобное сделать в GCC ? Пока нашёл одно решение, но оно только для C++ (перегрузка операторов и создание своего класса). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
adnega 11 21 августа, 2021 Опубликовано 21 августа, 2021 · Жалоба Можно typedef struct sARP_RECORD { sMAC_ADR mac; sIP_ADR ip; DWORD time; BYTE used; } __attribute__((packed)) sARP_RECORD; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 21 августа, 2021 Опубликовано 21 августа, 2021 · Жалоба 11 minutes ago, adnega said: Можно typedef struct sARP_RECORD { sMAC_ADR mac; sIP_ADR ip; DWORD time; BYTE used; } __attribute__((packed)) sARP_RECORD; Это на уровне структуры. А на уровне простых типов можно? Ситуация немного нештатная. Я декомпилировал программу, изначально написанную на x86. В результате декомпиляции, получившийся псевдо-С код вышел весь в разыменованных указателях, которые разыменовывают невыровненные участки памяти по 2 и 4 байта. Этих разыменованных настолько много в программе, что тут только фиксить на уровне своего класса-обёртки или спец-директивой как это в Keil и IAR. Чтобы вместо: int *p; Писать: int *p; *(_packed int*)p+1 = 0x12345678; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
adnega 11 21 августа, 2021 Опубликовано 21 августа, 2021 · Жалоба 21 минуту назад, repstosw сказал: получившийся псевдо-С код вышел весь в разыменованных указателях Может, их можно собрать в структуры, а затем вообще избавиться от невыровненных данных. В любом случае, оставлять указатели, по-моему, зло. В таком случае выдается предупреждение, что атрибут будет проигнорирован: *(_packed int*)p+1 = 0x12345678; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 21 августа, 2021 Опубликовано 21 августа, 2021 (изменено) · Жалоба 28 minutes ago, adnega said: Может, их можно собрать в структуры, а затем вообще избавиться от невыровненных данных. В любом случае, оставлять указатели, по-моему, зло. Нечто подобное уже сделал - создал класс с перегруженными операторами. В итоге если нужен указатель на невыровненные данные, то записываю так: int pool[20]; UA<int> *p=(UA<int> *)&pool; UA<char> *p2; *(UA<int>*)p+1 = *(UA<int>*)p+7; Всё отлично работает. Но я думал, что в GCC есть штатный способ сделать аналогичное. Кстати, вот результат успешно декомпилированной и отлаженной программы на ПК и вёб: https://github.com/rep-stosw/tube-game-dos Исходники староваты, позже залью новые - там я пофиксил алиасинг указателей и доступ к невыровненным данным. Процесс работы на ARM Cortex-A7: Изменено 21 августа, 2021 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 21 августа, 2021 Опубликовано 21 августа, 2021 · Жалоба Сам тип с перегрузками операторов выглядит так. Перегружал только то, что необходимо: template<typename T> struct UA { char ua[sizeof(T)]; inline operator T() const { T t; memcpy(&t,&ua,sizeof(T)); return t; } inline T operator=(const T p) { memcpy(&ua,&p,sizeof(T)); return p; } inline T operator+=(const T p) { T t; memcpy(&t,&ua,sizeof(T)); t+=p; memcpy(&ua,&t,sizeof(T)); return t; } inline T operator-=(const T p) { T t; memcpy(&t,&ua,sizeof(T)); t-=p; memcpy(&ua,&t,sizeof(T)); return t; } inline T operator>>=(const T p) { T t; memcpy(&t,&ua,sizeof(T)); t>>=p; memcpy(&ua,&t,sizeof(T)); return t; } inline T operator--() { T t; memcpy(&t,&ua,sizeof(T)); --t; memcpy(&ua,&t,sizeof(T)); return t; } }; Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
SII 0 21 августа, 2021 Опубликовано 21 августа, 2021 · Жалоба Если использовать C++11 или более позднюю версию стандарта, то там уже есть стандартные средства для управления выравниванием (ключевое слово alignas), которые можно использовать с любыми более-менее современными компиляторами (на то они и стандартные -- в отличие от __attribute__ и т.п. конструкций). Как в чистых сях, не знаю, ибо не использую, но, возможно, там аналогичное тоже ввели. Ну и стоит помнить, что компилятор-то сгенерирует код для невыровненного доступа, но исполнен он может быть не на каждом МК (в частности, ядро Cortex-M0 на такое не способно). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 242 21 августа, 2021 Опубликовано 21 августа, 2021 · Жалоба 3 часа назад, SII сказал: Ну и стоит помнить, что компилятор-то сгенерирует код для невыровненного доступа, но исполнен он может быть не на каждом МК (в частности, ядро Cortex-M0 на такое не способно). Это не так. Если в свойствах проекта правильно указано целевое ядро (M0), то вменяемый компилятор должен сгенерить корректный код, который будет исполнен. Проверяем (IAR 7.80.4), компилим: u32 __packed volatile *zz1; MeasureCPUuse(*zz1); 1. options: --cpu=Cortex-M3 результат: MeasureCPUuse(*zz1); 0x.... LDR.N R4,??DataTable6_1 0x6960 LDR R0,[R4, #+20] 0x6800 LDR R0,[R0, #+0] 0x.... 0x.... BL _Z13MeasureCPUusej 1. options: --cpu=Cortex-M0 результат: MeasureCPUuse(*zz1); 0x.... LDR R4,??DataTable6_1 0x6960 LDR R0,[R4, #+20] 0x.... 0x.... BL __aeabi_uread4 0x.... 0x.... BL _Z13MeasureCPUusej Думаю - нетрудно догадаться что внутри aeabi_uread4(). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 22 августа, 2021 Опубликовано 22 августа, 2021 (изменено) · Жалоба 6 hours ago, jcxz said: Если в свойствах проекта правильно указано целевое ядро (M0), то вменяемый компилятор должен сгенерить корректный код, который будет исполнен. Похожая ситуация была и в моём случае. Линковалась не та библиотека, которая не учитывала отсутствие невыровненного доступа: ключ -specs. Если он задан -specs=nosys.specs , то библиотечные функции типа memcpy, strcpy вешают процессор в эксепшн по выравниванию. Если задать -specs=nano.specs , то они работают корректно и с невыровненными данными. И не забыть про флаг -mno-unaligned-access AlignmentFault(1); //включен контроль выравнивания char pool[16]; char *p1=(char*)pool+1; char *p2=(char*)pool+7; *(UA<i32>*)p1=1234567; *(UA<i32>*)p2=8901234; *((UA<i32>*)p1) = *((UA<i32>*)p2); UART2_printf(*(UA<i32>*)p1); UART2_printf(*(UA<i32>*)p2); while(1); С ним код выглядит так: add r1, sp, #15 add r0, sp, #9 bl memcpy mov r2, #4 add r1, sp, #9 add r0, sp, r2 bl memcpy ldr r0, [sp, #4] bl UART2_printf Без флага: ldr r2, [sp, #20] ldr r3, [sp, #16] ldr r3, [r3] @ unaligned str r3, [r2] @ unaligned ldr r0, [sp, #20] bl _ZN2UAIiEcviEv mov r3, r0 mov r0, r3 bl UART2_printf Для контроля невыровненного доступа использую такую вставку: /* Alignment fault checking enabled */ asm volatile( "mrc p15, 0, r0, c1, c0, 0\n" "orr r0, r0, #1 << 1 \n" "mcr p15, 0, r0, c1, c0, 0\n" ); Более подробно вессь процесс реверса с разгребанием всех граблей изложил здесь: https://gamedev.ru/flame/forum/?id=262348&page=8&m=5430353#m119 Изменено 22 августа, 2021 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 242 22 августа, 2021 Опубликовано 22 августа, 2021 · Жалоба 4 часа назад, repstosw сказал: Если он задан -specs=nosys.specs , то библиотечные функции типа memcpy, strcpy вешают процессор в эксепшн по выравниванию. Если задать -specs=nano.specs , то они работают корректно и с невыровненными данными. 1. Про какой компилятор речь? И про какое ядро? 2. Что делает AlignmentFault(1)? Если она устанавливает CCR.UNALIGN_TRP в '1', то вы сам себе злобный буратино. Да - и зачем вы его устанавливаете? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 22 августа, 2021 Опубликовано 22 августа, 2021 (изменено) · Жалоба 1 hour ago, jcxz said: 1. Про какой компилятор речь? arm-none-eabi-g++.exe (GNU Arm Embedded Toolchain 10-2020-q4-major) 10.2.1 20201103 (release) 1 hour ago, jcxz said: И про какое ядро? Cortex-A7 (CPU Allwinner V3s) 1 hour ago, jcxz said: 2. Что делает AlignmentFault(1)? void AlignmentFault(u32 af) { /* Alignment fault checking enabled */ if(af)asm volatile( "mrc p15, 0, r0, c1, c0, 0\n" "orr r0, r0, #1 << 1 \n" "mcr p15, 0, r0, c1, c0, 0\n" ); /* Alignment fault checking disabled */ else asm volatile( "mrc p15, 0, r0, c1, c0, 0\n" "and r0, r0, #0xFFFFFFFD \n" "mcr p15, 0, r0, c1, c0, 0\n" ); } 1 hour ago, jcxz said: Если она устанавливает CCR.UNALIGN_TRP в '1', то вы сам себе злобный буратино. 1 hour ago, jcxz said: Да - и зачем вы его устанавливаете? Затем, чтобы выловить все обращения к невыровненным данным размерности более 1 байта. Чтобы адаптировать программу для работы на TMS320C6745, компилятор которого не поддерживает работу с типами невыровненных данных. Только через _mem2,4,8. Что собственно и привело меня к созданию типа с перегрузками, отлично работающего на платформах, где работа с невыровненными данными не прозрачна (как на архитектурах x86, cortex-A7). На TMS320C6745 код заработал после фикса обращений к невыровненным данным ))) Вот такая байда получилась для C6745: static inline void MCPY(void *d,const void *s,unsigned n) { if(n==1)*(char*)d=*(char*)s; else if(n==2)_mem2(d)=_mem2_const(s); else _mem4(d)=_mem4_const(s); } template<typename T> struct UA { char ua[sizeof(T)]; inline operator T() const { T t; MCPY(&t,&ua,sizeof(T)); return t; } inline T operator=(const T p) { MCPY(&ua,&p,sizeof(T)); return p; } inline T operator+=(const T p) { T t; MCPY(&t,&ua,sizeof(T)); t+=p; MCPY(&ua,&t,sizeof(T)); return t; } inline T operator-=(const T p) { T t; MCPY(&t,&ua,sizeof(T)); t-=p; MCPY(&ua,&t,sizeof(T)); return t; } inline T operator>>=(const T p) { T t; MCPY(&t,&ua,sizeof(T)); t>>=p; MCPY(&ua,&t,sizeof(T)); return t; } inline T operator--() { T t; MCPY(&t,&ua,sizeof(T)); --t; MCPY(&ua,&t,sizeof(T)); return t; } }; Её можно отключить: template<typename T> using UA=T; И без неё код не заработает на C6745 )) Изменено 22 августа, 2021 пользователем repstosw Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 143 22 августа, 2021 Опубликовано 22 августа, 2021 · Жалоба Я сделал такой класс: template<typename T> struct unaligned { operator T() const { return Data; } void operator =(T value) { Data = value; } T Data; } __attribute__((packed)); Компилятор сам решает - вставить обычный доступ, если ядро умеет читать/писать невыровненные данные или побайтовый, если ядро иначе не может. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
repstosw 18 22 августа, 2021 Опубликовано 22 августа, 2021 · Жалоба 1 hour ago, Сергей Борщ said: Я сделал такой класс: template<typename T> struct unaligned { operator T() const { return Data; } void operator =(T value) { Data = value; } T Data; } __attribute__((packed)); Компилятор сам решает - вставить обычный доступ, если ядро умеет читать/писать невыровненные данные или побайтовый, если ядро иначе не может. Попробовал построить с ним, вышла ошибка - нарушен строгий алиасинг из-за каламбура типов: Заменил: T Data; На: char Data[sizeof(T)]; Каламбур и алиасинг пропали, но без операторов никак: Над невыровненными данными в программе производятся также операции. Не только присваивание и инициализация. Остаётся вариант, который я приводил выше. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 143 22 августа, 2021 Опубликовано 22 августа, 2021 · Жалоба 1 час назад, repstosw сказал: Над невыровненными данными в программе производятся также операции. Не только присваивание и инициализация. Явные приведения типов никто не отменял. operator T() вполне позволяет неявно приводить к целому, над которым все эти операции определены ("я так думаю!"). И ругань идет не на мой класс, а на ваши макросы. Что они делают - мне, на первый взгляд, не совсем понятно, но вот этот ужасный WORDn(x,n) я бы записал так: template<typename T> inline uint16_t WORDn(T & from, size_t item_no) { return reinterpret_cast<unaligned<uint16_t*>>(&from)[item_no]; } или, на худой конец, так: #define WORDn(x, n) ((unaligned<uint16_t*>)(&(x))[n]) но за последнее не ручаюсь. А в целом не игрался бы с приведением в макросах, а сразу исходные vXXX объявлял бы как unaligned<нужный тип> Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 242 22 августа, 2021 Опубликовано 22 августа, 2021 · Жалоба 20 минут назад, Сергей Борщ сказал: И ругань идет не на мой класс, а на ваши макросы. Что они делают - мне, на первый взгляд, не совсем понятно, но вот этот ужасный WORDn(x,n) я бы записал так: ТС же писал выше зачем. Операция чтения/записи невыровненной в памяти переменной (частный случай - packed) может быть скомпилена либо в одну инструкцию LDRx/STRx либо в набор байтовых инструкций. Способ определяется наличием/отсутствием ключа -mno-unaligned-access и принципиальной возможностью поддержки невыровненного доступа выбранным ядром (>= CM3 (вроде)). Даже на ядре >=CM3 программист по каким-то причинам может хотеть использовать CCR.UNALIGN_TRP=1 (ТС привёл одну из возможных причин). И тогда его макросы (как я понимаю) обеспечат работоспособность такого кода независимо от наличия -mno-unaligned-access. А ваши приведения вызовут fault. Я тоже у себя в проектах иногда использую похожие макросы по той же причине (когда хочется потом попробовать скомпилить код для другого ARM-ядра, которое не поддерживает невыровненный доступ на аппаратном уровне и компилятор не имеет -mno-unaligned-access). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться