StellatorOS: EntryPoint


Cuando compilamos código en C el compilador decide en que posición del binario debe insertar cada parte de código y datos, hasta ahora eso no ha sido un problema ya que el kernel solo tenía una función, pero conforme vaya creciendo puede que las primeras instrucciones no correspondan con la función main().

 

Antes de comenzar es recomendable que leas estos artículos anteriores:

Veamos como quedaría el siguiente código:

vi kernel2.c
void some_function () {
}

void main () {
 char* video_memory = (char*) 0xb8000;
 *video_memory = 'X';
 some_function ();
}

Compilamos:

cc -m32 -ffreestanding -c kernel2.c -o kernel2.o

Mostramos el código ASM:

objdump -M intel -d kernel2.o 
kernel2.o:     file format elf32-i386-freebsd

Disassembly of section .text:

00000000 <some_function>:
   0: 55                    push   ebp
   1: 89 e5                 mov    ebp,esp
   3: 5d                    pop    ebp
   4: c3                    ret    
   5: 90                    nop    
   6: 90                    nop    
   7: 90                    nop    
   8: 90                    nop    
   9: 90                    nop    
   a: 90                    nop    
   b: 90                    nop    
   c: 90                    nop    
   d: 90                    nop    
   e: 90                    nop    
   f: 90                    nop    

00000010 <main>:
  10: 55                    push   ebp
  11: 89 e5                 mov    ebp,esp
  13: 50                    push   eax
  14: b8 00 80 0b 00        mov    eax,0xb8000
  19: 89 45 fc              mov    DWORD PTR [ebp-0x4],eax
  1c: 8b 45 fc              mov    eax,DWORD PTR [ebp-0x4]
  1f: c6 00 58              mov    BYTE PTR [eax],0x58
  22: e8 d9 ff ff ff        call   0 <some_function>
  27: 83 c4 04              add    esp,0x4
  2a: 5d                    pop    ebp
  2b: c3                    ret    

Generamos la imagen de disco con el kernel nuevo:

ld -o kernel2.bin -Ttext 0x1000 kernel2.o --oformat binary
cat boot_sect.bin kernel2.bin > os-image2

Mostramos los opcodes de la imagen de disco:

xxd os-image2
00000000: 8816 5c7d bd00 9089 ecbb 5d7d e80b 00e8  ..\}......]}....
00000010: 1a00 e820 01e8 ee00 ebfe 608a 073c 0074  ... ......`..<.t
00000020: 09b4 0ecd 1083 c301 ebf1 61c3 60b4 0eb0  ..........a.`...
00000030: 0acd 10b0 0dcd 1061 c360 b900 0083 f904  .......a.`......
00000040: 741c 89d0 83e0 0f04 303c 397e 0204 07bb  t.......0<9~....
00000050: 6b7c 29cb 8807 c1ca 0483 c101 ebdf bb66  k|)............f
00000060: 7ce8 b6ff 61c3 3078 3030 3030 0060 52b4  |...a.0x0000.`R.
00000070: 0288 f0b6 00b5 00b1 02cd 1372 075a 38f0  ...........r.Z8.
00000080: 7512 61c3 bb9c 7ce8 90ff e89f ff88 e6e8  u.a...|.........
00000090: a7ff eb06 bbac 7ce8 80ff ebfe 4469 736b  ......|.....Disk
000000a0: 2072 6561 6420 6572 726f 7200 496e 636f   read error.Inco
000000b0: 7272 6563 7420 6e75 6d62 6572 206f 6620  rrect number of 
000000c0: 7365 6374 6f72 7320 7265 6164 0000 0000  sectors read....
000000d0: 0000 0000 00ff ff00 0000 9acf 00ff ff00  ................
000000e0: 0000 92cf 0017 00cd 7c00 0060 ba00 800b  ........|..`....
000000f0: 008a 03b4 403c 0074 0b66 8902 83c3 0183  ....@<.t.f......
00000100: c202 ebed 61c3 fa0f 0116 e57c 0f20 c066  ....a......|. .f
00000110: 83c8 010f 22c0 ea1b 7d08 0066 b810 008e  ...."...}..f....
00000120: d88e d08e c08e e08e e8bd 0000 0900 89ec  ................
00000130: e816 0000 00bb 997d e8df fee8 eefe bb00  .......}........
00000140: 10b6 018a 165c 7de8 23ff c3bb 797d 0000  .....\}.#...y}..
00000150: e896 ffff ffe8 a692 ffff ebfe 0053 7461  .............Sta
00000160: 7274 6564 2069 6e20 3136 2d62 6974 2052  rted in 16-bit R
00000170: 6561 6c20 4d6f 6465 004c 616e 6465 6420  eal Mode.Landed 
00000180: 696e 2033 322d 6269 7420 5072 6f74 6563  in 32-bit Protec
00000190: 7465 6420 4d6f 6465 004c 6f61 6469 6e67  ted Mode.Loading
000001a0: 206b 6572 6e65 6c20 696e 746f 206d 656d   kernel into mem
000001b0: 6f72 7900 0000 0000 0000 0000 0000 0000  ory.............
000001c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000001f0: 0000 0000 0000 0000 0000 0000 0000 55aa  ..............U.
00000200: 5589 e55d c390 9090 9090 9090 9090 9090  U..]............
00000210: 5589 e550 b800 800b 0089 45fc 8b45 fcc6  U..P......E..E..
00000220: 0058 e8d9 ffff ff83 c404 5dc3            .X........].

Podemos ver que el segundo sector(nuestro kernel) empiza con los opcodes de la función some_function() no del main(), de este modo cuando se salte a la posición de memoria 0x1000 se ejecutará some_function(), ejecutará la instrucción ret, volverá al bootloader y finalizará.

Para solventar este problema la mayoría de SOs utilizan un pequeño truco, utilizan una rutina en ensamblador que buscará la etiqueta main y la llamará, la etiqueta main es externa a la rutina por lo tanto hay que definirla como extern.

vi kernel_entry.asm
; Ensures that we jump straight into the kernel ’s entry function.
[ bits 32] ; We ’re in protected mode by now , so use 32 - bit instructions.

[ extern main ] ; Declare that we will be referencing the external symbol main

call main ; invoke main () in our C kernel
jmp $ ; Hang forever when we return from the kernel

Compilamos la rutina, cabe destacar que vamos a generar un binario elf, esto es necesario si queremos utilizar la sentencia extern.

nasm kernel_entry.asm -f elf -o kernel_entry.o

Linkamos la rutina de carga y el kernel, el linkador sustituirá la etiqueta main por la dirección dentro del binario donde esté el código correspondiente a la función.

ld -o kernel2.bin -Ttext 0x1000 kernel_entry.o kernel2.o --oformat binary

Este truco funciona porque el linkador respeta el orden en el que se indican los objetos a linkar, en este caso kernel_entry.o kernel2.o, generamos la imagen de disco:

cat boot_sect.bin kernel2.bin > os-image2

La cargamos en Qemu:

qemu-system-x86_64 os-image2

Si te ha gustado el artículo puedes invitarme a un redbull aquí.
Autor: kr0m -- 18/06/2020 02:23:19