Common Object File Format
# Common Object File Format

Das Common Object File Format (COFF; deutsch allgemeines Objektdateiformat) ist ein Binärformat für Programme und Objektdateien. Es wurde von AT&T für das Betriebssystem Unix System V eingeführt und findet heutzutage vor allem im darauf aufbauenden Format PE für Windows Verwendung (siehe Portable Executable). Für Dateiendungen wird, falls vorhanden und abgesehen von den für PE genutzten Endungen, oft „cof“, „obj“ oder „lib“ verwendet.

## Geschichte

Ursprünglich wurde das Format a.out für ausführbare Dateien unter Unix verwendet. Dieses unterstützte jedoch moderne Entwicklungen wie eingebettete Debugging-Informationen oder dynamische Bibliotheken nicht. Deshalb entwickelte AT&T für Release 3 vom Unix System V das Common Object File Format. Da das originale COFF designtechnisch beschränkt war, entwickelten sich unterschiedliche Varianten unter den Unix-Herstellern (z. B. XCOFF von IBM für AIX, ECOFF von SGI und anderen). Mit dem Release 4 von System V im Jahre 1989 ersetzte AT&T COFF durch das neue, gemeinsam mit Sun Microsystems entwickelte Format ELF (Executable and Linking Format).

## Eigenschaften

Mit COFF wurde es möglich, Debugging-Informationen direkt in eine Binärdatei einzubetten. Bibliotheken können dynamisch gelinkt und als separate Dateien gehandhabt werden, brauchen also nicht zum unveränderlichen, unaustauschbaren Bestandteil einer Programmdatei zu werden. Dazu werden alle Adressen in den Relokationseinträgen relativ zur eigentlichen Adresse der Sektion in den virtuellen Speicher der Anwendung geladen. Dadurch braucht die Adresse der Sektion erst zur Übersetzungszeit festgelegt zu werden anstatt bereits bei der Programmierung. Nach COFF entwickelte Formate besitzen diese Fähigkeiten ebenfalls.

## Verwendung

Moderne Unix- und Linux-Versionen unterstützen COFF nicht mehr, allerdings wird es für Eingebettete Systeme noch verwendet. Unter Windows NT (und früher) ist die COFF-Variante Portable Executable (PE, manchmal auch PE/COFF) das Standarddateiformat für Bibliotheken und ausführbare Dateien, allerdings unterscheidet sich diese Variante geringfügig vom ursprünglichen COFF.

## Struktur

Eine COFF-Datei besteht aus mehreren Teilen. Sie beginnt mit dem File Header und einem Optional Header. Dann folgt eine Anzahl von Sektionen, bestehend aus Header, einer Datensektion sowie einem Bereich für Zeilennummerneinträge und einem Bereich für Relokationseinträge. Am Dateiende folgen eine Symboltabelle und eine Zeichenkettentabelle.

### File Header

Der File Header steht am Anfang einer Datei. Dort sind Daten gespeichert, die den Aufbau der gesamten Datei beschreiben. Dazu gehört die Magische Zahl, die für die unterschiedlichen Varianten (PE, XCOFF etc.) unterschiedlich ist, ein Unix-Timestamp mit dem Zeitpunkt der Erstellung der Datei, sowie die Position und Größe anderer Sektionen. Zudem können mittels Flag verschiedene Eigenschaften der Datei definiert werden (z. B. ob sie ausführbar ist).

### Optional Header

Der Optional Header enthält je nach COFF-Variante unterschiedliche Daten. Oft wird er für weitere zur Ausführung benötigte Informationen (z. B. die Einstiegsadresse) verwendet. Da er unterschiedlich lang sein kann, ist seine Größe im "File Header" gespeichert.

### Section Header

Der Section Header enthält Daten über eine Sektion, insbesondere wie groß diese ist und wohin sie in den virtuellen Speicher geladen werden sollte. Für ausführbare Dateien in der Regel der Anfang des Speichers, d. h. die erste Sektion wird an die Adresse 0 geladen, für gelinkte Daten kann dies anders sein. Zudem enthalten sie einen Zeiger auf und die Größe der Zeilennummerneinträge und der Relokationseinträge.

### Datensektion

Die Datensektion kann unterschiedlich lang sein. Sie enthält die eigentlichen Daten in der Datei. Dies sind in der Regel Anweisungen in Maschinencode, Platz für Variablen und Daten, die für die Ausführung benötigt werden – kurzum, das eigentliche Programm.

### Relokationseintrag

Ein Relokationseintrag definiert, wo die Symbole in der Datensektion gefunden werden können. Dies wird für jedes Symbol einzeln definiert.

### Zeilennummerneintrag

Ein Zeilennummerneintrag definiert, welche Zeile im Quellcode welcher Anweisung im Maschinencode entspricht. Dies ist insbesondere zum Debuggen von Anwendungen wichtig. Jede Sektion hat ihre eigene Tabelle mit Zeilennummern. Die Zeilen werden dabei für jede Funktion in der Sektion einzeln gezählt.

Zeilennummern werden ab Anfang jeder Funktion ab 0 hochgezählt. Für eine Zeile, auf der eine Funktion beginnt, wird also ein Eintrag mit l_lnno = 0 und dem Symbol der Funktion als l_symndx erstellt. Für jede weitere Zeile in der Funktion wird ein Eintrag mit der Anzahl an Zeilen seit dem Funktionsbeginn als l_lnno erstellt und der Adresse der ersten Anweisung aus dieser Zeile als l_paddr.

### Symboltabelle

Die Symboltabelle enthält Informationen über die in der Datei vorhandenen Symbole. Symbole sind z. B. Funktionen oder Variablen, die von anderen Programmen verwendet werden können. Die Größe und die Position der Symboltabelle wird im File Header festgelegt. Die Symboltabelle besteht aus Einträgen der Form

Der Name des Symbols wird in e_name gespeichert, wenn er höchstens acht Zeichen lang ist. Ansonsten wird er in der Zeichenkettentabelle abgelegt, dann ist e_zeros = 0, und e_offset gibt die Position dieses Eintrags in der Zeichenkettentabelle an. Der „Wert“ des Symbols wird in e_value gespeichert. Dies ist in der Regel die Adresse, an der dieses Symbol abgelegt ist, welche wiederum vom Datentyp und der Speicherklasse abhängt, die in e_sclass abgelegt ist. e_type definiert den Datentypen des Symbols. Dies kann entweder ein elementarer Typ (int, float etc.) oder ein zusammengesetzter Typ (struct, union) sein. Zudem kann das Symbol einen Wert, einen Zeiger ("pointer"), ein Feld ("array") oder eine Funktion, die diesen Wert zurückgibt, definieren. e_class definiert die Speicherklasse, also wo und wie das Symbol abgelegt ist (z. B. kann es ein externes Symbol sein, ein Funktionsargument, eine globale oder statische Variable etc.). Abhängig von Typen des Symbols können zusätzliche Einträge folgen. Die Anzahl dieser Einträge ist mit e_numaux angegeben.

### Zeichenkettentabelle

Die Zeichenkettentabelle folgt am Schluss der Datei. Sie beginnt mit einer Ganzzahl ("integer"), in der die Länge der Tabelle gespeichert ist. Danach folgen alle Zeichenketten hintereinander. Um eine Zeichenkette zu lesen, muss man deren Position kennen und kann an dieser Stelle mit dem Lesen beginnen. Die Zeichenketten sind nullterminiert.

