Hallo,
ich habe eine Verständnis- bzw. Implementierungsfrage zum Adressraum
eines gcc-Programms. Als uC nutze ich einen OpenRISC in einem FPGA,
allerdings sollte die Frage erstmal unabhängig vom target sein.
So wie ich es verstehe, steht grob gesagt im Adressraum (von Adresse 0
bis max) .text .data .bss .heap .stack.
Das was in .text steht ist mein Befehlsspeicher. Das was im Rest steht,
sind Daten, richtig?
Wenn ich nun einen Prozessor mit Harvard-Architektur habe (z.B.
bestimmte OpenRISC Implementierungen), d.h. getrennte Busse für Befehle
und Daten, wird meines Verständnisses nach alles in der .text über den
Befehlsbus adressiert und alles andere über den Datenbus.
Was ich gern (im FPGA) bauen möchte, sind zwei getrennte Speicher für
Befehle und Daten. Der Befehlsspeicher soll eine fixe Größe von z.B
16KByte haben, d.h. .text ist 16Kbyte groß.
Kann ich einfach während des Bauens dem gcc die Option
1
-Wl,--section-start=.data=0x00004000
mitgeben, um einen (read only) Befehlsspeicher von 0x00000000 bis
0x00003FFF und einen Datenspeicher von 0x00004000 bis max zu haben (d.h.
.text, .bss .heap .stack landet ab 0x00004000 im Datenspeicher)? Kann
ich dann einfach die per objdump nach out.bin gewandelte out.elf als
Initialbelegung in den Befehlsspeicher schreiben?
Oder ist das zu einfach gedacht?
Wenn ich einen gcc-size auf die erstellte out.elf mache, gibt es ja
Größen für .text, .data und .bss. Liegen in der .elf irgendwelche
.data/.bss Sachen, die doch schon (zur Bauzeit des FPGA mittels
Initialisierung) in den Datenspeicher müssen oder werden .data und .bss
mittels startup-code zur Laufzeit geschrieben und der Speicherbereich
für .data und .bss muss nur leer im Datenspeicher verfügbar sein?
Danke für jede Hilfe.
Vh
VHDL hotline schrieb im Beitrag #4095340:
> Kann ich einfach während des Bauens dem gcc die> Option-Wl,--section-start=.data=0x00004000> mitgeben, um einen (read only) Befehlsspeicher von 0x00000000 bis> 0x00003FFF und einen Datenspeicher von 0x00004000 bis max zu haben (d.h.> .text, .bss .heap .stack landet ab 0x00004000 im Datenspeicher)?
Müsste gehen. Als Option an der Kommandozeile hab ichs noch nicht
probiert, aber wenn Du ein Linkerscript hast und dort die Startadressen
entsprechend definierst dann geht das so auf jeden Fall. An der
Kommandozeile wahrscheinlich auch, warum auch nicht?
> Kann> ich dann einfach die per objdump nach out.bin gewandelte out.elf als> Initialbelegung in den Befehlsspeicher schreiben?
Ja.
VHDL hotline schrieb im Beitrag #4095340:
> Liegen in der .elf irgendwelche> .data/.bss Sachen,
.data sollte ans Ende des .text kommen, da liegen die Werte mit denen
globale Variablen initialisiert werden (macht der Starup-code [den Du
schreibst], der soll das dann vom Programmspeicher (von der Stelle an
der .data anfängt) in den Datenspeicher (an die Stelle an der .bss
anfängt) kopieren. Das wird alles im Linkerscript so definiert (z.B. daß
.data am ende von .text kommen soll und alles zusammen dann in den
Programmspeicher verfrachtet werden soll), wie man ihm das allerdings
über eine Kommandozeilenoption beibringt (wenn er es nicht schon von
sich aus macht und kein Linkerscript da ist) weiß ich allerdings nicht.
.bss landet im RAM (steht auch im Linkerscript)
Bei einer Harvard-Architektur spricht nichts dagegen, sowohl Code als
auch Daten bei 0 beginnen zu lassen (ist bei all den mir bekannten
Harvard-CPUs der Fall). In welcher Reihenfolge und an welchen Offsets
Code und Daten in einem bin- bzw. hex-File liegen, hat damit überhaupt
nichts zu tun.
VHDL hotline schrieb im Beitrag #4095340:
> Wenn ich einen gcc-size auf die erstellte out.elf mache, gibt es ja> Größen für .text, .data und .bss. Liegen in der .elf irgendwelche> .data/.bss Sachen, die doch schon (zur Bauzeit des FPGA mittels> Initialisierung) in den Datenspeicher müssen oder werden .data und .bss> mittels startup-code zur Laufzeit geschrieben und der Speicherbereich> für .data und .bss muss nur leer im Datenspeicher verfügbar sein?
Wenn Du Dein System einschaltest, ist das RAM ja erstmal undefiniert,
d.h. die Sections .data und .bss (beide liegen im Datenspeicher/RAM)
enthalten erstmal Müll.
Jetzt läuft Dein Program aus dem Codespeicher los und der Startup-Code
(den der GCC heimlich für Dich eingebaut hat) initialisiert den
Datenspeicher (RAM). Dabei gilt:
Die Section .bss wird einfach komplett mit 0 beschrieben.
In die Section .data werden die Initialwerte der initialisierten
Variablen kopiert, die in der Section .text hinterlegt sind.
Alle Informationen, die Dein Programm zum Laufen benötigt, liegen also
allein in der .text Section, während sich .bss und .data quasi aus den
im .text hinterlegten Adressen und dem Startup-Code ergeben.
Danke erstmal für alle Antworten.
Bernd K. schrieb:> .data sollte ans Ende des .text kommen, da liegen die Werte mit denen> globale Variablen initialisiert werden (macht der Starup-code [den Du> schreibst], der soll das dann vom Programmspeicher (von der Stelle an> der .data anfängt) in den Datenspeicher (an die Stelle an der .bss> anfängt) kopieren. Das wird alles im Linkerscript so definiert
Also liegen nur die Werte für .data schon im .elf file (in der .text
section) oder .data selbst?
Laut
DJ Tobsen schrieb:> Wenn Du Dein System einschaltest, ist das RAM ja erstmal undefiniert,> d.h. die Sections .data und .bss (beide liegen im Datenspeicher/RAM)
wohl ersteres? Mache ich aber einen objdump auf mein elf-file, sehe ich
im dump ein .data mit den globalen Variablen.
Mein Problem ist, dass ich im FPGA ja selbst Befehls- und Datenspeicher
instanziiere, die jeweils bei Adresse 0 anfangen, so wie hier schon
angemerkt
asdfasd schrieb:> Bei einer Harvard-Architektur spricht nichts dagegen, sowohl Code als> auch Daten bei 0 beginnen zu lassen
Die Speicher verbinde ich mit den entsprechenden Bussen und muss diese
natürlich selbst irgendwie laden.
Mein Plan ist, dass ich den Befehlsspeicher mit dem lade, was im .elf
file steht (objcopy binary in den Speicher laden), der Datenspeicher
bleibt erstmal leer (und wird zur Laufzeit initialisiert). .data muss
dann aber wirklich komplett im Datenspeicher (und damit nicht in der
elf-Datei initialisiert) liegen, da der Datenbus nur dort Zugriff hat.
VHDL hotline schrieb im Beitrag #4101386:
> Mein Plan ist, dass ich den Befehlsspeicher mit dem lade, was im .elf> file steht (objcopy binary in den Speicher laden), der Datenspeicher> bleibt erstmal leer (und wird zur Laufzeit initialisiert). .data muss> dann aber wirklich komplett im Datenspeicher (und damit nicht in der> elf-Datei initialisiert) liegen, da der Datenbus nur dort Zugriff hat.
Das ist üblich so.
Bedeutet natürlich, dass es auch Befehle geben muß, die Daten vom
Befehlsspeicher lesen können. Manchmal werden diese dann auch zur
Implementierung von .const genutzt.
VHDL hotline schrieb im Beitrag #4101386:
> Also liegen nur die Werte für .data schon im .elf file (in der .text> section) oder .data selbst?
Genau so wie ichs gesagt habe: Die liegen in .data (und das liegt im
flash, zusammen mit dem .text aber als eigene Sektion, .data eben) und
alles in .data wird zur Laufzeit im startup an die richtige Stelle im
Arbeitsspeicher kopiert. Und wo genau diese Stellen sind wird im
Linkerscript definiert, dort werden auch Symbole definiert auf die sich
später der Startup code beziehen kann damit er genau weiß was er von
welcher Adresse nach wo genau (und wieviel) kopieren soll.
Schau einfach mal wie es sich bei einen AVR (das ist Harward) verhält,
mach zu Anschauungszwecken ein kleines Testprogramm mit ein paar
globalen Variablen, ein paar davon initialisierst Du mit 0, ein paar
davon mit was anderem (irgendwas was leicht im Hex-Editor wiederzufinden
ist), dann schaust Du Dir die sections im .elf an, was wo drin ist,
anschließend schaust Du Dir das .bin oder das .hex an und vergleichst
welche Sections Du da wiederfindest und welche nicht und dann schaust Du
Dir noch an was der startup code unmittelbar nach dem Reset alles macht,
was er wie initialisiert und woher er die Daten dafür nimmt.
Wenn Du kein .data haben willst dann sorge dafür daß es Größe 0 hat
indem Du keine statischen globalen Initializer verwendest, dann entfällt
auch das Problem Daten vom Programmspeicher in den Arbeitsspeicher
kopieren zu müssen. Dann musst Du Dich nur noch um .text kümmern, Dein
.bin file wird dann auch nur noch .text und sonst nichts mehr enthalten,
das lädst Du in Deinen Programmspeicher und startest es, den Rest macht
dann Dein Programm.
VHDL hotline schrieb im Beitrag #4101386:
> Also liegen nur die Werte für .data schon im .elf file (in der .text> section) oder .data selbst?
Stell Dir mal vor, Du hast ein ELF-File für einen PC mit Betriebssystem.
Das ELF-File enthält .text + .data + .bss.
Beim Laden des ELF-Files (zur Laufzeit) macht der Fileloader des
Betriebssystem nun folgendes:
Er reserviert soviel RAM, wie .text benötigt und lädt .text dort hin.
Er reserviert soviel RAM, wie .data benötigt und lädt .data dort hin.
Er reserviert soviel RAM, wie .bss benötigt und initialisiert dieses mit
0.
=> Der Sinn, zwischen .data und .bss zu unterscheiden ist der, dass der
.bss-Bereich nicht in der Datei gespeichert werden muß. Dadurch wird die
Datei kleiner, was zu früheren Zeiten mal ein echter Vorteil war.
Stell Dir mal vor, Du hast ein ELF-File für einen µC ohne
Betriebssystem.
Das ELF-File enthält .text + .data + .bss.
Es gibt aber kein Betriebssystem und auch keinen Fileloader, der das
ELF-File zur Laufzeit lädt. Stattdessen muß der Debugger/Programmer aus
dem ELF-File den Maschinencode herausziehen und ins Flash programmieren.
Der Maschinencode muß aber noch eine Extrafunktion enthalten, die beim
Starten des Programmes die zur .data gehörigen Daten im RAM mit ihren
Startwerten initialisiert und die zum .bss gehörigen Daten im RAM mit 0
initialisiert. Diese Funktion steckt im Startup-Code.
Wichtig dabei:
Was im ELF-File steht landet nicht 1:1 im Flash!
Im ELF-File stecken viel mehr Informationen, z.B. die Namen Deiner
Variablen, damit Du beim Debuggen bequem mit Variablennamen arbeiten
kannst. Im Maschinencode hingegen stecken nur noch Adressen, aber keine
symbolischen Namen mehr.