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

GCC arm-none-eabi обращения к невыровненным данным

Добрый день!

Использую тулчейн 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++ (перегрузка операторов и создание своего класса).

 

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


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

Можно

typedef struct sARP_RECORD
{
	sMAC_ADR		mac;
	sIP_ADR			ip;
	DWORD				time;
	BYTE				used;
} __attribute__((packed)) sARP_RECORD;

 

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


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

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;

 

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


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

21 минуту назад, repstosw сказал:

получившийся псевдо-С код вышел весь в разыменованных указателях

Может, их можно собрать в структуры, а затем вообще избавиться от невыровненных данных. В любом случае, оставлять указатели, по-моему, зло.

В таком случае выдается предупреждение, что атрибут будет проигнорирован:

*(_packed int*)p+1 = 0x12345678;

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


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

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:

 

 

 

 

 

Изменено пользователем repstosw

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


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

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

 

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

 

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


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

Если использовать C++11 или более позднюю версию стандарта, то там уже есть стандартные средства для управления выравниванием (ключевое слово alignas), которые можно использовать с любыми более-менее современными компиляторами (на то они и стандартные -- в отличие от __attribute__ и т.п. конструкций). Как в чистых сях, не знаю, ибо не использую, но, возможно, там аналогичное тоже ввели.

Ну и стоит помнить, что компилятор-то сгенерирует код для невыровненного доступа, но исполнен он может быть не на каждом МК (в частности, ядро Cortex-M0 на такое не способно).

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


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

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().

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


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

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

Изменено пользователем repstosw

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


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

4 часа назад, repstosw сказал:

Если он задан -specs=nosys.specs , то библиотечные функции типа memcpy, strcpy вешают процессор в эксепшн по выравниванию.

Если задать -specs=nano.specs , то  они работают корректно и с невыровненными данными.

1. Про какой компилятор речь? И про какое ядро?

2. Что делает AlignmentFault(1)? Если она устанавливает CCR.UNALIGN_TRP в '1', то вы сам себе злобный буратино.

Да - и зачем вы его устанавливаете?

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


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

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 ))

Изменено пользователем repstosw

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


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

Я сделал такой класс:

template<typename T>
struct unaligned
{
    operator T() const          { return Data; }
    void operator =(T value)    { Data = value; }

    T   Data;
} __attribute__((packed));

Компилятор сам решает - вставить обычный доступ, если ядро умеет читать/писать невыровненные данные или побайтовый, если ядро иначе не может.

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


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

1 hour ago, Сергей Борщ said:

Я сделал такой класс:


template<typename T>
struct unaligned
{
    operator T() const          { return Data; }
    void operator =(T value)    { Data = value; }

    T   Data;
} __attribute__((packed));

Компилятор сам решает - вставить обычный доступ, если ядро умеет читать/писать невыровненные данные или побайтовый, если ядро иначе не может.

 

Попробовал построить с ним, вышла ошибка - нарушен строгий алиасинг из-за каламбура типов:

 arm.thumb.jpg.957a289e17699b1c32fc23d95db23696.jpg

 

Заменил:

  T   Data;

 

На:

char Data[sizeof(T)];

 

Каламбур и алиасинг пропали,  но без операторов никак: 

arm2.thumb.jpg.8f6f713ec0bc36c8be161c17ce076e8e.jpg

 

Над невыровненными данными в программе производятся также операции.

Не только присваивание и инициализация.

 

Остаётся вариант, который я приводил выше.

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


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

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<нужный тип>

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


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

20 минут назад, Сергей Борщ сказал:

И ругань идет не на мой класс, а на ваши макросы. Что они делают - мне, на первый взгляд, не совсем понятно, но вот этот ужасный WORDn(x,n) я бы записал так:

ТС же писал выше зачем.

Операция чтения/записи невыровненной в памяти переменной (частный случай - packed) может быть скомпилена либо в одну инструкцию LDRx/STRx либо в набор байтовых инструкций.

Способ определяется наличием/отсутствием ключа -mno-unaligned-access и принципиальной возможностью поддержки невыровненного доступа выбранным ядром (>= CM3 (вроде)).

Даже на ядре >=CM3 программист по каким-то причинам может хотеть использовать CCR.UNALIGN_TRP=1 (ТС привёл одну из возможных причин). И тогда его макросы (как я понимаю) обеспечат работоспособность такого кода независимо от наличия -mno-unaligned-access. А ваши приведения вызовут fault.

 

Я тоже у себя в проектах иногда использую похожие макросы по той же причине (когда хочется потом попробовать скомпилить код для другого ARM-ядра, которое не поддерживает невыровненный доступ на аппаратном уровне и компилятор не имеет -mno-unaligned-access).

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


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

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

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

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

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

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

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

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

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