Forum: Compiler & IDEs GCC: Variable wird trotz 'KEEP' im Linkerscript wegoptimiert?


von Ralf (Gast)


Lesenswert?

Hallo,

für die Programmierung eines STM32 verwende ich Atollic Truestudio, also 
GCC drunterliegend und bastle mir gerade die Option Bytes direkt ins ELF 
bzw. HEX File.

Hierfür habe ich das Linkerscript wie folgt modifiziert:
1
...
2
MEMORY
3
{
4
  ...
5
  OPT_BYTES (r)   : ORIGIN = 0x1FF80000, LENGTH = 32
6
}
7
8
/* Define output sections */
9
SECTIONS
10
{
11
  /* user option bytes */
12
  .opt_bytes :
13
  {
14
    . = ALIGN(4);
15
    KEEP(*(.FLASH_OPTR_L*)) /* FLASH_OPTR register */
16
    KEEP(*(.FLASH_OPTR_H*))
17
    KEEP(*(.FLASH_WRPROT1_L*)) /* FLASH_WRPROT1 register */
18
    KEEP(*(.FLASH_WRPROT1_H*))
19
    KEEP(*(.FLASH_WRPROT2_L*)) /* FLASH_WRPROT2 register */
20
  } >OPT_BYTES
21
  ...
Die Variablen hatte ich ursprünglich wie folgt angelegt:
1
const uint32_t FLASH_OPTR_L __attribute__((section(".FLASH_OPTR_L"))) = 0x20DF55AA;
Im Debug-Build taucht's korrekt im ELF/HEX-File auf, im Release ist es 
wohl wegoptimiert.
Abhilfe schafft das Attribut 'used':
1
const uint32_t FLASH_OPTR_L __attribute__((section(".FLASH_OPTR_L"))) __attribute__((used)) = 0x20DF55AA;
Ob das ganze nun denn auch so die gewünschte Wirkung erzielt muss ich 
noch ausprobieren, was mich gerade interessiert ist, warum ich die 
Variablen zusätzlich mit dem Attribut 'used' belegen muss, wenn ich doch 
im Linkerscript die Sektionen schon mit KEEP markiert habe - ich bin da 
nun einfach neugierig :)
Kann's mir jemand erklären?

Gruß Ralf

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ralf schrieb:
> was mich gerade interessiert ist, warum ich die Variablen
> zusätzlich mit dem Attribut 'used' belegen muss, wenn ich doch
> im Linkerscript die Sektionen schon mit KEEP markiert habe

Davon weiß GCC aber nix.

"used" wirkt auf Compiler-Ebene.

"KEEP" wirkt auf Linker-Ebene.

Wenn globale Optimierungen aktiviert sind, dann kann GCC nicht 
referenzierte Objekte / Funktionen entsorgen.  Gleiches gilt auch auf 
Ebene einer Compilation Unit, wenn der Compiler die Nichtreferenzierung 
auf dieser Ebene nachweisen kann.

Der Linker arbeitet nur mit Symbolen und Input- und Output-Sections. 
Wenn aus einer bestimmten Input-Section keine Symbole referenziert 
werden, dann kann der Linker diese Section mit --gc-sections entsorgen 
falls nicht mit KEEP markiert.  Dabei gilt des Entry-Symbol als 
referenziert.

In deinem Falle braucht's auf Linker-Ebene ein KEEP und auf 
Compiler-Ebene
1
__attribute__((__used__,__externally_visible__))

: Bearbeitet durch User
von Ralf (Gast)


Lesenswert?

Ah, okay. Dann hab ich ja die richtige Lösung verwendet.

>> In deinem Falle braucht's auf Linker-Ebene ein KEEP und auf
>> Compiler-Ebene
>> __attribute__((_used_,__externally_visible__))
Das muss ich mal probieren, ob ich das zweimalige _attribute_ 
wegbekomme.

Besten Dank.

Ralf

von Blume (Gast)


Lesenswert?

Du kannst auch mal testen ob es reicht deine Variable zusätzlich

extern zu deklarieren:
1
extern const uint32_t FLASH_OPTR_L;

vielleicht in einem Header File das inkludiert wird.


Das hat bei mir in ähnlichen Situationen geholfen.

- Blume

von Bernd K. (prof7bit)


Lesenswert?

Ich hab das bei nem Freescale mal so gelöst:

C-Code:
1
#define SECT_FLASHCONF          __attribute__((section(".kinetis_flash_config_field"), __used__))
1
SECT_FLASHCONF const long __flash_config[4] = {
2
    0xffffffff,
3
    0xffffffff,
4
    0xffffffff,
5
    0xfffeffff,
6
};

Linkerscript:
1
MEMORY
2
{
3
  VECTORS           (rx) : ORIGIN = 0x00000000, LENGTH = 0x00000400
4
  FLASH_PROTECTION  (rx) : ORIGIN = 0x00000400, LENGTH = 0x00000010
5
  FLASH             (rx) : ORIGIN = 0x00000410, LENGTH = 8K - 0x00000410
6
  RAM              (rwx) : ORIGIN = 0x1FFFFF00, LENGTH = 1K
7
}
1
    .flash_protect :
2
    {
3
        KEEP(*(.kinetis_flash_config_field))
4
    } > FLASH_PROTECTION

von Markus F. (mfro)


Lesenswert?

Ist das nicht ein prima use case für das (anscheinend in sich 
widersprüchliche)
1
const volatile
???

Sollte eigentlich den Compiler ("ich habe nicht vor, da was dran zu 
ändern, aber möglicherweise jemand anders") davon abhalten, die Variable 
zu entsorgen.

von A. B. (Gast)


Lesenswert?

Ralf schrieb:

> für die Programmierung eines STM32 verwende ich Atollic Truestudio, also
> GCC drunterliegend und bastle mir gerade die Option Bytes direkt ins ELF
> bzw. HEX File.

Das ist zwar an sich eine schöne Idee, es ist schon ärgerlich, dass die 
Option-Bytes immer "nebenher" laufen. Allerdings wird das nicht so 
einfach zum Ziel führen:

Die Option-Bytes werden nicht über die "normale" Flash-Programmierung 
beschrieben, sondern mittels Schreiben in die Flash-Option-**Register**.
Dabei werden die "echten" Flash-Zellen zuerst gelöscht und danach die 
"aufgebohrten" Werte (d .h. zusätzlich auch noch die invertierten Werte) 
in die Flash-Zellen geschrieben.

Das Konzept mit den Option-Bytes im Elf-File würde nur funktionieren, 
wenn der Flash-Programmer die Option-Bytes da herausfischt und sie dann 
in die entsprechenden Register schreibt. Müsste man für jeden Chip 
individuell einbauen ...

Bei der Kinetis-Familie ist das etwas schöner gelöst, hat aber auch 
wieder unschöne Seiteneffekte, da ein Erase dann gleich alle 
Option-Bytes mit löscht.

von Bauform B. (bauformb)


Lesenswert?

A. B. schrieb:
> Die Option-Bytes werden nicht über die "normale" Flash-Programmierung
> beschrieben, sondern mittels Schreiben in die Flash-Option-**Register**.

> Das Konzept mit den Option-Bytes im Elf-File würde nur funktionieren,
> wenn der Flash-Programmer die Option-Bytes da herausfischt und sie dann
> in die entsprechenden Register schreibt.

Macht der eingebaute UART-Bootloader in den STM32 nicht genau das?

von Bernd K. (prof7bit)


Lesenswert?

A. B. schrieb:
> Das Konzept mit den Option-Bytes im Elf-File würde nur funktionieren,
> wenn der Flash-Programmer die Option-Bytes da herausfischt und sie dann
> in die entsprechenden Register schreibt. Müsste man für jeden Chip
> individuell einbauen ...

Eine Alternative (falls er sie nicht wie ein Kinetis beim Reset 
automatisch aus dem Flash liest) ist es am Anfang der Main (oder im 
Startup oder wo auch immer es gut passt) den Zustand der Option-Bytes zu 
prüfen und sie ggf. dann auf die dafür vorgesehene Weise zu setzen. Dann 
muss man sein Gerät halt mindestens einmal einschalten bevor man es 
verkauft. Für den Debugbuild muss man diese Routine halt deaktivieren.

von M.K. B. (mkbit)


Lesenswert?

Ralf schrieb:
> Ob das ganze nun denn auch so die gewünschte Wirkung erzielt muss ich
> noch ausprobieren, was mich gerade interessiert ist, warum ich die
> Variablen zusätzlich mit dem Attribut 'used' belegen muss, wenn ich doch
> im Linkerscript die Sektionen schon mit KEEP markiert habe - ich bin da
> nun einfach neugierig :)

Ich habe noch nicht ganz verstanden, was du mit der Variable erreichen 
willst. Wenn der Compiler die aber wegoptimiert, dann war sie entweder 
überflüssig (also warum dann erzwingen) oder nicht richtig als volatile 
gekennzeichnet und der Compiler hat es wegoptimiert. Bei zweitem bin ich 
mir nicht sicher, ob das used reicht, wenn der Compiler dann trotzdem 
den Code zum setzen löscht.

von Bernd K. (prof7bit)


Lesenswert?

M.K. B. schrieb:

> Ich habe noch nicht ganz verstanden, was du mit der Variable erreichen
> willst.

Er will daß diese Konstante genau an der Stelle im Flash zu liegen kommt 
wo diese Werte die gewünschte Wirkung erzielen. Bei einigen Controllern, 
zum Beispiel bei den oben kurz erwähnten Kinetis-Controllern gibts 16 
Bytes im Flash bei Adresse 0x400 kurz hinter der Vektortabelle damit 
werden die Lockbits gesetzt und noch ein paar andere Optionen. Bei jeden 
Einschalten wird das als erstes gelesen und die Flash Security und noch 
ein paar andere Sachen entsprechend gesetzt. So ähnlich wie die Fuses 
beim AVR (die man dort allerdings separat setzen muss), nur liegen sie 
in dem Fall als ganz normale Bits im Programmflash und man kann dann 
direkt ins .bin reinbacken und die werden dann immer gleich in einem 
Rutsch vollautomatisch an die richtige Stelle geflasht.

Allerdings ist der Einwand des anderen Users weiter oben nicht 
unberechtigt, es mag durchaus sein daß das bei STM32 nicht ganz so 
simpel funktioniert wie beim Kinetis, STM32 sind in jeder Hinsicht 
weitaus komplizierter zu handhaben und alles ist intern deutlich 
verschwurbelter organisiert als bei den simplen Kinetis-Controllern wo 
alles ziemlich direkt und geradeaus ist, von STM32 kommt der Mythos daß 
"ARM kompliziert" sei. Wer den ersten Kontakt allerdings mit einem 
Kinetis erlebt hat kann darüber nur den Kopf schütteln, aber lassen wir 
das, ich schweife ab...

> bin ich
> mir nicht sicher, ob das used reicht,

KEEP und used reicht, das funktioniert wie dokumentiert. Auch für andere 
Zwecke ist das verwendbar, zum Beispiel eine Konfiguration des 
Bootloaders die beim Flashen des Bootloaders einmalig eingebrannt wird 
(zum Beispiel für eine I2C-Adresse oder eine Seriennummer oder 
dergleichen) und die später auch von der Anwendung gelesen werden kann 
schon beim Flashen der Bootloader.bin an eine fixe Stelle ins Flash zu 
legen.

: Bearbeitet durch User
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.