Dziwny problem z kodem w C (gcc mips/pic32)

May 17, 2023 Last reply: 3 years ago 169 Replies

Pierwszy raz się spotkałem z czymś takim, o to problematyczny fragment kodu:


const char *connectionFailureStrings[] = {                                         "NULL",           /* 0 - not used */                                         "NULL",           /* 1 - not used */                                         "WF_JOIN_FAILURE",           /* 2            */                                         "WF_AUTHENTICATION_FAILURE",           /* 3            */                                         "WF_ASSOCIATION_FAILURE",           /* 4            */                                         "WF_WEP_HANDSHAKE_FAILURE",           /* 5            */                                         "WF_PSK_CALCULATION_FAILURE",           /* 6            */                                         "WF_PSK_HANDSHAKE_FAILURE",           /* 7            */                                         "WF_ADHOC_JOIN_FAILURE",           /* 8            */ "WF_SECURITY_MISMATCH_FAILURE",          /* 9            */ "WF_NO_SUITABLE_AP_FOUND_FAILURE",       /* 10           */ "WF_RETRY_FOREVER_NOT_SUPPORTED_FAILURE"/*11*/ };



static void OutputConnectionFailedMsg(UINT16 eventInfo) {     UINT8 status;     UINT8 reason;     status = (UINT8)(eventInfo >> 8);     reason = (UINT8)(eventInfo & 0xff);



   printf (" status %d  %p\r\n",status,connectionFailureStrings[status]);    printf("WF_Event: Connection Failed: %s ",connectionFailureStrings[status]); }


Mamy tablicę stringów umieszczoną we flash (pamięci stałej) dzięki const. Niestety w losowych przypadkach drugi printf generuje wyjątek  adresu z poza zakresu. Zmienna status nie przekracza zakresu i zawsze nawet jak jest wyjątek  ma wartość 3. Okazuje się, że gdy jest wyjątek to wskaźnik (%p w pierwszym printf) pokazuje jakiś bzdurny adres, co oczywiście doprowadza do wyjątku w drugim printf. Jak to możliwe, że adres jest bzdurny?? Przecież tablica jest we flash, adres tutaj powinien być stały i wygenerowany na etapie kompilacji. Oczywiście w większości przypadków gdy wszystko jest OK printf pokazuje prawidłowy adres 3 indeksu w tej tablicy. Ale raz na 10 startów kodu nie... Co ciekawe wygląda na to, że bzdurny adres pojawia się przy pierwszym uruchomieniu kodu po flashowaniu mcu ale też nie za każdym razem... 



Cały kod jest dość obszerny (75 tys linii), nie ma innych problemów, to jedyne miejsce, więc chyba można wykluczyć problem z mcu/flash...


Nie testowałem, ale na pierwszy rzut oka to w deklaracji brakuje jednego consta:

formatting link
obecnej formie masz tablicę, która wskazuje na stringi we flashu. To jest inicjowana stała, więc można by przypuszczać ze zawsze będzie miała taką samą zawartość, ale czasami coś się popsuje. Sprawdź w jakim segmencie jest umieszczona tablica, i jeśli w RAMie, to zacznij szukać gdzie masz bląd w programie, bo najprawdopodobniej coś gdzieś wyjeżdża poza zaalokowany obszar i nadpisuje losowe rejony pamięci.

Jacek

Marek wrote on 17.05.2023 21:23:

Nie, string masz we flashu, ale tablicę wskaźników do nich masz w RAMie.

Trzeba było napisać: const char * const connectionFailureStrings[] = {

}

Jest we flash, tak jak powinno. Gdy wszystko działa jak należy wskaźnik wskazuje na adres we flash, dump mapy pamięci kodu również wskazuje, że ta tablica jest we flash. Ten fragment kodu nawet nie jest mój, to fragment z biblioteki MCHP, zresztą *to* nie wygląda na błąd w kodzie. Jedyne podejrzenie to użycie uint8 zamiast int jako indexu ale to nie powinno powodować problemu.

Okok faktycznie. Dobra, szukam dalej co po tej tablicy jeździ..

W dniu 17.05.2023 o 21:23, Marek pisze:

Dla mnie trochę dziwny jest ten fragment, reason-nie wykorzystana zmienna a komunikat (z tablicy) dwa razy wywołujesz ten sam, zmienna status.

Kod na potrzeby posta trochę uprościłem. Znalazłem dziada:

unsigned short BT[300]; int i;

for (i=0; i<sizeof(BT);i++) BT[i] = getval(i);

Analizując mapę linkera widać, że BT była umieszczona tuż przed tamtą tablicą ze wskaźnikami do stringów. Na starość to trzepać worki po cemencie a nie programować....

For

No i co z tego, przecież to dwie osobne tablice i osobno się adresują i chyba kompilator czy linkier nie ma tu błędu w adresacji?

Eee tam, ja mam już prawie 63 i dalej piszę programy i naprawiam elektronikę. A zaczynałem przygodę z komputerami od Mery 9150 i jej asemblera w połowie lat '80 czyli dość późno.

Chodzi o to, że BT namierzyłem z mapy linkera bo było zaadrasowane tuż przed tamtą, więc stała się podejrzaną o przepełnienie. Błedne użycie sizeof() powodowało przepełnienie w tym for() i wjazd na tą drugą. Jak widzisz wiek ma jednak znaczenie ;)

czwartek, 18 maja 2023 o 12:44:14 UTC+2 Janusz napisał(a):

Wykonaj kod na kartce to się dowiesz. To nie turbo pascal tylko C na uC - i tak się dziwię, że OP pisze tu o jakichś wyjątkach, może to w wersji mips pod jakimś unixem. A chwalisz się tym asemblerem Mery 9150 - nie ma to jak używać terminala jako komputera, stąd się przecież wziął bodajże 8008 ;P

No wlasnie - const to jedno, ale cos jezdzi.

Przekompiluj jak wyzej - moze wyrzuci błąd i znajdziesz, ale jesli nie, to moze być bardziej skomplikowane szukanie.

J.

Janusz <janusz snipped-for-privacy@o2.pl napisał(a):

Niby osobno adresowane ale nie do końca, bo są w jednej pamięci. Jak wyjedziesz poza zakres jednej, to wpadniesz na drugą. To nie jest błąd kompilatora ani linkera ale uroki C. Błąd w kodzie polega na zapomnieniu, że sizeof() nie służy do zwracania liczby elementów tablicy. Można go użyć w tej roli tylko dla tablic o typie jednobajtowym (char, uint8_t itp) a nie short.

Słusznie. Pozwólcie, że wsadzę kij w mrowisko i pokażę co należy używać od kilku lat:

formatting link
A bodaj od 20 lat jest w boost.

heby snipped-for-privacy@poczta.onet.pl> napisał(a):

Ale to trzeba zacząć C++ używać :)

Błagam, przecież pic32 to pełen mips dlaczego nie ma mieć wyjątków?? Ma nawet wsparcie częściowe do VM user/kernel space. A MZy nawet TBLe do translacji adresów wirtualnych. Na ATmega8 świat się nie kończy...

czwartek, 18 maja 2023 o 14:01:30 UTC+2 Marek napisał(a):

O, nawet się nie zainteresowałem, że pic32 to mips - sądziłem, że się bardziej wysilili. Wszystko kwestia kosztu, w tym ilości RAMu - 386 było już w 1986, ja miałem już w 1933, a Linux Torvalds, dla zbawienia ludzkości, na szczęście już w 1990 (chyba).

No ale żeby się nie kopać po kostkach, to i na 386 można sobie puszczać jeden program (tak narodził się Linux - Linusowi nie podobał się emulator terminal w minixie, więc napisał własny, działający na "gołym" 386 - ale 32-bitowym), i pewnie na pic32 też - ale żeby były wyjątki obsługiwane, to coś tam jeszcze trzeba albo dokompilować - w stylu eCosa, czyli jednozadaniowego, wielowątkowego systemu operacyjnego - lub coś po prostu puszczać, w stylu Linuxa czy innego OSa. Więc co tam jeszcze masz? Czy wszystko, łącznie z obsługą wyjątków, jest w tych 75k linii kodu? Bo można i tak, choć ciekawi, jak te wyjątki obsługujesz.

czwartek, 18 maja 2023 o 13:54:05 UTC+2 Grzegorz Niemirowski napisał(a):

Zaraz usłyszysz, że "jest za darmo" ;>

Tak. Trzeba zmienić 2 litery w nazwie kompilatora na dwa +.

I to wystarczy, żeby sizeof zaczął oznaczać liczbę elementów tablicy? Analogicznie do dowodu nie wprost: "Jedna rana stanowczo śmiertelna, ale pozostałe dadzą się wyleczyć!"

Wyjątki obsługuje funkcja przypisana do exception handler, ją już wywołuje core procesorami. Jest kilka wyjątków:

static enum {       EXCEP_IRQ = 0,            // interrupt       EXCEP_AdEL = 4,            // address error exception (load or ifetch)       EXCEP_AdES,                // address error exception (store)       EXCEP_IBE,                // bus error (ifetch)       EXCEP_DBE,                // bus error (load/store)       EXCEP_Sys,                // syscall       EXCEP_Bp,                // breakpoint       EXCEP_RI,                // reserved instruction       EXCEP_CpU,                // coprocessor unusable       EXCEP_Overflow,            // arithmetic overflow       EXCEP_Trap,                // trap (possible divide by zero)       EXCEP_IS1 = 16,            // implementation specfic 1       EXCEP_CEU,                // CorExtend Unuseable       EXCEP_C2E                // coprocessor 2   } _excep_code;

Oczywiście można po wyjątku wyjść z handlera i działać dalej ale w moim przypadku handler zapamiętuje co i gdzie się wydarzyło i zatrzymuje kod aż watchdog zresetuje procesor.

Kiedyś na MZ zrobiłem małego OSa z ochroną pamięci, obsługą pamięci wirtualnej i TBLami, ładowaniem procesów itp... Skończyło się tylko na tym, że jeden proces wypisywał AAA a drugi BBB na konsoli (zbieżność z początkiem innego OSa przypadkowa). Zresztą na MZ można uruchomić pełnego Linuxa więc....

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required