Detaillierte Erläuterung der Zusammensetzung der ARM-Bilddatei

Einführung

        Der ARM-Compiler kompiliert verschiedene Quelldateien (Assemblerdateien, C-Sprachprogrammdateien, C++-Sprachprogrammdateien) in Zieldateien im ELF-Format (Suffix ist .o-Datei, im Folgenden wird die Zieldatei kurz als .o-Datei bezeichnet). .o-Datei Über den Connector wird es zusammen mit der C/C++-Laufzeitbibliothek kompiliert, um eine Bilddatei im ELF-Format zu generieren (Bild, z. B. die Bin-Datei, die von der häufig verwendeten MKD mit fromelf.exe generiert wird, ist ein Bild). Die Bin-Datei kann direkt in Flash geschrieben werden. Tatsächlich ist der mit jlink heruntergeladene Inhalt der Inhalt der Bin-Datei (die Hex-Datei ist eine Bin-Datei mit Adressinformationen, die als hex = Adressinformationen + bin verstanden werden kann). Die Bilddatei wird im Folgenden auch als Bin-Datei oder Bilddatei bezeichnet. Der Kompilierungsprozess ist wie unten dargestellt.

Das Bild stammt aus „Einführung in die Armv8-M-Architektur und ihr ProgrammersModel“

Verwandte Dokumente sind hinten verlinkt

1. Zusammensetzung der ARM-Bilddatei

1.1 Zusammensetzung der ARM-Bilddatei

Die ARM-Bilddatei ist eine hierarchische Struktur, die aus den folgenden drei Teilen besteht: Region, Ausgabeabschnitt und Eingabeabschnitt. Die drei haben die folgende Beziehung:

  • Eine Bin-Datei besteht aus einem oder mehreren Feldern

  • Eine Domäne enthält einen oder mehrere Ausgabeabschnitte

  • Ein Ausgabeabschnitt enthält einen oder mehrere Eingabeabschnitte

  • Jeder Eingabeabschnitt enthält den Code und die Daten in der Zieldatei

        Der Eingabeabschnitt kann als die spezifischen Attribute verstanden werden, die in dem von uns geschriebenen Code enthalten sind. Das Eingabesegment umfasst 4 Kategorien: Code, initialisierte Daten, nicht initialisierter Speicherbereich, Bereich mit auf 0 initialisiertem Inhalt, d. h. (Code+RW), ZI und ZI, die jeweils RO-Segment, RW-Segment und ZI-Segment sind. Der Compiler gruppiert diese Eingabeabschnitte basierend auf ihren Attributen in verschiedene Ausgabeabschnitte und Domänen.

        Ein Ausgabeabschnitt enthält eine Reihe von Eingabeabschnitten mit denselben RO-, RW- und ZI-Attributen. Die Attribute des Ausgabeabschnitts sind dieselben wie die Attribute der darin enthaltenen Eingabeabschnitte. Innerhalb eines Ausgabeabschnitts werden die Eingabeabschnitte nach bestimmten Regeln sortiert.

        ​​​​Eine Domäne enthält 1 bis 3 Ausgabesegmente, wobei die Attribute jedes Ausgabesegments unterschiedlich sind. Die Reihenfolge jedes Ausgabesegments wird durch seine Attribute bestimmt. Das RO-Attribut steht an erster Stelle, gefolgt von RW und schließlich dem ZI-Segment. Eine Domäne wird normalerweise einem physischen Speicher zugeordnet, z. B. Flash, RAM usw.

        Abgesehen von dem eher schriftlichen Ausdruck oben haben wir Keil als Beispiel. In MDK wird die Definition der Domäne durch die Scattered-Link-Datei (SCT) bestimmt. Wenn Sie eine Keil-SCT-Datei öffnen, können Sie Folgendes sehen:

In sct sind drei Domänen mit den Namen LR_IROM1, ER_IROM1 und RW_IRAM1 definiert. LR_IROM1 ist die letzte Bin-Datei, ER_IROM1 ist die Ladedomäne und RW_IRAM1 ist die Ausführungsdomäne. Der von Keil generierte Bin enthält im Allgemeinen drei Segmente: RO-Segment (Code + schreibgeschützte Daten), RW-Segment und ZI-Segment. Eine Domain enthält mindestens eines der oben genannten drei Segmente.

        Eine C-Datei kann Code, globale Variablen, schreibgeschützte Daten, 0-Daten und nicht initialisierte Daten enthalten. Während der Kompilierung wird jedes Segment automatisch klassifiziert. Beispielsweise wird in der Datei led.c der Codeteil im RO-Segment platziert, globale Variablen werden im RW-Segment platziert und nicht initialisierte Variablen werden im ZI gespeichert Segment.

        Lassen Sie uns ein paar Vermutungen anstellen: Wenn wir zwei Dateien led1.c und led2.c haben, die jeweils RO, RW und ZI generieren, kann dies wie folgt verstanden werden: led1.c und led2.c sind die Eingabeabschnitte 1.1.1 und 1.1. In 2 sind die von ihnen generierten RO, RW und ZI die Ausgabesegmente 1.1 und 1.2, die schließlich in Domäne 1 zusammengeführt werden (tatsächlich wissen wir, dass diese Domäne im Flash im M4-Chip gespeichert ist).

1.2 Adresszuordnung jeder Komponente der ARM-Bilddatei

        Es gibt zwei Adressen für Bin-Dateien im Speichersystem (z. B. im internen Flash): Eine davon ist die Adresse, an der sich die Bin-Datei im Speicher befindet, die als Ladeadresse bezeichnet wird. Eine davon ist die Adresse, an der die Bin-Datei ausgeführt wird, die sogenannte Laufadresse. Der Grund für die Unterteilung in zwei Kategorien liegt darin, dass in der Bin-Datei einige Felder in neue Speicherbereiche verschoben werden können. Beispielsweise werden die Daten im RW-Feld während des Betriebs in den RAM verschoben.

        Wie in der folgenden Abbildung dargestellt, lautet die Startadresse des Flashs 0x0800 0000, in der die RO-, RW- und ZI-Segmente nacheinander gespeichert werden. Während des Betriebs werden ZI und RW in den SRAM verschoben.

        Eine Bin-Datei enthält im Allgemeinen mehrere Felder und ein Feld enthält mehrere Ausgabeabschnitte. Der ARM-Linker muss die folgenden Informationen kennen, um die Bin-Datei beim Linken korrekt zu generieren:

  • Gruppierungsinformationen bestimmen, wie jeder Eingabeabschnitt in entsprechende Ausgabeabschnitte und -felder organisiert wird.

  • Positionierungsinformationen bestimmen die Startadresse jeder Domäne im Speicherplatz

    Abhängig von der Komplexität der Adresszuordnung in der Bin-Datei können Sie Befehlszeilenoptionen verwenden, um sie bereitzustellen, oder eine Konfigurationsdatei (z. B. die SCT-Datei von Keil) bereitstellen.

2. Einstiegspunkt der ARM-Bilddatei

2.1 Zwei Arten von Einstiegspunkten in ARM-Bilddateien

        Die Bin-Datei verfügt über zwei Arten von Einstiegspunkten: Einer ist der Einstiegspunkt, wenn die Bilddatei ausgeführt wird, der als anfänglicher Einstiegspunkt (anfänglicher Einstiegspunkt) bezeichnet wird, und der andere ist ein gewöhnlicher Einstiegspunkt (Einstiegspunkt).

        Der anfängliche Einstiegspunkt ist der Einstiegspunkt, wenn die Bin-Datei ausgeführt wird. Jeder Bin hat nur einen eindeutigen anfänglichen Einstiegspunkt, der in der ELF-Header-Datei gespeichert ist. Wenn bin vom Betriebssystem geladen wird, lädt das Betriebssystem die Bilddatei, indem es zur Ausführung zum ursprünglichen Einstiegspunkt springt.

        Normale Einstiegspunkte werden mithilfe von ENTRY in Assembly definiert. Eine typische Verwendung in eingebetteten Systemen besteht darin, das Interrupt-Serviceprogramm als normalen Einstiegspunkt zu definieren, um zu verhindern, dass das Interrupt-Serviceprogramm während der Verknüpfung gelöscht wird. In einer C-Bibliothek ist __main der Einstiegspunkt. In sct können Sie Folgendes sehen:

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x10000000 0x0000F000  {    ; load region size_region
  ER_IROM1 0x10000000 0x0000F000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }
  RW_IRAM1 0x1FFFC000 0x00003000  {  ; RW data
   .ANY (+RW +ZI)
  }
}

Unter diesen ist REST das Attribut des anfänglichen Einstiegspunkts. Platzieren Sie den anfänglichen Einstiegspunkt im ROM. Sie können die folgende Montagemethode in start.s sehen:

Dies ist der Teil des Codes, der den anfänglichen Einstiegspunkt definiert.

        In einer Bin-Datei gibt es nur einen anfänglichen Einstiegspunkt. Es können mehrere gemeinsame Einstiegspunkte vorhanden sein. Der anfängliche Einstiegspunkt kann ein gemeinsamer Einstiegspunkt sein oder nicht.

2.2 Definieren Sie den ersten Einstiegspunkt

Der anfängliche Einstiegspunkt muss die folgenden zwei Bedingungen erfüllen:

  • Der anfängliche Einstiegspunkt muss innerhalb der Laufzeitdomäne der Bin-Datei liegen

  • Der Laufzeitbereich, der den anfänglichen Einstiegspunkt enthält, kann nicht überschrieben werden und seine Ladeadresse und seine Ausführungsadresse müssen identisch sein (dieser Bereich wird als feste Region, Stammregion) bezeichnet.

Sie können die Linkoption -entry address verwenden, um den anfänglichen Einstiegspunkt des Bins anzugeben, wobei „address“ die Adresse des anfänglichen Einstiegspunkts darstellt.

        Wenn bin geladen und dann von einem Ladeprogramm wie einem Bootloader oder einem Betriebssystem ausgeführt wird, muss die bin-Datei einen anfänglichen Einstiegspunkt enthalten. Beispielsweise wird eine Betriebssystem-Image-Datei von einem Boot-Programm geladen. Zu diesem Zeitpunkt springt das Programm zum anfänglichen Einstiegspunkt der Image-Datei und beginnt mit der Ausführung. Es überschreibt das Boot-Programm und wird im System als Betriebssystem bezeichnet. Diese Art von Bilddatei enthält normalerweise andere gemeinsame Einstiegspunkte, bei denen es sich im Allgemeinen um die Eintragsadressen von Ausnahme-Interrupt-Handlern handelt.

        Wenn der Benutzer die Linkoption -entry nicht angibt, um die Linkadresse anzugeben, wird der anfängliche Einstiegspunkt der Bilddatei gemäß den folgenden Regeln bestimmt:

  • Wenn es in der Eingabeobjektdatei nur einen gemeinsamen Einstiegspunkt gibt, wird der gemeinsame Einstiegspunkt vom Linker als erster Einstiegspunkt der Bin-Datei verwendet.

  • Wenn die Eingabeobjektdatei keinen gemeinsamen Einstiegspunkt oder mehr als einen gemeinsamen Einstiegspunkt hat, enthält die vom Linker generierte Bin-Datei den anfänglichen Einstiegspunkt nicht und es wird eine Warnung generiert.

2.3 Nutzung gemeinsamer Einstiegspunkte

Gemeinsame Einstiegspunkte werden im Allgemeinen für zwei Zwecke verwendet:

  • Geben Sie den Einstiegspunkt des Interrupt-Serviceprogramms an, um zu verhindern, dass der Programmcode des Interrupt-Servers während der Verknüpfung gelöscht wird

  • Wenn die Linkoption -entry-Adresse nicht angegeben ist und es in der Eingabezieldatei nur einen gewöhnlichen Einstiegspunkt gibt, wird der gewöhnliche Einstiegspunkt als anfänglicher Einstiegspunkt verwendet.

3. Anordnungsreihenfolge der Eingabesegmente

Der Linker organisiert diese Eingabeabschnitte entsprechend ihren Attributen. Eingabeabschnitte mit denselben Attributen werden in einem kontinuierlichen Adressraum in der Domäne platziert, um einen Ausgabeabschnitt zu bilden. Im Ausgabeabschnitt hängt die Startadresse jedes Eingabeabschnitts mit der Startadresse des Ausgabeabschnitts und der Anordnungsreihenfolge der Eingabeabschnitte im Ausgabeabschnitt zusammen.

Normalerweise wird die Reihenfolge der Eingabeabschnitte im Ausgabeabschnitt durch die folgenden Faktoren bestimmt:

  • Geben Sie Segmentattribute ein

  • Geben Sie den Namen des Segments ein

  • Die Reihenfolge, in der die einzelnen Eingabeabschnitte in der mit der Befehlszeile verbundenen Eingabeabschnittsliste angeordnet sind

Entsprechend den Attributen des Eingabeabschnitts ist die Reihenfolge wie folgt:

  • Nur-lesbares Codesegment (RO-Code)

  • Schreibgeschütztes Datensegment (RO-Daten)

  • Lesbares und beschreibbares Codesegment (RW-Code)

  • Andere und initialisierte Datensegmente (RW-Daten)

  • Nicht initialisierte Daten (ZI)

        Eingabeabschnitte mit denselben Attributen werden in der Reihenfolge ihrer Namen sortiert. Bei den Namen der Eingabeabschnitte wird die Groß-/Kleinschreibung beachtet und sie werden in der Reihenfolge ihrer ASCII-Codes sortiert. Wenn auch die Eingabeabschnitte denselben Namen haben, werden sie entsprechend ihrer Reihenfolge in der Liste der Eingabeabschnitte sortiert. Das heißt, selbst wenn die Attribute und Namen der einzelnen Eingabeabschnitte unverändert bleiben und die Anordnungsattribute der einzelnen Eingabeabschnitte in der Liste während der Kompilierung unterschiedlich sind, sind auch die generierten Bilddateien unterschiedlich.

        Sie können die Verbindungsoptionen -first und -last verwenden, um die Reihenfolge der Eingabeabschnitte zu ändern, oder Sie können die Konfigurationsdatei verwenden, um die Reihenfolge der Eingabeabschnitte zu ändern. Es gibt drei Faktoren, die die Sortierregeln beeinflussen: Eingabesegmentattribute, Eingabesegmentnamen und die Reihenfolge der Eingabesegmente in der Liste. Die Verbindungsoptionen -first und -last wirken sich nur auf den Namen des Eingabeabschnitts und die Reihenfolge in der Liste aus und können die Sortierregeln basierend auf den Attributen des Eingabeabschnitts nicht ändern.

        Nachdem jedes Eingabeende sortiert wurde und bevor die Startadresse jedes Eingabesegments endgültig bestimmt wird, kann der „Patch“ ausgefüllt werden, damit jedes Eingabesegment die Anforderungen an die Adressausrichtung erfüllt.

4. Exkurs

        Bei diesem Artikel handelt es sich um einen Notizartikel, den der Blogger geschrieben hat, als er das ARM-System erlernte. Er wurde erstmals auf CSDN geteilt. Die verwendeten Referenzdokumente sind:

  • „Einführung in die Armv8-M-Architektur und ihr Programmiermodell“
  • „ARM-Architektur und Programmierung“

Relevante Dokumente wurden in das Lager des Bloggers hochgeladen, Link:https://gitee.com/zichuanning520/htq_library

Guess you like

Origin blog.csdn.net/zichuanning520/article/details/133787930