6 Stimmen

Unklarheit über die Aufgabe des Linkers

Ich verwende die Sprache C unter Windows. Diese Frage war zuvor Teil von Was geschieht mit Bezeichnern in einem Programm? . Ich habe sie zerlegt, um die Anzahl der Fragen zu reduzieren. Dies ist eine eigenständige Abfrage (hängt nicht von der vorherigen Frage ab)

Wenn es nichts zu verknüpfen (d.h.. Ich benutze keine Bibliotheken. Ich weiß, es wird nicht von Nutzen sein.) wird der Linker die Objekt-Code-Ausgabe von Assembler ändern? Wenn ja, was ändert er?

Ich habe gehört, dass LINKER auch einige Speicherzuordnungen vornimmt. Ich verstehe nicht, wie. Das Programm läuft nicht, es befindet sich nur in der Herstellungsphase. Wie könnte Linker den Speicher abbilden? Wie würde das aussehen? Was sind die Funktionen von LINKER?

Wenn die Leute von "Umzug", "Adressbindung" sprechen. verstehe ich nicht wirklich, was sie meinen. Was ist das und was ist der Zweck?

Einige Debugger zeigen Informationen wie : Call Stack: 0xfffef32 , 0xf3234fe usw.. Ist das zur Laufzeit richtig oder sind das die Speicheradressen des so genannten "Memory Mapping" des Linkers?

wenn Menschen sich auf etwas beziehen wie symbols o symbol table . Meinen sie Bezeichner (Variablennamen, Konstantennamen, Funktionsnamen)?

Ich habe im Internet nach Informationen gesucht, konnte aber nichts Nützliches finden. Vielleicht bin ich nicht sicher, wonach ich suchen soll. Ich möchte keine dicken Bücher über dieses Thema lesen. Aber wenn es irgendwelche Artikel oder Tutorials gibt, die die Konzepte verdeutlichen. Das wäre auch hilfreich.

Ich bin ein unerfahrener Programmierer. Es wäre also toll, wenn Sie es in einfachen, aber technischen Begriffen erklären könnten.

4voto

Richard Pennington Punkte 19289

Wenn Sie eine Quelldatei kompilieren, wird sie normalerweise vom Compiler/Assembler in mehrere Abschnitte unterteilt. Stellen Sie sich als hypothetisches Beispiel vor, dass die folgenden Abschnitte verwendet werden:

  • .text - enthält den gesamten ausführbaren Code
  • .const - enthält konstante Daten
  • .data - enthält initialisierte Lese-/Schreibdaten
  • .bss - enthält nicht initialisierte Daten zum Lesen/Schreiben

In einer einzelnen Quelldatei ordnet der Compiler/Assembler die entsprechenden Elemente den entsprechenden Abschnitten zu und gibt den verwendeten Symbolen Offsets im Abschnitt, beginnend bei Null.

Zum Beispiel:

int i;
const j = 3;
int k = 4;
int l;
int main()
{
return 1;
}

Dies könnte zu der folgenden Symboltabelle führen:

Symbol Section Offset
i      .bss    0
j      .const  0
k      .data   0
l      .bss    4
main   .text   0

In der Objektdatei können neben der Symboltabelle auch die Daten der einzelnen Abschnitte gespeichert werden. In diesem Beispiel würde der Abschnitt .text den Objektcode für "return 1" enthalten, der Abschnitt const die Zahl 3 und der Abschnitt data die Zahl 4. Der Abschnitt .bss müsste nicht in der Objektdatei enthalten sein, da die Variablen nicht initialisiert wurden.

Das erste, was ein Linker tun könnte, ist, alle Abschnitte der Eingabeobjektdatei zu verketten und die Symbol-Offsets entsprechend anzupassen.

Jetzt kommen wir zu dem, was "Relocation" oder "Adressbindung" genannt wird. Nehmen wir an, dass in einem hypothetischen System der ausführbare Code an der Adresse 0x1000 beginnt. Nehmen wir weiter an, dass die Datenabschnitte eines Programms an einer geraden Seitengrenze nach dem ausführbaren Code beginnen sollen. Der Linker würde 0x1000 als Basis der verketteten .text-Abschnitte zuweisen und alle Symbole anpassen. Dann wird die Basis der .const-, .data- und .bss-Abschnitte in ähnlicher Weise festgelegt, um sie an den entsprechenden Stellen im Speicher zu platzieren.

Manchmal gibt es in einem Abschnitt symbolische Hinweise. Diese Verweise müssen vom Linker aktualisiert werden, um die endgültige Position des Symbols, auf das verwiesen wird, wiederzugeben. Die Objektdatei könnte "Relocation Records" enthalten, die wie folgt aussehen

section offset symbol
.text   0x1234 foo

Der Linker geht zu jedem Offset in jedem Abschnitt und aktualisiert den Wert dort, um den endgültigen Symbolwert wiederzugeben.

Nachdem all dies erledigt ist, kann die resultierende "absolute" Objektdatei in den Speicher geladen (natürlich an der richtigen Stelle!) und ausgeführt werden.

1voto

Carl Smotricz Punkte 64366

Ich werde für diese Diskussion mit C arbeiten.

Es kommt selten vor, dass ein C-Programm nicht zumindest auf einige Bibliotheksfunktionen verweist. Selbst wenn Ihr Code nur in einem Modul (einer Datei) enthalten ist, gibt es also normalerweise Verweise auf Bibliotheksfunktionen. In der kompilierten Form Ihres Programms befinden sich diese Verweise in einer externen Referenztabelle, d.h. in einer Tabelle, in der die textuellen Namen zusammen mit den Stellen in Ihrem Programm erscheinen, die auf diese externen Adressen verweisen sollen.

Die Aufgabe des Linkers ist es, Ihr Programm in einer einzigen Datei mit allen anderen Modulen, die es verwendet, zu verketten und dann die externen Definitionen in einem Modul mit den externen Referenzen in einem anderen abzugleichen, d.h. Parcheando alle Querverweise mit der App so, dass die Aufrufe die richtigen Adressen treffen.

Selbst wenn Sie keine externen Module referenzieren, wird der Link wahrscheinlich einige relative Referenzen in Ihrem Code in absolute umwandeln müssen; d.h. sobald er "weiß", wo in der Datei Ihr Code sitzen wird, kann er den Dingen die richtigen endgültigen Adressen zuweisen.

1voto

rpj Punkte 2370

Keine Antwort, nur eine Anregung: kaufen "Linker und Lader" Ich habe es ein paar Mal gelesen. Es ist erstaunlich hilfreich.

1voto

starblue Punkte 53167

Wenn es nichts zu verknüpfen gibt (d.h. ich verwende keine Bibliotheken. Ich weiß, dass es nicht von Nutzen sein wird.) wird der Linker die Objektcode-Ausgabe von Assembler ändern? Wenn ja, was ändert er?

Er verknüpft immer einen Initialisierungscode. Sie können dies ausprobieren, indem Sie ein leeres Programm schreiben und es linken und dann objdump -d verwenden, um es zu disassemblieren.

Ich habe gehört, dass LINKER auch einige Speicherzuordnungen vornimmt. Ich verstehe nicht, wie. Das Programm läuft nicht, es befindet sich nur in der Herstellungsphase. Wie könnte Linker den Speicher abbilden? Wie würde das aussehen? Was sind die Funktionen von LINKER?

Jedes System hat ein Speicherlayout, dem ausführbare Programme folgen müssen, um zu funktionieren. Es legt fest, wo die verschiedenen Teile des Programms hingehören (zumindest Code, initialisierte Daten, auf Null initialisierte Daten). Der Linker muss die ausführbare Datei nach diesen Regeln erstellen, die von System zu System unterschiedlich sind, z. B. bei Windows und Linux. Bei eingebetteten Systemen wird es sogar noch interessanter, denn dort befindet sich das Programm in der Regel im Nur-Lese-Speicher (Flash) und die Daten im RAM, und je nach Art des Mikrocontrollers gibt es feste Adressbereiche für die verschiedenen Arten von Speicher.

Wenn von "Umzug" die Rede ist, ist damit "Adressbindung" gemeint. verstehe ich nicht wirklich, was sie meinen. Was ist das und was ist der Zweck?

Binden bedeutet im Allgemeinen, einem Namen einen Wert zu geben, in diesem Fall eine Adresse für das Symbol einer Funktion oder einer globalen Variablen.

Was die Verschiebung betrifft, so verknüpfen Sie in der Regel mehrere Objektdateien miteinander, und jede Objektdatei gibt ihre Adressen als Offsets relativ zu ihrem Anfang an. Wenn Sie sie zusammenfügen, erhält jede ihren eigenen Adressbereich, und der Linker berechnet die Adresse für ein Symbol, indem er den Offset auf den Adressbereich abbildet. Dies wird als Relokation bezeichnet.

Einige Debugger zeigen Informationen wie: call stack: 0xfffef32 , 0xf3234fe usw.. Ist das zur Laufzeit richtig oder sind das die Speicheradressen des so genannten "Memory Mapping" des Linkers?

Die Adresse 0xfffef32 wäre eine typische Adresse auf dem Stack, da der Stack in der Regel am Anfang des Speichers liegt und nach unten wächst. Der Stack wird für Rücksprungadressen, lokale Variablen und aktuelle Funktionsparameter verwendet. Diese sind lokal und werden an Adressen relativ zum Stack-Zeiger gespeichert, so dass sie in der Regel nicht vom Linker behandelt werden, sondern der Compiler kennt bereits die zu verwendenden Offsets und fügt sie in den Assembler-Code ein.

wenn von Symbolen oder einer Symboltabelle die Rede ist. Meinen sie Bezeichner (Variablennamen, Konstantennamen, Funktionsnamen)?

Die Symboltabelle ist eine Tabelle, die Symbole auf Werte (Zahlen, Offsets, Adressen) abbildet. Es gibt einige Symbole für Ihre Bezeichner, aber auch mehr für andere Zwecke. Ihre Bezeichner können mehr oder weniger verändert werden, um zu Symbolen zu werden, hauptsächlich um Namenskonflikte zu vermeiden (z. B. das Voranstellen von "_").

Der Linker verfügt über die Option --print-map, um die Symboltabelle zu drucken. Sie können -Wl,--print-map verwenden, wenn Sie gcc zum Linken benutzen.

Wenn Sie diese Art von technischem Low-Level-Kram mögen, sollten Sie einen Blick auf die Embedded-Programmierung werfen, d.h. die Programmierung von Mikrocontrollern, die in verschiedenen elektrischen Geräten eingesetzt werden. Bei Desktop-Systemen wie Windows müssen Sie sich normalerweise nicht mit dieser Art von Details befassen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X