Forum: Compiler & IDEs [s] Dateiendung für #include


von Bauform B. (bauformb)


Lesenswert?

Hallo Spezialisten!

dies ist mal wieder ein Versuch, eine Datei mit C-Quelltext in eine 
andere zu #includen. Nachdem man das ja nicht tut, wollte ich ihr eine 
andere Dateiendung geben. *.h geht garnicht, es ist kein Header. *.c 
wäre vom Inhalt her richtig, man könnte sie auch normal übersetzen, aber 
dann landet sie irgendwo im Flash.

Es geht um eine Tabelle (tabelle.foo), die an einer festen 
physikalischen Adresse stehen muss, aber nicht am Anfang vom Flash, da 
gehört schon eine andere (header.c) hin. Die wird per 
__attribute__((section)) und Linker Script platziert. Wenn ich am Ende 
von header.c ein #include "tabelle.foo" schreibe, funktioniert es.

Alles wäre ganz normal, wenn header.c alles enthalten würde. Die Tabelle 
wird aber per Script erzeugt. header.c mit dem gleichen Script zu 
erzeugen wäre extrem uncool/unflexibel/doof.

Man könnte im Linker Script per MEMORY zwei zusätzliche Bereiche für 
header und tabelle anlegen. Das ist hässlich, weil MEMORY (bisher) 1:1 
den physikalen Speicher abbildet.

Also, wie nennt man so eine Datei? Ich frage jetzt nicht "wie macht man 
das normalerweise", eine IDE würde mir nicht helfen. 
Verbesserungsvorschläge sind natürlich willkommen, danke schon mal.

von Bernhard M. (boregard)


Lesenswert?

eigentlich ist es egal, wie die heisst.
Ich würde sie *.inc nennen, für "include"...

von S. R. (svenska)


Lesenswert?

Am Linker vorbei zu platzieren, in der Hoffnung, dass der das schon 
passend macht, ist unschön bis großflächig bescheuert. Macht man nicht.

Entweder, du machst eine neue SECTION im Linkerscript, oder du 
erweiterst die Tabelle am Flashanfang um die Daten, die direkt darauf 
folgen sollen.

Du kannst z.B. auch sowas machen:
1
char data[] = {
2
#include "data.inc"
3
}

In so einem Fall würde ich sie auch INC, DAT, FW oder sowas nennen. Je 
nachdem, was noch nicht verwendet wurde und im Kontext sinnvoll ist. 
Wobei ich INC eher aus Assemblermodulen kenne, nicht für C.

: Bearbeitet durch User
von Bauform B. (bauformb)


Lesenswert?

Bernhard M. schrieb:
> eigentlich ist es egal, wie die heisst.

make kennt viele implizite Regeln, da ist das nicht egal. Der gcc 
entscheidet auch anhand von .c bzw. .cpp was erlaubt ist. Deswegen war 
ich skeptisch.

S. R. schrieb:
> Am Linker vorbei zu platzieren, in der Hoffnung, dass der das schon
> passend macht, ist unschön bis großflächig bescheuert. Macht man nicht.

stimmt, man muss ihm schon erklären, was wohin soll
1
SECTIONS
2
{
3
  $image_header : {
4
     FILL (0xff)
5
     KEEP (*($image_header))
6
     KEEP (*($syslib_table))
7
     KEEP (*($memory_map))
8
     KEEP (*($vtab))
9
  } > flash_syslib
So funktioniert das zwar, aber wer schaut schon ins Linker Script? 
Deswegen sollte die Reihenfolge (auch) im Quelltext klar zu sehen sein. 
Aber das geht auch mit #include nicht, maßgeblich ist nur die 
Reihenfolge der input sections.

So lohnt sich das #include nicht, also keine Sonderlocke, keine 
Spezial-Endung. Die zweite Tabelle wird ganz normal übersetzt und 
gelinkt. __attribute__((section)) in den beiden Files sagt eigentlich 
deutlich genug, dass da etwas spezielles passiert.

Vielen Dank, jetzt sehe ich klarer.

von S. R. (svenska)


Lesenswert?

Bauform B. schrieb:
> make kennt viele implizite Regeln, da ist das nicht egal.

Wer die heute noch nutzt, gehört erschossen. Ernsthaft, die impliziten 
Regeln waren vielleicht vor 50 Jahren angemessen, aber schon vor 30 
Jahren bescheuert.

> Der gcc entscheidet auch anhand von .c bzw. .cpp was erlaubt ist.

Das betrifft nur das Frontend, was entsprechende Parameter weiterleitet. 
Den Präprozessor (#include) hat es nicht zu interessieren.

> So funktioniert das zwar, aber wer schaut schon ins Linker Script?

Naja, das akzeptiere ich nicht als Argument. Wenn ein Linkerscript für 
eine Embedded-Plattform beiliegt, dann sollte das auch genutzt werden. 
Wer es ersetzen will, sollte in jedem Fall reinschauen.

> Deswegen sollte die Reihenfolge (auch) im Quelltext klar zu sehen sein.

Nein. Erstens ist die Reihenfolge im Quelltext weder sichtbar noch klar 
(der Quelltext bestimmt die Reihenfolge nicht!), andererseits ist 
überflüssige Redundanz immer ein Maintenance-Problem.

> Aber das geht auch mit #include nicht, maßgeblich ist nur die
> Reihenfolge der input sections.

An deiner Stelle würde ich einfach genau eine Sonderlocke drehen. Das 
heißt, es liegt genau eine Section am Flash-Anfang und in dieser Section 
befindet sich genau eine Datenstruktur. In dieser befindet sich dann 
alles, was an festen Stellen auftauchen muss.

Das heißt, statt sowas im Linkerscript:
1
     KEEP (*($image_header))
2
     KEEP (*($syslib_table))
3
     KEEP (*($memory_map))
4
     KEEP (*($vtab))
hätte ich dann sowas im Quelltext:
1
struct {
2
    struct image_header;
3
    struct syslib_table;
4
    struct memory_map;
5
    struct vtab;
6
} __attribute__((packed,section(.header))) {
7
    .image_header = { ... },
8
    .syslib_table = { ... },
9
    .memory_map = { ... },
10
    .vtab = { ... },
11
}

> So lohnt sich das #include nicht, also keine Sonderlocke, keine
> Spezial-Endung. Die zweite Tabelle wird ganz normal übersetzt und
> gelinkt. __attribute__((section)) in den beiden Files sagt eigentlich
> deutlich genug, dass da etwas spezielles passiert.

Das heißt aber nicht, dass es zuverlässig funktioniert. Ich hab auf 
Arbeit genug mit "brittle code" zu tun - solange man ihn nicht zu scharf 
anschaut, funktioniert er prima. Aber Featureentwicklung damit ist die 
Hölle.

von Bauform B. (bauformb)


Lesenswert?

S. R. schrieb:
> An deiner Stelle würde ich einfach genau eine Sonderlocke drehen. Das
> heißt, es liegt genau eine Section am Flash-Anfang und in dieser Section
> befindet sich genau eine Datenstruktur. In dieser befindet sich dann
> alles, was an festen Stellen auftauchen muss.

Einerseits wollte ich etwas ähnliches erreichen, andererseits haben die 
4 Teil-structs überhaupt nichts miteinander zu tun. Die so eng zusammen 
zu packen gefällt mir garnicht.

> Das heißt aber nicht, dass es [die 4 sections] zuverlässig funktioniert

Was übersehe ich da? Es geht doch nur um die richtige Reihenfolge, die 
Adresse der zweiten struct ergibt sich durch die Größe der ersten, das 
passt so. Ja, bei einer Änderung der ersten struct muss viel neu 
kompiliert werden - egal. Die Zuordnung einer struct zu einer input 
section ist eindeutig und deren Reihenfolge ist nicht zufällig, sondern 
wie im Linker Script definiert.

Ich werde zur input section noch den Filenamen dazu schreiben. Ich 
denke, dann braucht es schon kriminelle Energie um das kaputt zu machen. 
Oder bringt es was, jeder input section eine eigene output section zu 
geben?

von Rolf M. (rmagnus)


Lesenswert?

Bauform B. schrieb:
> Einerseits wollte ich etwas ähnliches erreichen, andererseits haben die
> 4 Teil-structs überhaupt nichts miteinander zu tun. Die so eng zusammen
> zu packen gefällt mir garnicht.

Wenn sie nichts miteinander zu tun haben, warum müssen sie dann in genau 
definierter Reihenfolge direkt hintereinander im Speicher liegen?

von Markus F. (mfro)


Lesenswert?

S. R. schrieb:
> Wer die heute noch nutzt, gehört erschossen. Ernsthaft, die impliziten
> Regeln waren vielleicht vor 50 Jahren angemessen, aber schon vor 30
> Jahren bescheuert.

Nun ja, dann gehöre ich zum Beispiel anscheinend erschossen...

Warum soll ich selbst neu (mit wahrscheinlich neuen Fehlern) erfinden, 
was schon seit 50 Jahren stabil funktioniert? Die impliziten Regeln 
decken sicher >80% der täglich gebrauchten Anwendungsfälle ab und von 
den restlichen 20% lassen sich meist mehr als 10 durch geschicktes 
Variablensetzen abdecken.

Natürlich muss man wissen, was man macht (aber muss man das nicht 
immer?).
1
CC=arm-none-eabi-gcc AR=arm-none-eabi-ar make libarch.a\(tst.o\)

baut mir (z.B.) - nur durch Vorhandensein einer tst.c-Datei (man braucht 
noch nicht mal ein Makefile) - eine Library.

Warum soll ich die dazu benötigten Regeln ohne Not alle neu schreiben?

von A. S. (Gast)


Lesenswert?

Rolf M. schrieb:
> Wenn sie nichts miteinander zu tun haben, warum müssen sie dann in genau
> definierter Reihenfolge direkt hintereinander im Speicher liegen?

Sehe ich genauso. Es kann ja nur um SW gehen, die irgendwelche 
Informationen aus anderen Linkläufen an fester Stelle erwartet.

Entweder es kann in eine Struktur gepresst werden (weil es nur zusammen 
konsistent ist und darum auch zusammengezogen werden kann)

Oder es benötigt unabhängige, feste Adressen (weil die Informationen 
sich unabhängig ändern können, z.B. größer oder kleiner)

Oder man macht es auf die Herkömmliche Art, dass in einer 
Verwaltungsstruktur die Lage der Informationen beschrieben wird 
(Startadresse, gerne auch Größe oder Version).

In den allermeisten Fällen ist der Overhead (Struktur direkt oder über 
einen Pointer) kleiner als gedacht. Ich habe sogar schon Compiler 
gesehen, die bei direkten Adressen diese jedesmal neu ganz gelesen 
haben, während beim Pointer ein deutlich kleinerer Offset in Summe viel 
schneller war.

von Bauform B. (bauformb)


Lesenswert?

Rolf M. schrieb:
> Bauform B. schrieb:
>> Einerseits wollte ich etwas ähnliches erreichen, andererseits haben die
>> 4 Teil-structs überhaupt nichts miteinander zu tun. Die so eng zusammen
>> zu packen gefällt mir garnicht.
>
> Wenn sie nichts miteinander zu tun haben, warum müssen sie dann in genau
> definierter Reihenfolge direkt hintereinander im Speicher liegen?

A. S. schrieb:
> Es kann ja nur um SW gehen, die irgendwelche
> Informationen aus anderen Linkläufen an fester Stelle erwartet.

So ist es. "müssen" ist also übertrieben, es gibt viele Möglichkeiten, 
aber...

 * die Adressen von image_header und syslib_table werden außerhalb des 
Programms gebraucht, z.B. vom Bootloader, sie sollten also möglichst 
konstant sein; 0 ist ideal, gibt's aber nur einmal. Die Größe der 
syslib_table ändert sich oft, die der struct image_header hoffentlich 
nie mehr ;)
 * vtab ist die Cortex-M vector table, die muss aligned(512) beginnen. 
Die Größe ist vom Chip abhängig. Auch hier wäre 0 die ideale Adresse, 
sonst ist der Speicher davor schlecht nutzbar
 * memory_map nutzt genau dieses Loch, es ist nur eine große Tabelle, 
die überall stehen kann.

Die vector table auf 0 wäre nett, aber da die zwischen 8 und 628 (oder 
mehr?) Byte groß sein kann, wird es mühsam, den image_header zu finden. 
Ich käme in Versuchung, das Wort 0 (initial stack pointer) als Pointer 
auf den image_header zu missbrauchen. Dann wäre die Reihenfolge vtab, 
syslib_table, image_header und zwar lückenlos, also keine 
Flash-Verschwendung.
Wie unmoralisch wäre das?

A. S. schrieb:
> Oder man macht es auf die Herkömmliche Art, dass in einer
> Verwaltungsstruktur die Lage der Informationen beschrieben wird
> (Startadresse, gerne auch Größe oder Version).

Die struct image_header ist genau dafür gedacht, deshalb steht (stand?) 
sie am Anfang

Markus F. schrieb:
> Warum soll ich die dazu benötigten Regeln ohne Not alle neu schreiben?

Weil dann das Makefile verständlicher wird; jedenfalls für Leute, die 
die Regeln nicht so genau kennen.

A. S. schrieb:
> In den allermeisten Fällen ist der Overhead (Struktur direkt oder über
> einen Pointer) kleiner als gedacht. Ich habe sogar schon Compiler
> gesehen, die bei direkten Adressen diese jedesmal neu ganz gelesen
> haben, während beim Pointer ein deutlich kleinerer Offset in Summe viel
> schneller war.

Der gcc für Cortex-M macht das immer so; naja, ich hab' ihn noch nicht 
erwischt ;)

A. S. schrieb:
> Oder man macht es auf die Herkömmliche Art, dass in einer
> Verwaltungsstruktur die Lage der Informationen beschrieben wird
> (Startadresse, gerne auch Größe oder Version).

: Bearbeitet durch User
von S. R. (svenska)


Lesenswert?

Bauform B. schrieb:
> Einerseits wollte ich etwas ähnliches erreichen, andererseits haben die
> 4 Teil-structs überhaupt nichts miteinander zu tun. Die so eng zusammen
> zu packen gefällt mir garnicht.

Alle Teile zusammen bilden bei dir offensichtlich einen "Header". Was 
ist so schlimm daran, den an einem Stück zu halten?

>> Das heißt aber nicht, dass es [die 4 sections] zuverlässig funktioniert
>
> Was übersehe ich da? [...] Die Zuordnung einer struct zu einer input
> section ist eindeutig und deren Reihenfolge ist nicht zufällig, sondern
> wie im Linker Script definiert.

Wenn du jede Section einzeln im Linkerscript definierst, dann geht das - 
und das wäre auch der aus meiner Sicht richtige Weg.

Ich hatte nur das Gefühl, dass du nur die erste Tabelle (IVT) im 
Linkerscript definieren willst, alle anderen Teile aber einfach in der 
gleichen Datei definierst und erwartest, dass der Linker die dann direkt 
dahinter legt. Das wäre unzuverlässig.

Was hindert dich eigentlich daran, den Headern einfach eine feste Größe 
(mit ein bisschen Aufrunden) und eine Versionsnummer zu geben? Im 
schlimmsten Fall klebst du in 5 Jahren ein "void * extension_fields" 
dazu.

Markus F. schrieb:
> Warum soll ich selbst neu (mit wahrscheinlich neuen Fehlern) erfinden,
> was schon seit 50 Jahren stabil funktioniert?

Weil es eben nicht schon seit 50 Jahren stabil funktioniert, weil du 
damit Wissen voraussetzt, was nicht mehr als gegeben angesehen werden 
kann, weil die genaue Funktionsweise im Sourcecode von "make" steht 
statt im Makefile, weil die Existenz von seltsamen Dateien dein System 
verwirren kann, weil du damit verhinderst, dass "make" selbst 
weiterentwickelt werden kann.

Gut, der letzte Punkt löst sich ja zunehmend von selbst. Gibt sicherlich 
noch mehr Gründe.

> Warum soll ich die dazu benötigten Regeln ohne Not alle neu schreiben?

Gute Frage. Du könntest ja mal schauen, welche impliziten Regeln dein 
Make so durchgeht, bis es auf deine Wunschregeln trifft. Und die drei 
Regeln, die du wirklich brauchst, stören nicht.

Dann fällt dir vielleicht auf, dass Yacc und Lex nicht mehr weit 
verbreitet sind, und bison nicht unbedingt so benutzt wird wie 1979.

Oder dass C-Programme heutzutage häufig aus mehr als einer C-Datei 
bestehen können und es sowas wie Headerfiles gibt, von denen diese 
C-Dateien abhängen.

Oder dass SCCS als Versionierungssystem nicht mehr so ganz Stand der 
Technik ist. Die stat-Aufrufe für SCCS deswegen waren übrigens ein 
wesentlicher Grund, warum Android von "make" weg ist - es macht keinen 
Spaß, wenn "make" gute 10 Minuten braucht, um festzustellen, dass nichts 
zu tun ist.

Die impliziten Regeln waren vor über 40 Jahren angemessen, weil man sich 
ein paar Bytes in den Makefiles sparen wollte. Vor 20 Jahren wurde das 
irrelevant, jetzt sind sie mehr Problem als Lösung.

von Markus F. (mfro)


Lesenswert?

Für die, die's vergessen haben:

make -p -f /dev/null

Im übrigen können implizite Regeln - wenn's denn sein muss - 
überschrieben werden. Daß andererseits make von Header-Abhängigkeiten 
nichts weiß (bzw. gar nicht wissen kann), hat mit dem Vorhandensein 
von impliziten Regeln nichts zu tun. Und warum implizite Regeln die 
Weiterentwicklung von make verhindern sollen, will mir nicht so recht 
einleuchten.

In einem hast Du recht: sccs verwendet kein Schwein mehr. So what?

von S. R. (svenska)


Lesenswert?

Markus F. schrieb:
> Daß andererseits make von Header-Abhängigkeiten nichts weiß
> (bzw. gar nicht wissen kann), hat mit dem Vorhandensein
> von impliziten Regeln nichts zu tun.

Nun, in den letzten 40 Jahren haben Compiler die Möglichkeit bekommen, 
entsprechende Informationen nebenbei zu generieren. Dann weiß make auch 
von Header-Abhängigkeiten, aber...

Markus F. schrieb:
> Und warum implizite Regeln die Weiterentwicklung von make
> verhindern sollen, will mir nicht so recht einleuchten.

...weil es Leute wie dich gibt, die sich auf das Verhalten der 
impliziten Regeln verlassen, kann man diese Regeln nicht ändern. 
Kompatiblität mit der Steinzeit und so.

Und damit hast du recht: Make kann von Header-Abhängigkeiten tatsächlich 
nichts wissen. Das liegt aber aber an den bescheuerten impliziten 
Regeln, nicht an make selbst.

Markus F. schrieb:
> In einem hast Du recht: sccs verwendet kein Schwein mehr. So what?

Bei kleineren Projekten ist das sicherlich egal, solange niemand auf den 
Gedanken kommt, eine "main.c,v" anzulegen.

Bei größeren Projekten ist das ein Performance-Problem.

PS: Ich habe übrigens RCS vergessen.

Markus F. schrieb:
> Im übrigen können implizite Regeln - wenn's denn sein muss -
> überschrieben werden.

Was effektiv nichts anderes bedeutet, dass ich für eine saubere 
Make-Umgebung (ohne Aufrufparameter) erstmal ein halbes Kilo antike 
Regeln wegdefinieren muss, in der Hoffnung, alle erwischt zu haben.

Klingt intelligent.

Ich fands auch mal cool, dass ein "make blub" automagisch aus make.y ein 
Binary erzeugen kann (d.h. yacc, mv, cc und rm aufruft und implizit eine 
vorhandene y.tab.c überschreibt).

Irgendwann habe ich aber gelernt, dass ich Magie nicht besonders mag. 
Vor allem dann nicht, wenn ich sie debuggen muss.

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

Ich zähle mich nicht zu denen, die immer recht haben müssen, deswegen 
streite ich hier nicht weiter rum.

Nur soviel: die impliziten Regeln, die make mitbringt sind für mich 
nützlich. Darum verwende ich sie, wo's passt. Dort, wo's nicht passt, 
lass' ich's bleiben.

Damit nehme ich keinem was weg - die eingebauten Regeln sind 
Suffix-Regeln, die benutzt sowieso keiner mehr. Du kannst jederzeit 
(d)einen Satz an Pattern-Regeln erzeugen, der sich daran nicht stört.

Make kann naturgemäss nichts von include-Abhängigkeiten wissen. Das kann 
nur der Compiler. gcc kann aber eine Abhängigkeits-Datei erzeugen, die 
im Makefile includiert werden kann. Wo ist das Problem?

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.