Forum: Mikrocontroller und Digitale Elektronik ARM Cortex M4F: Arglos in die Unalign-Falle


von Walter T. (nicolas)


Lesenswert?

Hallo zusammen,

nachdem unaligned-Zugriffe auf dem Cortex M4 mit einer Zeitstrafe belegt 
werden, wollte ich mein Programm dahingehend testen. Also habe ich 
irgendwo an Anfang von main() die "unalign_trp" aktiviert:
1
        uint32_t *CCR = 0xE000ED14;
2
        uint32_t unalign_trp = 1<<3;
3
        *CCR |= 1<<3;

In meinem eigentlichen Programm habe ich damit ein schlecht 
ausgerichtetes struct erwischt (auch wenn es mir da noch ein Rätsel ist, 
warum an dieser Stelle kein padding war - aber das ist eine andere 
Geschichte), aber ansonsten ist das komplett glimpflich abgelaufen.

Es gibt allerdings eine Funktion in den Modultests auf dem STM32F446RE, 
bei dem ich überhaupt nicht verstehe, wie und warum die unalign-Falle 
hier zuschnappt.

Der Quelltext:
1
    uint16_t crc16_calc(const uint8_t *buffer, uint8_t buffersize);
2
3
    TEST(crc, testValues)
4
    {
5
        uint8_t buffer[12]="Hello";
6
        uint16_t crc1;
7
8
        // CRC16 berechnen
9
        crc1 = crc16_calc(buffer, 5);
10
        TEST_ASSERT_EQUAL_HEX( 0xF377, crc1 );
11
    }

Das Disassembly:
1
080112d0 <TEST_crc_testValues_>:
2
3
    TEST(crc, testValues)
4
    {
5
 80112d0:  b510        push  {r4, lr}
6
 80112d2:  b086        sub  sp, #24
7
        uint8_t buffer[12]="Hello";
8
 80112d4:  4b0c        ldr  r3, [pc, #48]  ; (8011308 <TEST_crc_testValues_+0x38>)
9
 80112d6:  e893 0003   ldmia.w  r3, {r0, r1}
10
 80112da:  9003        str  r0, [sp, #12]
11
 80112dc:  f8ad 1010   strh.w  r1, [sp, #16]   
12
 80112e0:  2400        movs  r4, #0
13
 80112e2:  f8cd 4012   str.w  r4, [sp, #18]   <--- Hardfault in dieser Zeile
14
 80112e6:  f8ad 4016   strh.w  r4, [sp, #22]
15
        uint16_t crc1;
16
17
        // CRC16 berechnen
18
        crc1 = crc16_calc(buffer, 5);
19
 80112ea:  2105        movs  r1, #5
20
 80112ec:  a803        add  r0, sp, #12
21
 80112ee:  f7ff faf5   bl  80108dc <crc16_calc>
22
        TEST_ASSERT_EQUAL_HEX( 0xF377, crc1 );
23
 80112f2:  2344        movs  r3, #68  ; 0x44
24
 80112f4:  9300        str  r3, [sp, #0]
25
 80112f6:  2331        movs  r3, #49  ; 0x31
26
 80112f8:  4622        mov  r2, r4
27
 80112fa:  4601        mov  r1, r0
28
 80112fc:  f24f 3077   movw  r0, #62327  ; 0xf377
29
 8011300:  f7f0 fe5c   bl  8001fbc <UnityAssertEqualNumber>
30
    }
31
 8011304:  b006        add  sp, #24
32
 8011306:  bd10        pop  {r4, pc}
33
 8011308:  0802a6a0   .word  0x0802a6a0
34
 801130c:  54534554   .word  0x54534554
35
 8011310:  6372635f   .word  0x6372635f
36
 8011314:  7365745f   .word  0x7365745f
37
 8011318:  72655a74   .word  0x72655a74
38
 801131c:  5f6f        .short  0x5f6f
39
 801131e:  00            .byte  0x00
40
 801131f:  00            .byte  0x00
41
 8011320:  ff000014   .word  0xff000014

Sprich: Der Hard Fault wird schon in der ersten Zeile "uint8_t 
buffer[12]="Hello";" erzeugt.

Die einzige Möglichkeit, wie ich die Frage formulieren könnte, lautet: 
"WTF"?

Wieso wird der Wert #0 überhaupt auf den Stack geschoben? Und warum auf 
diese Weise? Und warum als Wort kompletter Breite? Und überhaupt?

: Bearbeitet durch User
von Niklas Gürtler (Gast)


Lesenswert?

Walter T. schrieb:
> Wieso wird der Wert #0 überhaupt auf den Stack geschoben?

Um das Array mit Nullen aufzufüllen, denn es sind ja nur die ersten 5 
Einträge belegt.

Kompilierst du mit "-munaligned-access"? Was genau macht dieses 
TEST-Makro, wie sieht exakt der erzeugte C-Code aus?

von Niklas Gürtler (Gast)


Lesenswert?

Walter T. schrieb:
> uint32_t *CCR = 0xE000ED14;

PS: Das kompiliert, so ganz ohne cast? "volatile" fehlt auch.

Ein minimaler Testcase, gern auch auf godbolt.org, wäre nicht schlecht.

von Walter T. (nicolas)


Lesenswert?

Niklas Gürtler schrieb:
> Das kompiliert, so ganz ohne cast? "volatile" fehlt auch.

Ja, die Warnung "makes pointer from integer without a cast" ist drin. 
Ich habe sie mit Absicht nicht weggecastet, damit ich nachher nicht 
vergesse, das später wieder zu entfernen.

Niklas Gürtler schrieb:
> Kompilierst du mit "-munaligned-access"?

Nein

Kompletter Compiler-Aufruf:
1
arm-none-eabi-gcc.exe -mcpu=cortex-m4  -mthumb -mfpu=fpv4-sp-d16 
2
  -D__HEAP_SIZE=0x0000 -D__STACK_SIZE=0x0100 
3
  -mpoke-function-name 
4
  -Wundef -Werror-implicit-function-declaration -Wdiv-by-zero -Wextra -Wall 
5
  -fno-strict-aliasing -fexec-charset=UTF-8 
6
  -mfloat-abi=hard 
7
  -Wdouble-promotion -Wunused-value -Wunused-variable -Wunused-parameter 
8
  -Wunused-label -Wunused-function -Wunused -Wunknown-pragmas -Wuninitialized 
9
  -Wreturn-type -Wmissing-braces -Wimplicit-int -Wimplicit-function-declaration 
10
  -Wformat -Wcomment 
11
  -fdata-sections -ffunction-sections 
12
  -O1 -g3  
13
  -DDOUBLE_MATH_ENABLED=1 -DARM_MATH_CM4 -DSTM32F446ZE -DSTM32F4XX -DUSE_STDPERIPH_DRIVER 
14
  -D__FPU_PRESENT -DUNITTEST_ENABLED=1 -DTARGET_HARDWARE_MYELS_REV01  
15
  -c src_library\src_mathlike\Test_crc.c 
16
  -o obj\myels_rev_01_debug\src_library\src_mathlike\test_crc.o 
17
  -MMD 
18
  -I.<sehr viele eigene Include-Verzeichnisse>
19
  -I.\src_3rd_party\src_stm32f4xx 
20
  -I.\src_3rd_party\src_u8g2_fonts 
21
  -I.\src_3rd_party\src_unittests_unity 
22
  -I"C:\Program Files (x86)\EmBitz\1.11\share\em_armgcc\bin\..\arm-none-eabi\include" 
23
  -I"C:\Program Files (x86)\EmBitz\1.11\share\em_armgcc\bin\..\arm-none-eabi" 
24
  -I"C:\Program Files (x86)\EmBitz\1.11\share\em_armgcc\bin\..\lib\cmsis\include"

Niklas Gürtler schrieb:
> Was genau macht dieses
> TEST-Makro, wie sieht exakt der erzeugte C-Code aus?

Das stückelt drei Makro-"Funktionen" (TEST_SETUP(crc), TEST(crc, 
testValues) und  TEST_TEAR_DOWN(crc)) zu einer Funtion 
TEST_crc_testValues_() zusammen. Da die beiden anderen Makro-Funktionen 
hier leer sind, spielen sie hier keine Rolle.

Niklas Gürtler schrieb:
> Um das Array mit Nullen aufzufüllen, denn es sind ja nur die ersten 5
> Einträge belegt.

Okay, das Array wird direkt auf dem Stack angelegt, und er versucht, die 
fehlenden 6 Elemente als zwei WORDs zu schreiben - was aufgrund des 
Index mit einem unaligned-access fehlschlägt. Außerdem schreibt er zwei 
Null-Bytes dahinter, die gar nicht gebraucht werden.

Niklas Gürtler schrieb:
> Ein minimaler Testcase, gern auch auf godbolt.org, wäre nicht schlecht.

Stimmt. Mal sehen, ob ich das hinbekomme.

von Niklas Gürtler (Gast)


Lesenswert?

Walter T. schrieb:
> Nein
> Kompletter Compiler-Aufruf:

Ich glaube das ist standardmäßig an für ARMv7M. Übergebe mal 
-mno-unaligned-access.

Walter T. schrieb:
> -fno-strict-aliasing

Sicher dass du das aktiviert haben willst?

Walter T. schrieb:
> Außerdem schreibt er zwei Null-Bytes dahinter, die gar nicht gebraucht
> werden.

Der GCC war noch nie gut darin Variablen effizient zu initialisieren... 
Viel effizienter wäre es wohl einen schlichten Pointer auf ein 
String-Literal zu verwenden anstatt den String in ein Stack-Array zu 
kopieren.

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Haste denn bei "Hardfault in dieser Zeile" schon den Pipelining Offset 
abgezogen?
Sonst reden wir über die falsche Stelle.

Der Inhalt des SP Registers wär an der Stelle auch interessant.

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Ich bekomme auch im Godbolt den unaligned access (netterweise direkt 
kommentiert):

https://godbolt.org/z/PzM6fdez4

Er wird durch die Option "-mcpu=cortex-m4" ausgelöst. Ohne ist er weg.

Niklas Gürtler schrieb:
> Übergebe mal
> -mno-unaligned-access.

Stimmt. Mit dieser Option ist er auch mit -mcpu=cortex-m4 weg.

Mw E. schrieb:
> Haste denn bei "Hardfault in dieser Zeile" schon den Pipelining Offset
> abgezogen?
> Sonst reden wir über die falsche Stelle.

Ich habe mich mit dem Debugger bis zu dieser Zeile vorgetastet und lande 
danach im HardFault-Handler. Und das Listing zeigt ja, dass da wirklich 
etwas im Stack nicht ausgerichtet ist.

Wahnsinn. 20595 Zeilen Quelltext und das ist die einzige Stelle, wo mir 
das Alignment auf die Füße fällt.

Niklas Gürtler schrieb:
> Walter T. schrieb:
>> -fno-strict-aliasing
>
> Sicher dass du das aktiviert haben willst?

Guter Hinweis. Eigentlich brauche ich das schon lange nicht mehr.

: Bearbeitet durch User
von Nop (Gast)


Lesenswert?

Niklas Gürtler schrieb:

> Ich glaube das ist standardmäßig an für ARMv7M.

Stimmt, siehe https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html :

-munaligned-access
-mno-unaligned-access
Enables (or disables) reading and writing of 16- and 32- bit values from 
addresses that are not 16- or 32- bit aligned. By default unaligned 
access is disabled for all pre-ARMv6, all ARMv6-M and for ARMv8-M 
Baseline architectures, and enabled for all other architectures. If 
unaligned access is not enabled then words in packed data structures are 
accessed a byte at a time.

von Walter Tarpan (Gast) (Gast)


Lesenswert?

Niklas Gürtler schrieb:
> Viel effizienter wäre es wohl einen schlichten Pointer auf ein
> String-Literal zu verwenden anstatt den String in ein Stack-Array zu
> kopieren

Ich will nicht drüber reden. peinlich

von Mw E. (Firma: fritzler-avr.de) (fritzler)


Lesenswert?

Walter T. schrieb:
> Ich habe mich mit dem Debugger bis zu dieser Zeile vorgetastet und lande
> danach im HardFault-Handler. Und das Listing zeigt ja, dass da wirklich
> etwas im Stack nicht ausgerichtet ist.

Ja dann wirds da sein.
Wo du noch unaligned Probleme bekommen kannst ist die FPU.
Die mag keine unaligned floats einlesen/schreiben, das gibt direkt nen 
fault.

von Walter T. (nicolas)


Lesenswert?

Jetzt weiss ich auch wieder, warum diese komische String-Variante. Eine 
Altlast. In einer früheren Inkarnation hat die CRC-Funktion das Ergebnis 
an das Array angehängt.

von Hänsele (Gast)


Lesenswert?

Walter T. schrieb:
> uint8_t buffer[12]="Hello";

Dem Index 0 eines 12 byte großen Array auf uint8_t wird
"Hello"
zugewiesen???

von Nop (Gast)


Lesenswert?

Hänsele schrieb:
> Walter T. schrieb:
>> uint8_t buffer[12]="Hello";
>
> Dem Index 0 eines 12 byte großen Array auf uint8_t wird
> "Hello" zugewiesen???

Nein.

von Phantomix X. (phantomix)


Lesenswert?

Hänsele schrieb:
> Walter T. schrieb:
>> uint8_t buffer[12]="Hello";
>
> Dem Index 0 eines 12 byte großen Array auf uint8_t wird
> "Hello"
> zugewiesen???

Index 0 ist und wird immer Index 0 bleiben, wenn du einer Indexvariable 
was anderes als 0 zuweist, ist sie nicht mehr 0 ;-)

Natürlich füllt oberes Konstrukt den Speicherbereich "buffer" mit
{ 'H', 'e', 'l', 'l', 'o', 0, 0, 0, 0, 0, 0, 0 }

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.