Assemblieren per C
-
Ich habe hier in einem Beitrag http://www.c-plusplus.net/forum/viewtopic-var-p-is-1935393.html#1935393 was gesehen, was mir eine Fraue aufwirft. Kann man mit C bzw. einem C Compiler ein bootfähiges Programm schreiben?
-
Theoretisch ist es möglich, in der Praxis jedoch nicht. Was du bräuchtest, wäre ein Compiler, welcher aus deinem ISO C Programm ein bootfähiges Binary erzeugt, ohne dass du dabei noch auf Inline-Assembler zurückgreifen oder ein weiteres Modul dazu linken musst.
Es würde darauf hinauslaufen, dass du für dein Kompilat implizit einen Bootloader hinzugefügt bekommst. Vielleicht gibt es einen solchen Compiler.
Was du tun kannst, ist ein Bootloader mit Assembler programmieren, und diesen zu deinem Programm entsprechend Linken. Wenn du alles automatisierst, kannst du theoretisch den Rest des Betriebssystems in C schreiben.
MfG
-
Nein, zumindest nicht wenn der Compiler dies nicht als spezielles Feature anbietet. Denn normalerweise wird nur eine ausführbare Datei mit einem Einstiegspunkt namens "main" angelegt, welcher vom Betriebssystem angesprungen wird. Das BIOS kann damit aber nichts anfangen, das lädt einfach nur stumpf den Bootsektor in den Speicher und springt an die erste Adresse davon.
Daher hat man hier meistens ein kleines (oder auch großes) Assemblerprogrämmchen welches dann den eigentlichen Kernel lädt und anspringt. Das nennt man dann Bootloader.
Den Rest kann man dann aber natürlich ganz normal mit C programmieren. Das ist auch wie die meisten Betriebssysteme programmiert sind.
-
Es ist doch aber schon so das der C Compiler Maschienencode compiliert der selbständig lauffähig ist oder? Warum lassen sich dann die notwendigen Komandos zum Bootvorgang auch nicht gleich in C schreiben?
Mit Assembler Bootcode schreiben und dann einfach nur noch per call das C Programm aufrufen? Oder muss in C Programm besonders behandelt werden bzw. die Umgebung mit Assemblercode speziel konfiguriert werden?
-
System-Elektroniker schrieb:
Es ist doch aber schon so das der C Compiler Maschienencode compiliert der selbständig lauffähig ist oder? Warum lassen sich dann die notwendigen Komandos zum Bootvorgang auch nicht gleich in C schreiben?
weil der Bootvorgang u.a. spezielle Befehle benutzt, die nicht zwangsläufig vom Compiler übersetzt werden. Außerdem ist der Bootvorgang zu speziell, um in eine Hochsprache zu schreiben.
Bei meinem Mikrokernel besteht der Bootvorgang aus nur ein Paar Assembler Zeilen bis ich zur pros_main Funktion springe, die in C geschrieben ist.
Den Boootloader habe ich nicht selber geschrieben, sondern verwende ich einen, der bereits installiert war (u-boot) und wie SeppJ schon erklärt hat, dieser lädt meinen Kern auf einer bestimmten Adresse und springt sog. zur ersten rein und fährt dort weiter fort.
Bei mir sieht dann so der Einstiegspunkt aus:
.text .code 32 .align 0 .type _start, function .global _start @ vector table /* * table offset * 00 - Reset * 04 - Undefined instructions * 08 - SWI instructions * 0C - prefetch abort * 10 - Data abort * 14 - Reserved * 18 - IRQ interrupts * 1C - FIQ interrupts */ _start: ldr pc, [pc, #0x18] ldr pc, [pc, #0x18] ldr pc, [pc, #0x18] ldr pc, [pc, #0x18] ldr pc, [pc, #0x18] nop ldr pc, [pc, #0x18] ldr pc, [pc, #0x18] exception_table: .word __arm_core_initialize .word __arm_core_undefined_handler .word __arm_core_swi_handler .word __arm_core_prefetch_abort_handler .word __arm_core_data_abort_handler nop .word __arm_core_irq_handler .word __arm_core_fiq_handler _arm_core_initialize: /* setting up user mode stack */ mov r2, #NoInt|Sys32md msr cpsr_c, r2 ldr sp, USR_Stack_val /* setting up IRQ Stack */ mov r2, #NoInt|IRQ32md msr cpsr_c, r2 ldr sp, IRQ_Stack_val /* setting up FIQ Stack */ mov r2, #NoInt|FIQ32md msr cpsr_c, r2 ldr sp, FIQ_Stack_val /* setting up Undefined Stack */ mov r2, #NoInt|Und32md msr cpsr_c, r2 ldr sp, UND_Stack_val /* setting up Abort Stack */ mov r2, #NoInt|Abt32md msr cpsr_c, r2 ldr sp, ABT_Stack_val /* setting up supervisor (SVC) stack */ mov r2, #NoInt|SVC32md msr cpsr_c, r2 ldr sp, SVC_Stack_val /* Setup MMU */ mov r0, #0xffffffff mcr p15, 0, r0, c3, c0, 0 bl pxa/* set page table address */ ldr r0, PXA_CONFIG_MAP_ADDR mcr p15, 0, r0, c2, c0, 0 /* Invalidate I&D cache & BTB */ mcr p15, 0, r0, c7, c7, 0 /* activate MMU */ mrc p15, 0, r0, c1, c0, 0 bic r0, r0, #0x2000 orr r0, r0, #0x1 mcr p15, 0, r0, c1, c0, 0 /* Invalidate I&D cache & BTB */ mcr p15, 0, r0, c7, c7, 0 /* setting c1[13] of cp15 to 0 vector table loads from 0x00000000 Take a look at ARM System Developer's guide on pages 513,514 */ /* disable IRQ */ bl __arm_disable_irq /* disable FIQ */ bl __arm_disable_fiq /* jump into pros_main */ bl pros_main /* restart system */ mov pc, #0 USR_Stack_val: .word USR_Stack IRQ_Stack_val: .word IRQ_Stack SVC_Stack_val: .word SVC_Stack FIQ_Stack_val: .word FIQ_Stack UND_Stack_val: .word UND_Stack ABT_Stack_val: .word ABT_Stack PXA_CONFIG_MAP_ADDR: .word CONFIG_MAP_ADDR _init_memory_map
pros_main
ist bereits in C geschrieben. Wenn du es genau hinschaust (du braucht nicht den ARM-Befehlsatz zu kennen, um zu verstehen/erahnen), sind die ersten Assemblerzeilen einfach viel zu speziell als das der Compiler genau sowas erzeugen kann (es wird Kompiler geben, die sowas automatisch als extra feature erzeugen). Dabei wird hier oft denmrc
verwendet (Co-Prozessor), was 1. sehr selten verwendet wird und 2. ganz speziell ist (um z.B. Memory Controller zu konfigurieren). Normaler Code wird niemals den Co-Prozessor verwenden können (der ja praktisch in User-Space nicht erreichbar ist) und deshalb kann ich mit C nicht auf ihn zugreifen, da muss ich Assembler benutzen.Aber wie du siehst, hält sich die Assemblernutzung stark in Gränzen. Zwecks Optimierung habe ich hier und da auch Assembler verwendet, aber an sich ist das praktisch die einzige Stelle im gesamten Mikrokerncode, an der ich richtig viel Assembler gebraucht habe.
-
prob_main ist der Dateiname des Pogramms was in C geschrieben ist und befindet sich an selber Stelle wie das Bootfile oder in der höchsten Ebene auf der Partition von der das Bootfile geladen wurde, also gleich auf C:/ bzw. A:/?
Andere Frage, gibt es tatsächlich bereits fetige mnemo Codes die die x86 Architekur unterstützen und nur darauf aufgelegt sind dann das C Programm, also der Kernel, zu laden?
-
pros_main ist der Name einer Funktion. Wo die Funktion implementiert wird, spielt keine Rolle. Der Linker nimmt alle Objekt Dateien zusammen und linkt sie zu einer ganzen Datei, die alles enthält.
Im Prinzip ist es genau so wie wenn man ein Programm schreibt, welches mehrere Source Dateien hat. Einfaches Bsp: man hat die Dateien main.c, sound.c, video.c, usw. Der Compiler kompiliert diese Dateien unabhängig voneinander zu main.o, sound.om video.o [1]. Diese .o Dateien sind Objekt Dateien. Der Linker nimmt diese .o Dateien und fügt sie zusammen zu einer einzelnen Binary (z.b. mein_super_dupper_propgi) und dann kannst du mein_super_dupper_propgi ausführen.
In C gibt es die main Funktion, die als Startpunkt deines Programmes funktioniert. Wenn du mein_super_dupper_propgi startest, lädt das Betriebssystem diese Binaries auf den RAM und [2] führt eine vom OS spezielle Funktion (z.b. _start bei GNU/Linux) aus, die die Umgebung vorbereitet und deine main-Funktion aufruft.
Der Linker fügt zur Binary mein_super_dupper_propgi den nötigen Code, damit diese _start Funktion überhaupt aufgerufen wird.
Bei einem Kernel ist es genauso. Man hat viele Objekt Dateien, die zu einer einzelnen Binary zusammengefügt wird. Hier muss man aber den Linker so aufrufen, dass er z.b. diesen extra Code für den Start der _start Funktion nicht hinzufügt, denn dieser Code direkt vom Bootloader aufgerufen wird.
Deshalb habe ich auch geschrieben, besorg dir ein Buch über Betriebssysteme und Rechnerarchitektur (siehe meine ARM-Buch Empfehlung), denn du hast Wissenslücken, die einfach notwendig sind, um in das Theme einsteigen zu können. Das Thema ist nicht einfach und es gibt verdammt viele Details, erwarte nicht von uns, hier alles genau zu erklären, denn sonst müssten wir eben ein Buch drüber schreiben.
Andere Frage, gibt es tatsächlich bereits fetige mnemo Codes die die x86 Architekur unterstützen und nur darauf aufgelegt sind dann das C Programm, also der Kernel, zu laden?
ich verstehe deine Frage nicht.
-----
[1] Die Endung soll hier egal sein, da sie sich vom Compiler zu Compiler unterscheiden kann
[2] in Wriklichkeit passiert viel mehr und ist vom OS zu OS auch unterschiedlich
-
SeppJ schrieb:
Nein, zumindest nicht wenn der Compiler dies nicht als spezielles Feature anbietet. Denn normalerweise wird nur eine ausführbare Datei mit einem Einstiegspunkt namens "main" angelegt, welcher vom Betriebssystem angesprungen wird.
Falsch. Das Betriebssystem hat von main keine Ahnung. Es startet normalerweise einfach am Anfang des Code-Segments. Dort wird der Startupcode gebunden und der ruft main auf.
mfg Martin