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.
eigentlich ist es egal, wie die heisst. Ich würde sie *.inc nennen, für "include"...
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
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.
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.
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?
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?
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?
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.
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
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.
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?
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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.