Forum: Compiler & IDEs avrgcc: verteilte Array-Initialisierung


von Moriz (untertaucher)


Lesenswert?

Folgendes Problem:
Ich habe eine Sammlung Quellmodule, die per #define gesteuert Funktionen 
ein- oder ausschließen.

Namen und Adressen dieser Funktionen sollen in eine Namenstabelle 
eingetragen werden, wenn die jeweilige Funktion im Objektcode enthalten 
ist. Die Tabelle soll im Flasspeicher angelegt werden. Das soll 
automatisch zur Generierungszeit passieren.

Die Tabelle wird zur Laufzeit von einer Routine durchsucht, die die 
Funktionsadressen in Namensstrings umsetzt. Es muss also auch die 
variable Länge der Tabelle zur Laufzeit feststellbar sein.

Wir bekommt man das hin?

von Oliver S. (oliverso)


Lesenswert?

Mit der Script-/Programmiersprache deines geringsten Misstrauens.

Oliver

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Alternativ: überall da, wo via #define irgendwas hinzu kommt, kommt auch 
gleich der Tabelleneintrag mit dazu (bspw. in Form einer eigenen memory 
section).

Das spart es, den ganzen Salat via Postprocessing nachzuverarbeiten, was 
ja am Ende auf zweimaliges Linken hinaus läuft (einmal, um zu wissen, 
was alles "an Bord" ist, das zweite Mal, um die mit diesem Wissen 
gebaute Tabelle dazu zu linken).

von Moriz (untertaucher)


Lesenswert?

Jörg W. schrieb:
> Alternativ: überall da, wo via #define irgendwas hinzu kommt, kommt auch
> gleich der Tabelleneintrag mit dazu (bspw. in Form einer eigenen memory
> section).

Genau sowas schwebt mir vor. Wie fange ich das an, ohne zusätzlich ein 
Linkerscript bauen zu müssen??

Beitrag #7849046 wurde vom Autor gelöscht.
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Moriz schrieb:
> Wie fange ich das an, ohne zusätzlich ein Linkerscript bauen zu müssen??

Man kann halt nicht alles haben. Ein eigenes Linkerscript brauchst du 
dafür dann schon.

Ist aber m.E. immer noch einfacher als irgendein Postprocessing.

von Harry L. (mysth)


Lesenswert?

Ich würde das über #defines machen, und mir den ganzen Tabellen-Zirkus 
sparen.
Der Linker macht das ja für dich.

Wenn du tatsächlich unterschiedliche Memory-Regions brauchst -was bei 
den 8bit AVR eher die Ausnahme ist- kannst du ja einfach alle denkbaren 
Memory-Regions im Linkerscript eintragen, und via #define kontrollieren, 
welche tatsächlich genutzt werden.

Ungenutzte Zuweisungen im Linkerscript, tun ja nicht weh.

Ist zwar kein AVR, aber in diesem Projekt kannst du sehen, wie man in 
einer config.h mit nem Haufen #define kontrolliert, was da gerade gebaut 
wird.:

Beitrag "Re: [STM32/HAL] simples U(S)ART-Library"

: Bearbeitet durch User
von Moriz (untertaucher)


Lesenswert?

Harry L. schrieb:
> Ich würde das über #defines machen, und mir den ganzen Tabellen-Zirkus
> sparen.
> Der Linker macht das ja für dich.

Mir scheint, ich habe mich nicht klar genug ausgedrückt…

Ich habe einen Scheduler als Quelltextbibliothek.

Für die Task-Tabelle des Schedulers gibt es eine Dumpfunktion, die im 
Moment eine Adresse-nach-Namensumsetzungsroutine in der Anwendung 
aufruft. Dafür muss eine Tabelle gepflegt werden, die den Task-Adressen 
den Klarnamen zuordnet. Die Pflege der Tabelle will ich umgehen, indem 
ich einfach in jeder Task-Routine einen Macro aufrufe, der den 
zugehörigen Umsaetzungstabelleneintrag generiert.

Die Tabelleneinträge müssen hintereinander in einer Flash-Section landen 
– das ist Linkers Job.

Damit die Suchroutine weiß, wo die Tabelle endet, muss hinter dieser 
Section eine weitere leere liegen, die die Adresse hinter der Tabelle 
liefert.


Ich habe etwas herum experimentiert: der Compiler nimmt Deklarationen 
wie diese an:
1
#define N(name)         { name, FSTR(#name) }
2
static const Name __flash1 NameTable[] = {
3
  N(powerBankRefreshed),
4
  N(powerBankRefresher),
5
};
6
#undef N
wobei __flash1 der der allgemeinen Form __flashN entspricht.
Wenn ich dahinter einen leeren Array in __flash2 anlege, könnte das 
funktionieren, wenn die Sections hintereinander liegen.

Nur leider finde ich im .map-File dazu überhaupt nichts, weder 
Sectionnamen, noch Variablennamen. (Dieser .map-File scheint mir 
überhaupt nicht zu allzuviel nutze zu sein…)

: Bearbeitet durch User
von Bruno V. (bruno_v)


Lesenswert?

wieso eigenes Linker-Script? Du brauchst doch nur eine weitere Memory 
section für Deine Tabelle. Und der Linker liefert Dir nachher Adresse 
und Länge der Section. Also nur ein paar Zeilen im Script.

Wieso brauchst Du überhaupt getrennte Quellen? Wenn Du in ein Sammelfile 
alle Header einbinden kannst, kann das der Precompiler für Dich 
erledigen.

Wenn es nur eine Handvoll Teilprojekte sind, kannst du auch eine Liste 
von (Teilprojekt-)Tabellen anlegen.

Wenn es Dir nur um das Listen-/Tabellenende geht, mache sie 
"Nullterminiert" (natürlich nicht wenn der Linker die anordnet).

von Daniel A. (daniel-a)


Lesenswert?


von Moriz (untertaucher)


Lesenswert?

Bruno V. schrieb:
> wieso eigenes Linker-Script? Du brauchst doch nur eine weitere Memory
> section für Deine Tabelle. Und der Linker liefert Dir nachher Adresse
> und Länge der Section. Also nur ein paar Zeilen im Script.

In welchem Skript?

> Wieso brauchst Du überhaupt getrennte Quellen? Wenn Du in ein Sammelfile
> alle Header einbinden kannst, kann das der Precompiler für Dich
> erledigen.

Ganz simpel: ich will den Scheduler einfach in ein Projekt aufnehmen, 
also scheduler.c und scheduler.h

In der Anwendung sind dann die Task-Funktionen verstreut. Wenn jede 
Taskfunktion es schafft, den zugehörigen Umsetzungstabellen-Eintrag in 
eine spezielle Section zu bugsieren und der Name des Section-Anfangs und 
-Endes dem Scheduler bekannt ist, dann muss man sich um die Tabelle 
nicht mehr weiter kümmern.

> Wenn es nur eine Handvoll Teilprojekte sind, kannst du auch eine Liste
> von (Teilprojekt-)Tabellen anlegen.

Nein, solches Zeug will ich tunlichst vermeiden – das wird schnell 
furchtbar unübersichtlich und ist bald unpflegbar…

: Bearbeitet durch User
von Moriz (untertaucher)


Lesenswert?


von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Moriz schrieb:
> static const Name __flash1 NameTable[] =

Named Address Spaces?  Was haben die damit zu tun?

https://gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html#AVR-Named-Address-Spaces-1

von Moriz (untertaucher)


Lesenswert?

Gibt es irgendwo ein Standard-Linkerskript für avrgcc? Oder ist 
Section-Skelett irgendwo hart verdrahtet?

von Moriz (untertaucher)


Lesenswert?

Unter /usr/lib/avr/lib/ldscripts liegt eine große Sammlung von 
Linker-Scripten – nur welcher wird für den 328P benutzt?

von Moriz (untertaucher)


Lesenswert?

Hier: Beitrag "Re: AVR GCC .text is full - ld scripts finden" steht, es sei 
avr2.

Also vermutlich /usr/lib/avr/lib/ldscripts/arv2.x

Wie findet man das heraus?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Moriz schrieb:
> Nur leider finde ich im .map-File dazu überhaupt nichts, weder
> Sectionnamen, noch Variablennamen. (Dieser .map-File scheint mir
> überhaupt nicht zu allzuviel nutze zu sein…)

Das Map-File ist nützlich zum Debuggen, wenn man Probleme mit dem Linker 
hat. Das ist ganz sicher nicht dafür da, irgendwie maschinenlesbar zu 
sein.

Die Symboltabelle wäre vielleicht interessanter für dich.

Moriz schrieb:
> Wie findet man das heraus?

Indem du den GCC mit -v startest, dann sagt er dir, welche Backends er 
so aufruft.

von Peter D. (peda)


Lesenswert?

Moriz schrieb:
> Ich habe eine Sammlung Quellmodule, die per #define gesteuert Funktionen
> ein- oder ausschließen.

> Die Tabelle wird zur Laufzeit von einer Routine durchsucht, die die
> Funktionsadressen in Namensstrings umsetzt.

Wozu diese Umstände?
Ein #define läßt sich doch prima schon zur Compilezeit auswerten.
1
#define MODULE_CMD_GETCALIB       "GetCalib"           // Diagnostics only. Returns the calbiration signature
2
#define MODULE_CMD_READ_ADC       "ReadAdc"            // Diagnostics only, doesnt work for muxed ADC.
3
#define MODULE_CMD_GETSTACK       "GetStack"           // Diagnostics only. Returns stack usage.
4
5
#define CMD_SIZE        20              // for sscanf format, decimal number only !
6
7
typedef char cmd_str_t[CMD_SIZE+1];
8
9
typedef void (*func)(void);
10
11
typedef struct
12
{
13
  cmd_str_t cmd;
14
  cmd_str_t cmd_short;
15
  func func;
16
} command_t;
17
18
command_t const cmd_table[] PROGMEM =
19
{
20
#ifdef MODULE_CMD_GETSTACK
21
  {MODULE_CMD_GETSTACK "\0",      "gst", cmd_get_stack},
22
#endif
23
#ifdef MODULE_CMD_GETCALIB
24
  {MODULE_CMD_GETCALIB "\0",      "gc",  cmd_get_calib_signature},
25
#endif
26
#ifdef MODULE_CMD_READ_ADC
27
  {MODULE_CMD_READ_ADC "\0",      "ra",  cmd_read_adc},
28
#endif
29
  {"", "", 0},
30
};

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Moriz schrieb:
> Nein, solches Zeug will ich tunlichst vermeiden – das wird schnell
> furchtbar unübersichtlich und ist bald unpflegbar…

Ich hab nen Kollegen, der ist Fan von überall im Code verteilten 
sscanf() zum Parsen. Mit dem Erfolg, daß gerne mal Schreibfehler 
passieren oder sogar Namen doppelt vergeben werden bzw. fehlen. Für 
einen anderen Leser ist es sehr unübersichtlich, wenn die Strings und 
Aufrufe überall wahllos verteilt sind. Außerdem erzeugt jedes weitere 
sscanf() auch massig Codeoverhead.
In einer Tabelle kann man viel besser auf einheitliche Syntaxregeln 
achten und Fehler erkennen.

von Harald K. (kirnbichler)


Lesenswert?

Peter D. schrieb:
> Außerdem erzeugt jedes weitere
> sscanf() auch massig Codeoverhead.

Wieso das? sscanf wird, wie jede Library-Funktion, einmal zum Code 
dazugelinkt. Ob man es an drei, zehn oder fünfzig Stellen im Code 
verwendet, ändert an dessen Größe nichts. Ist doch keine inline-Funktion 
...

von Peter D. (peda)


Lesenswert?

Harald K. schrieb:
> Ob man es an drei, zehn oder fünfzig Stellen im Code
> verwendet, ändert an dessen Größe nichts.

sscanf() Aufrufe bekommen oft sehr viele Parameter übergeben, die auf 
dem Stack abgelegt werden müssen. Man kann sich mal im Listing 
anschauen, was für ein Bohei vor jedem Aufruf veranstaltet wird.
1
  cmd_data.args = sscanf_P(buf, PSTR(CMD_FMT CMD_FMT "%g"), 
2
                           cmd_data.cmd, cmd_data.par, &cmd_data.val);
3
    4890:  2e e6         ldi  r18, 0x6E  ; 110
4
    4892:  3c e0         ldi  r19, 0x0C  ; 12
5
    4894:  3f 93         push  r19
6
    4896:  2f 93         push  r18
7
    4898:  c9 e5         ldi  r28, 0x59  ; 89
8
    489a:  dc e0         ldi  r29, 0x0C  ; 12
9
    489c:  df 93         push  r29
10
    489e:  cf 93         push  r28
11
    48a0:  24 e4         ldi  r18, 0x44  ; 68
12
    48a2:  3c e0         ldi  r19, 0x0C  ; 12
13
    48a4:  3f 93         push  r19
14
    48a6:  2f 93         push  r18
15
    48a8:  23 e0         ldi  r18, 0x03  ; 3
16
    48aa:  3e e0         ldi  r19, 0x0E  ; 14
17
    48ac:  3f 93         push  r19
18
    48ae:  2f 93         push  r18
19
    48b0:  9f 93         push  r25
20
    48b2:  8f 93         push  r24
21
    48b4:  0e 94 1f 45   call  0x8a3e  ; 0x8a3e <sscanf_P>

: Bearbeitet durch User
von Moriz (untertaucher)


Lesenswert?

Peter D. schrieb:
> Mit dem Erfolg, daß gerne mal Schreibfehler
> passieren oder sogar Namen doppelt vergeben werden bzw. fehlen.

Im Prinzip hast du Recht, aber hier liegt ein spezieller Fall vor, für 
den das eben gerade nicht gilt, während es für die global zu pflegende 
Tabelle zutrifft.

In schedule.h gibt es bereits einen Macro SCHEDULER_TASK(name), der 
eigentlich nur syntaktischer Zucker ist und die Lesbarkeit des Codes 
verbessert:
1
#define SCHEDULER_TASK(name)   void name()

Wenn es gelingt, diesen Macro so umzubauen, dass er gleich den 
entsprechenden Namenstabelleneintrag generiert, dann braucht man keine 
globale Tabelle mehr, der Code bleibt lesbar und Namensänderungen 
schlagen sofort auf die Tabelle durch.

Zusätzlich kann man auch zwei Versionen davon bauen, eine mit 
eingebautem Tabellen-Dump und einen ohne und die ganze Chose über ein in 
der Kommandozeile des Compilers angebbares Symbol steuern.

Dann wäre das alles völlig automatisiert und trotzdem transparent.

Die Tabelleneintragsgenerierung kann man mit dem Trick mit der 
verzeigerten Liste aus 
https://www.mikrocontroller.net/articles/C_Linkerhacks_%2B_Modularisierung 
dezentralisieren. (Das mit den Sections funktioniert beim AVR nach dem 
Artikel nicht, weil die Reihenfolge der Sections nicht definiert ist.)

von Moriz (untertaucher)


Lesenswert?

Jörg W. schrieb:
> Die Symboltabelle wäre vielleicht interessanter für dich.

Danach habe ich gesucht… Wie krieg ich die?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Moriz schrieb:
> Jörg W. schrieb:
>> Die Symboltabelle wäre vielleicht interessanter für dich.
>
> Danach habe ich gesucht… Wie krieg ich die?

avr-nm (mit verschiedenen Optionen)

Oder programmatisch mit einer ELF-Bibliothek.

von Moriz (untertaucher)


Lesenswert?

Wieso gibt es eigentlich nur leere man-Eintrage für das avr-* Zeug?

von Peter D. (peda)


Lesenswert?

Moriz schrieb:
> Wenn es gelingt, diesen Macro so umzubauen, dass er gleich den
> entsprechenden Namenstabelleneintrag generiert

Das Problem an C-Macros ist, daß es nur eine Textersetzung durchführt.
Ein Macro kann also nicht weitere Macros erzeugen und es sind auch keine 
nested Macros möglich.
Sowas kann nur der AVR Assembler.

von Daniel A. (daniel-a)


Lesenswert?

Moriz schrieb:
> Das mit den Sections funktioniert beim AVR nach dem Artikel nicht, weil
> die Reihenfolge der Sections nicht definiert ist

Das eigentliche Problem beim AVR ist das linker script. Die custom 
sections kommen nicht in .text oder .data rein, die Objekte landen dann 
nicht auf dem AVR.
Und wenn man seine Sections z.B. .data.bla nennt, dann sollte es zwar in 
.data rein kommen, aber dann generiert der Linker die _start und 
_stop symbole nicht mehr, die gibts nur wenn der Sektionsname ein 
gültiger c identifier wäre, und "." kann man da halt nicht haben. Eine 
echt blöde Situation...

von Moriz (untertaucher)


Lesenswert?

Peter D. schrieb:
> Ein Macro kann also nicht weitere Macros erzeugen und es sind auch keine
> nested Macros möglich.

Muss es auch nicht. Sieh dir den Artikel an, den Daniel A. hier 
Beitrag "Re: avrgcc: verteilte Array-Initialisierung" verlinkt hat…

Ein Problem könnte es geben, wenn in zwei Quellmodulewn der Macro 
zufällig auf gleichen Zeilennummern aufgerufen wird. Das könnte man aber 
leicht beheben, z.B. mit einer für jeden Quellfile individuellen 
Modul-ID – das habe ich in meinem Projekt auch bereits eingebaut, um 
fatale Fehler mit einer LED blinkend zu kommunizieren.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Moriz schrieb:
> Wieso gibt es eigentlich nur leere man-Eintrage für das avr-* Zeug?

Das musst du denjenigen fragen, der das bei dir zusammen gepackt hat. 
Ich habe sowohl auf einem Ubuntu als auch auf dem FreeBSD dort reale 
Einträge.

Aber: was die Symboltabelle anbelangt, da kannst du auch einfach "man 
nm" machen.

von Moriz (untertaucher)


Lesenswert?

Jörg W. schrieb:
> Aber: was die Symboltabelle anbelangt, da kannst du auch einfach "man
> nm" machen.

Unter Mint 21 kommt da ein leerer man-Eintag.

von Moriz (untertaucher)


Lesenswert?

Peter D. schrieb:
> Das Problem an C-Macros ist, daß es nur eine Textersetzung durchführt.

Das wird in dem zitierten Artikel mit dem alten CPP CONCAT-Trick 
umgangen. Womöglich kannst du da noch was lernen ;-)

Neue Macros in einem Macro erzeugen geht nicht, aber das braucht man 
hier auch nicht. Für solche Schweinereien muss dann der olle m4 her… 
Dessen Syntax ist allerdings gewöhnungsbedürftig.

: Bearbeitet durch User
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Moriz schrieb:
> Jörg W. schrieb:
>> Aber: was die Symboltabelle anbelangt, da kannst du auch einfach "man
>> nm" machen.
>
> Unter Mint 21 kommt da ein leerer man-Eintag.

Da musst du dich wohl bei den Mint-lern bedanken gehen.

Hast du mal
1
env LANG=C man ld
probiert?

von Moriz (untertaucher)


Lesenswert?

Jörg W. schrieb:
> Hast du malenv LANG=C man ld
> probiert?

Damit geht es, Teufel… Aber es geht auch ohne Präfix…
Für das avr-*-Zeug hilft das nichts.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Moriz schrieb:
> Hier: Beitrag "Re: AVR GCC .text is full - ld scripts finden" steht, es sei
> avr2.
>
> Also vermutlich /usr/lib/avr/lib/ldscripts/arv2.x

avr2 auf gar keinen Fall.

Das ld script trägt den Namen der Emulation, also was im Linkeraufruf 
auf -m folgt:
1
(echo "" | avr-gcc -xc - -r -mmcu=atmega328p -Wl,-v 2>&1) | grep -o -- ' -mavr[a-z_0-9]*\b' | sed -e 's: -m::'
2
avr5

Das verwendete ld Script ist also avr5.x.

Erklärung: Es wird ein leeres Programm mit -Wl,-v gelinkt.  Der Linker 
zeigt mit -v wie er aufgerufen wurde:
1
collect2 version $version
2
$prefix/bin/../lib/gcc/avr/$version/../../../../avr/bin/ld ... -mavr5 ...
3
GNU ld (GNU Binutils) $version
grep und sed filtern dann die Emulation raus.

Zur Referenz sind die ld Scripte installiert unter 
$prefix/avr/lib/ldscript.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Moriz schrieb:
> Jörg W. schrieb:
>> Hast du malenv LANG=C man ld
>> probiert?
>
> Damit geht es, Teufel…

Normalerweise sollte das man-Kommando natürlich auf die englische 
Version zurück fallen, wenn keine in der Regionalsprache installiert 
ist. Da scheint also generell was bei dem System kaputt zu sein.

> Für das avr-*-Zeug hilft das nichts.

Das wiederum dürfte an demjenigen liegen, der die avr-* Pakete gebaut 
hat. Aber von avr-libc abgesehen (die via "avr-man" bei Bedarf einen 
Sack voller eigener man pages installieren kann), sind die man pages von 
GCC und binutils natürlich eh die gleichen wie die ohne "avr-".

: Bearbeitet durch Moderator
von Moriz (untertaucher)


Lesenswert?

Peter D. schrieb:
> sscanf() Aufrufe bekommen oft sehr viele Parameter übergeben, die auf
> dem Stack abgelegt werden müssen. Man kann sich mal im Listing
> anschauen, was für ein Bohei vor jedem Aufruf veranstaltet wird.

Mit ziemlicher Sicherheit gibts noch viel mehr Bohei, wenn man das, was 
ein sscanf-Aufruf macht, zu Fuß in einzelne Funktionsaufrufe auflöst.

Aber wegen einem einzigen sscanf den ganzen Modul zu laden, sollte man 
sich im Zweifelsfall schon gut überlegen.

: Bearbeitet durch User
von Moriz (untertaucher)


Lesenswert?

Jetzt habe ich mal so ein Macro gestrickt:
1
#ifndef DUMP_TASK_TABLE
2
    #define SCHEDULER_TASK(n)   static void n()
3
#else
4
    typedef struct Schd_NameTableEntry {
5
        const void                          *taskAddr;
6
        const char const __flash            *name;
7
        struct Schd_NameTableEntry          *next;
8
    } Schd_NameTableEntry;
9
10
    #ifndef CONCAT
11
        #define CONCAT_EVAL(a, b)    a ## b
12
        #define CONCAT(a, b)         CONCAT_EVAL(a, b)
13
    #endif // CONCAT
14
15
    #define SCHEDULER_TASK(n) \
16
       static void n(); \
17
       static Schd_NameTableEntry CONCAT( n, _Schd_NameTableEntry) = { \
18
          (n), FSTR(#n), NULL}; \
19
       \
20
       extern Schd_NameTableEntry *schd_NameTable; \
21
       \
22
       static void CONCAT( n, _Schd_NameTableEntry_ctor )() \
23
          __attribute__((constructor)); \
24
       static void CONCAT( n, _Schd_NameTableEntry_ctor )(){ \
25
          CONCAT( n, _Schd_NameTableEntry ).next = schd_NameTable; \
26
          schd_NameTable = &CONCAT( n, _Schd_NameTableEntry ); \
27
       } \
28
       \
29
       static void n()
30
#endif // DUMP_TASK_TABLE

Für den folgenden Aufruf
1
SCHEDULER_TASK(initReadSensors) {
2
   // Code der Taskroutine
3
}

generiert der C-Preprozessor diesen (handformatierten) Code:
1
static void initReadSensors();
2
3
static Schd_NameTableEntry initReadSensors_Schd_NameTableEntry = {
4
    (initReadSensors),
5
    ((const __flash char[]) { "initReadSensors" } ),
6
    ((void *)0)
7
};
8
9
extern Schd_NameTableEntry *schd_NameTable;
10
11
static void initReadSensors_Schd_NameTableEntry_ctor() __attribute__((constructor));
12
static void initReadSensors_Schd_NameTableEntry_ctor(){
13
    initReadSensors_Schd_NameTableEntry.next = schd_NameTable;
14
    schd_NameTable = &initReadSensors_Schd_NameTableEntry;
15
}
16
17
static void initReadSensors() {
18
  // Code der Taskroutine
19
}

Das sieht schon mal ganz gut aus… nur finde ich die Funktion 
initReadSensors_Schd_NameTableEntry_ctor() weder in der Symboltabelle, 
noch im .map-File. Irgend was scheint da mit der Behandlung von 
Konstruktoren nicht zu stimmen.

von Moriz (untertaucher)


Lesenswert?

Ursache für die fehlende initReadSensors_Schd_NameTableEntry_ctor() ist 
Linker-Optimierung. Wenn ich die Option -flto entferne, sind sie im 
Objekt enthalten.

Gibt es eine Möglichkeit, dem Linker mitzuteilen, dass er eine Funktion 
von der Optimierung ausnehmen soll?

von Daniel A. (daniel-a)


Lesenswert?

Versuch es mal mit
1
__attribute__((constructor,used))

von Moriz (untertaucher)


Lesenswert?

Daniel A. schrieb:
> Versuch es mal mit
>
1
__attribute__((constructor,used))

Damit funktioniert es.

Der gesamte Definitionsblock sieht dann so aus:
1
#ifndef DUMP_TASK_TABLE
2
    #define SCHEDULER_TASK(n)   static void n()
3
#else
4
    typedef struct Schd_NameTableEntry {
5
        const void                          *taskAddr;
6
        const char const __flash            *name;
7
        struct Schd_NameTableEntry          *next;
8
    } Schd_NameTableEntry;
9
10
    #ifndef CONCAT
11
        #define CONCAT_EVAL(a, b)    a ## b
12
        #define CONCAT(a, b)         CONCAT_EVAL(a, b)
13
    #endif // CONCAT
14
15
    #define SCHEDULER_TASK(n) \
16
       static void n(); \
17
       static Schd_NameTableEntry CONCAT( n, _Schd_NameTableEntry) = { \
18
          (n), FSTR(#n), NULL }; \
19
       \
20
       extern Schd_NameTableEntry *schd_NameTable; \
21
       \
22
       static void CONCAT( n, _Schd_NameTableEntry_ctor )() \
23
          __attribute__((constructor,used)); \
24
       static void CONCAT( n, _Schd_NameTableEntry_ctor )(){ \
25
          CONCAT( n, _Schd_NameTableEntry ).next = schd_NameTable; \
26
          schd_NameTable = &CONCAT( n, _Schd_NameTableEntry ); \
27
       } \
28
       \
29
       static void n()
30
#endif // DUMP_TASK_TABLE

Die Funktionsdefinition für eine Task:
1
SCHEDULER_TASK(machWas) {
2
   // …
3
}

Je nachdem, ob das Symbol DUMP_TASK_TABLE definiert ist, oder nicht, 
wird ein Namenstabelleneintrag generiert, oder nicht.

: 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.