|
Обработка математических ошибок в ARM |
|
|
|
Jan 15 2015, 10:14
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(cyrax0 @ Jan 15 2015, 14:35)  Не очень понимаю, с какой стати errno может быть вынесен в отдельную секцию. У меня стандартные секции ROM, RAM, CMMRAM. В map-файле errno, скорее всего, вот он: __iar_Errno 0x20008bdc 0x4 Data Gb errno.o [3] Находится в стандартной секции RAM. RAM - это не секция, это одна из целевых областей линковки. Я говорил о выходных секциях компилятора (для IAR это: .text, .bss и т.п.). Часто бывает что в библиотеках определяют собственные имена секций, для возможности более оптимально размещать их в памяти. Посмотрел у себя в map-файле: Код .bss zero 0x40006ef8 0x8 errno.o [3] __iar_Errno 0x40006efc 0x4 Data Gb errno.o [3] __iar_Tls_setup___iar_Errno 0x40006ef8 0x4 Data Gb errno.o [3] Как видно - в .bss-секцию из файла errno.o слинкованы 8 байт (две переменных: __iar_Errno и __iar_Tls_setup___iar_Errno). Вот этот кусочек и можно перенести в отдельный регион MPU_RO_mem, который потом закрыть по записи MPU. В icf-файле: place in MPU_RO_mem {section .bss object errno.o};Правда косяк, что прерывание будет происходить и при модификации __iar_Tls_setup___iar_Errno если она будет случаться. Это нужно будет учесть в ISR. Минус такого способа конечно в том, что гранулярность защиты памяти в MPU довольно большая (дискретность границ вроде около 1 кБ). Так что потеряете на этом размер ОЗУ равный гранулярности. Можно конечно смаппировать MPU_RO_mem на адреса, где нет ОЗУ. Но тогда (если нужно значение errno) надо будет в ISR разбирать код команды вызвавшей сбой защиты и определять регистр-источник записываемого значения. Цитата(cyrax0 @ Jan 15 2015, 14:35)  Если править библиотеки, то проще уж наверное в math.h добавить проверку аргументов, чтобы не заморачиваться с MPU? С ним я еще не работал. Зато будет польза что разобрались с ним
|
|
|
|
|
Jan 15 2015, 10:22
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(cyrax0 @ Jan 15 2015, 12:25)  В итоге так, скорее всего, и придется сделать. Минус в том, что вместо math.h нужно будет подключать условный safemath.h, что с учетом сотрудников-вредителей не факт, что будет выполняться. Не проверять же мне потом все их программы. Не нужно это, достаточно написать свой файл math.h и указать компилятору директорию с этим файлом для поиска заголовков. Яр ищет заголовки в папке стандартной библиотеки в последнюю очередь, то есть свой файл подцепится первым. Правда, как оттуда подцепить настоящий math.h, не совсем ясно :-) Можно по абсолютному пути, но это не очень комильфо. Update: Там есть забавная прагма include_alias. Файл preinclude.h (заставить включать его ключом компилятора --preinclude): Код #pragma include_alias (<math.h> , "safemath.h") Файл safemath.h: Код #pragma include_alias (<math.h> , <math.h>) #include <math.h>
...
|
|
|
|
|
Jan 15 2015, 10:27
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(scifi @ Jan 15 2015, 15:08)  Не факт. Вроде бы линкеры позволяют размещать данные в том числе по имени объектного файла. Нужно только вычислить, в каком файле определяется errno. Кроме того, можно обработать файл библиотеки ELF-тулзами и подменить название секции. Позволяют, но только не отдельные переменные, а выходные секции компилятора. Если для переменной не задана явно секция, то она будет компилятором объединена в секцию .bss общую для всех переменных данного файла. И разделить эту .bss потом, на этапе линковки, невозможно. Тут надо или в исходниках для errno явно указать секцию отличную от .bss. Или вроде есть опция компилятора, которая позволяет каждую переменную выделять в отдельную секцию. Не помню только это в IAR или где я видел. Но это увеличивает размер кода, так как если ранее компилятор для группы соседних переменных мог в .text создавать одну ссылку (указатель на начало секции), то теперь ему нужно будет отдельно адресовать каждую переменную. Так что эту опцию нужно применить только к файлу errno.c.
|
|
|
|
|
Jan 15 2015, 12:28
|

Гуру
     
Группа: Модератор FTP
Сообщений: 4 479
Регистрация: 20-02-08
Из: Москва
Пользователь №: 35 237

|
Господа! Вы что, с ума сошли?  Переменную errno в отдельную секцию выносить??? Все это можно сделать много проще, а не такими драконовскими мерами. Заметим, что ни в одной функции, и log() в том числе, значение errno не присваивается явно, а только через вызов дефайнов: Код #define _FERAISE_INVALID() { errno = EDOM; } #define _FERAISE_DIVBYZERO() { errno = ERANGE; } #define _FERAISE_OVERFLOW() { errno = ERANGE; } #define _FERAISE_UNDERFLOW() { errno = ERANGE; } #define _FERAISE_INEXACT() Так что, если вы взялись за дело настолько серьезно, что решили перекомпилировать библиотеку, то было бы куда проще переопределить эти 5 дефиниций на свои функции. Однако я полагаю, что и в этом нет большой нужды, т.к. эти дефиниции определены в хидере xmath.h не жестко, а условно: Код #if defined(math_errhandling) && math_errhandling == MATH_ERRNO
/* Optimization of the _Feraise function when the IAR default is used. */ #define _FERAISE_INVALID() { errno = EDOM; } #define _FERAISE_DIVBYZERO() { errno = ERANGE; } #define _FERAISE_OVERFLOW() { errno = ERANGE; } #define _FERAISE_UNDERFLOW() { errno = ERANGE; } #define _FERAISE_INEXACT()
#else ... // здесь определена функция _Feraise()
#define _FERAISE_INVALID() _Feraise(FE_INVALID) #define _FERAISE_DIVBYZERO() _Feraise(FE_DIVBYZERO) #define _FERAISE_OVERFLOW() _Feraise(FE_OVERFLOW) #define _FERAISE_UNDERFLOW() _Feraise(FE_UNDERFLOW) #define _FERAISE_INEXACT() _Feraise(FE_INEXACT)
#endif Т.е. достаточно определить "math_errhandling" иначе, чем MATH_ERRNO, чтобы определение этих дефиниций изменилось на альтернативное, с явным вызовом функции _Feraise(), получающей код ошибки в качестве параметра. Саму функцию _Feraise() при желании можно легко заменить на свою (т.к. самодельный вариант линкуется раньше библиотечного), а можно оставить той, что написана авторами компилятора - она там errno присваивает, а потом исключение вызывает. Одного только я уверенно сказать не могу, нужно ли для этого перекомпилировать библиотеку или достататочно будет определить math_errhandling перед включением хидера xmath.h.
|
|
|
|
|
Jan 15 2015, 13:46
|
Участник

Группа: Участник
Сообщений: 22
Регистрация: 9-12-14
Пользователь №: 84 046

|
Цитата(scifi @ Jan 15 2015, 14:22)  Там есть забавная прагма include_alias. Спасибо, этот вариант подходит! Только код пришлось написать немного по-другому: Код #pragma include_alias("math.h", "safemath.h") #pragma include_alias(<math.h>, <safemath.h>) Цитата(jcxz @ Jan 15 2015, 14:14)  RAM - это не секция, это одна из целевых областей линковки. Я говорил о выходных секциях компилятора (для IAR это: .text, .bss и т.п.). Часто бывает что в библиотеках определяют собственные имена секций, для возможности более оптимально размещать их в памяти. Хорошо, но bss - это тоже стандартная секция. Цитата В icf-файле: place in MPU_RO_mem {section .bss object errno.o}; Попробовал так: define region MPU_region = mem:[from 0x20000000 to __ICFEDIT_region_RAM_start__ - 1]; place in MPU_region {section .bss object errno.o}; // Place in RAM_region .data, .bss, and .noinit place in RAM_region { readwrite };Возможно place in RAM_region перекрывает директиву place in MPU_region - __iar_Errno у меня остался на прежнем месте в RAM. Цитата Минус такого способа конечно в том, что гранулярность защиты памяти в MPU довольно большая (дискретность границ вроде около 1 кБ). Вроде бы нет. Регистр MPU_RASR в Cortex-M4: Bits 5:1 SIZE: Size of the MPU protection region. The minimum permitted value is 3 (b00010), see SIZE field values for more information. Bit 0 ENABLE: Region enable bit. SIZE field values The SIZE field defines the size of the MPU memory region specified by the MPU_RNR regsiter as follows: (Region size in bytes) = 2(SIZE+1) The smallest permitted region size is 32B, corresponding to a SIZE value of 4.Цитата Правда косяк, что прерывание будет происходить и при модификации __iar_Tls_setup___iar_Errno если она будет случаться. Это нужно будет учесть в ISR. А в ISR MPU можно получить, скажем, адрес функции, которая вызвала прерывание? Цитата Зато будет польза что разобрались с ним  Честно говоря, сейчас скорее всего не будет на это времени. Но разобраться все равно хотел бы  . XeniaВариант интересный, спасибо. Хотя у нас подключается math.h, но через алиас можно и xmath.h подключить. Перекомпиляция нужна, если меняется один из дефайнов, используемых в библиотеке? Я этим никогда не занимался, не представляю как это делается, хотя сейчас наверно и не понадобится. Еще раз всем спасибо за ответы! Вопрос в принципе решен  .
Сообщение отредактировал cyrax0 - Jan 15 2015, 13:48
|
|
|
|
|
Jan 15 2015, 13:53
|
Участник

Группа: Участник
Сообщений: 22
Регистрация: 9-12-14
Пользователь №: 84 046

|
Цитата(scifi @ Jan 15 2015, 17:47)  Подозреваю, что топикстартера не только логарифм интересует. А если он ещё захочет ловить деление на ноль, то ему точно от Си++ и перегрузки операторов не отвертеться. Деление на ноль как раз ловится элементарно: SCB->CCR |= 0x10; Без этого (по умолчанию) деление на ноль дает ноль. С этим - HardFault.
|
|
|
|
|
Jan 15 2015, 17:08
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Xenia @ Jan 15 2015, 18:28)  Господа! Вы что, с ума сошли?  Переменную errno в отдельную секцию выносить??? Все это можно сделать много проще, а не такими драконовскими мерами. Переменная errno в IAR уже практически в отдельной секции. Я же написал выше. И способ с MPU хорош тем, что не надо ничего перекомпилировать и вмешиваться в библиотеку. Цитата(cyrax0 @ Jan 15 2015, 19:46)  Попробовал так: define region MPU_region = mem:[from 0x20000000 to __ICFEDIT_region_RAM_start__ - 1]; place in MPU_region {section .bss object errno.o}; // Place in RAM_region .data, .bss, and .noinit place in RAM_region { readwrite }; Возможно place in RAM_region перекрывает директиву place in MPU_region - __iar_Errno у меня остался на прежнем месте в RAM. Так поменяйте их местами. Линкер считывает команды по порядку и последующая перекрывает действие предыдущих. Цитата(cyrax0 @ Jan 15 2015, 19:46)  А в ISR MPU можно получить, скажем, адрес функции, которая вызвала прерывание? Вы же уже открыли даташит на странице MPU? Так и прочитайте Я не использовал на практике MPU на Cortex, только на других CPU. Если адрес команды записи в errno нельзя получить из регистров MPU, то очевидно его можно получить из сохранённого контекста на входе в прерывание. Только не забудьте отключить write-back для защищаемого региона.
|
|
|
|
|
Jan 16 2015, 06:09
|
Участник

Группа: Участник
Сообщений: 22
Регистрация: 9-12-14
Пользователь №: 84 046

|
Цитата(jcxz @ Jan 15 2015, 21:08)  Так поменяйте их местами. Линкер считывает команды по порядку и последующая перекрывает действие предыдущих. Я попробовал и так, и так. Цитата(scifi @ Jan 15 2015, 18:51)  И с плавучкой это будет работать? Бинго! С плавучкой не работает. Спасибо за подсказку!
|
|
|
|
|
Jan 16 2015, 07:25
|

бессмертным стать можно тремя способами
    
Группа: Свой
Сообщений: 1 405
Регистрация: 9-05-06
Из: Москва
Пользователь №: 16 912

|
здравствуйте. вставлю свои 5 копеек. 1. FPU вычислительный блок который, как справедливо заметила Xenia, над данными типа флоат умеет делать арифмитические операци + немного необходимых ништяков. FPU за то то эти арифметические операции он сделал правильно с предсказуемым результатом. 2. в силу того сто косинусы логарифмы и тд это функции с аргументом типа флоат не раелизованы аппаратно в соответствующих библиотеках они реализованы как подпрограмки. стандарно это делается в виде быстросходящегося ряда. поэтому FPU понятия не имеет что за результат он получил в результате вычисленй и не может интепретировать результат логарифма - эта сущность выше по уровню. В частности результат NAN или INF это хороший правильный резульат - какие данные воткнули он вам для них выдал то что описывает стандарт IEEE764 .... он свое дело сделал и может честно отдыхать. 3. Вывод - в данной случае если вызывающая сторона не может контролировать входные данные то анализ резульата подпрограммы должна делать она сама - это забота libm (math.h) что и предполагается по стандарту на POSIX Standard C Library (https://ru.wikipedia.org/wiki/Errno.h). Код либы гарантирует что он в переменную errno запишет сотояние результата .... он свое дело сделал и может честно отдыхать. А его анализ ложится на вызывающую сторону. Если ты вызвал логарифм с кривыми входными данными - о чем ты можен по многим причина не знать и не ПРОВЕРИЛ СОСТОЯНИЕ РЕЗУЛЬТАТА то ты сам ДУРАК. одним словом FPU + libc/libm дает иструмент позволяющий выплыть из нештатной ситуации. пример - возьмем реализацию из библиотеки NewLib в части libm : log Код #include "fdlibm.h" #include <errno.h>
#ifdef __STDC__ float logf(float x) /* wrapper logf */ #else float logf(x) /* wrapper logf */ float x; #endif { #ifdef _IEEE_LIBM return __ieee754_logf(x); #else float z; struct exception exc; z = __ieee754_logf(x); if(_LIB_VERSION == _IEEE_ || isnan(x) || x > (float)0.0) return z; #ifndef HUGE_VAL #define HUGE_VAL inf double inf = 0.0;
SET_HIGH_WORD(inf,0x7ff00000); /* set inf to infinite */ #endif exc.name = "logf"; exc.err = 0; exc.arg1 = exc.arg2 = (double)x; if (_LIB_VERSION == _SVID_) exc.retval = -HUGE; else exc.retval = -HUGE_VAL; if(x==(float)0.0) { /* logf(0) */ exc.type = SING; if (_LIB_VERSION == _POSIX_) errno = ERANGE; else if (!matherr(&exc)) { errno = ERANGE; } } else { /* logf(x<0) */ exc.type = DOMAIN; if (_LIB_VERSION == _POSIX_) errno = EDOM; else if (!matherr(&exc)) { errno = EDOM; } exc.retval = nan(""); } if (exc.err != 0) errno = exc.err; return (float)exc.retval; #endif } здесь видно что если чтото пошло не так с точки зрения сути логарима то это отражается в глобальной для потока переменной errno для того чтоб пользователь не улетел в космос далнейшими вычислениями..... 4. это было введение о том как есть, а теперь мое предложение о том как сделать решение как хочется автору поста (естественно это приминительно для CORTEX-M4F или прочий ибо автор про его FPU затеял тему). если нада дествительно эксепшен так что хачу-нимагу!! , ну например подчиненные программисты лесом посылают errno и лепят горбатого в своем коде то можно поступить следующим образом. берем исходнички libm и во всех местах где errno присваиваются кода крывых состояний вызывать svc - бутет порожден эксепшен который приведет к исполненению обработчика прерывания - вот тут уже бойцы не смогут перепрыгнуть через окоп для танков и им прийдется прореагировать на ситуацию что чтото пошло не так. 5. а вообще, я считаю, нада всех кто не хочет стандарты изучать и применять в жизни, тек кто изобретает велосипЭд и прочих водителей луноходов пороть ремнем три раза в день и оставлять без ужина  надесь чем то помог.
|
|
|
|
|
Jan 16 2015, 17:55
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(cyrax0 @ Jan 16 2015, 12:09)  Я попробовал и так, и так. Что-то у вас странное происходит... Вот выдержка из icf из одного моего текущего проекта: Код place in RAM_regionA {ro, first block IMAGE_HEAD, last section .codetail}; place in RAM_regionBB { section .text object timeout.o, section .text object str.o, ... }; Всё, что посылается в RAM_regionBB там и оказывается.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|