Foro de elhacker.net

Programación => Programación General => Mensaje iniciado por: ~ en 4 Julio 2012, 14:48 pm



Título: Robusteciendo las Prácticas de Programación con Estándares de Lenguajes y C
Publicado por: ~ en 4 Julio 2012, 14:48 pm
El kernel que he escrito y al que me refiero es el del Nivel 1 en este repositorio:

>> Lista de Núcleos de Entrenamiento (solamente Nivel 1 por ahora) (http://devel.no-ip.org/misc/computer/programming/os/index.html) <<


El kernel al que me refiero ha llegado a lo que yo llamo el Nivel 1 de desarrollo.

Eso significa que es el nivel mínimo de usabilidad de un núcleo, que pueda serle útil a un usuario.

El nivel mínimo funcionalmente utilizable (Nivel 1) es poder leer programas del directorio raíz de un floppy mediante sus nombres cortos de archivo, enlazado dinámico básico, funciones de sistema exportadas para los programas, entrada de usuario con un teclado PS/2 y una consola de comandos muy básica.

-------------------------

En lo personal, escribir en ensamblador me es bastante fácil, y sería perfecto si el ensamblador x86 se usara por el resto de plataformas.

Pero ya que ese no es el caso, existen lenguajes como C, que tienen convenciones definidas para codificar sus tipos de datos, y llamadas a rutinas, pasado de parámetros, etc.

Este es una pequeña coyuntura clave: Escribí un programa en C que usa las funciones exportardas del kernel. Al comparar la forma en que se escribe un programa en C, su código generado y la falta de convenciones que usan las funciones de mi kernel actual, me llego a dar cuenta que puedo terminar de robustecer mi kernel si insisto en escribir programas en C y en ensamblador, e incluso reescribir el kernel en C y en ensamblador para comparar el código generado, y empezar a adoptar las convenciones, para obtener una portabilidad e interoperabilidad máximas.

Ya que los estándares de C y las convenciones necesarias son muy voluminosas y técnicas, esto no puede tomarse como una adivinanza, así que voy a tener que reunir esos documentos de especificaciones y traducirlos para estudiarlos a profundidad.

También voy a necesitar aprender a programar en C y en ensamblador para más de una plataforma, para poder ser capaz de crear código portable en todos los niveles. Debería por ejemplo aprender una plataforma ARM además de x86-16, x86-32 y x86-64.

-------------------------

Al usar las funciones del kernel como las tengo actualmente, me doy cuenta de que necesito especificar registros del CPU en el código de C a compilarse por GCC. Esto puede hacerse portable si uso mi lenguaje y compilador Real C, pero en GCC eso es un problema de portabilidad de plataformas enorme.

Este es el primer punto en el que voy a robustecer el kernel: Voy a aprender cómo codificar cualquier tipo de parámetros de funciones y variables de todo tipo.

El código de un programa escrito en C requiere más trabajo y más archivos, algo como esto:


"include/external_exported_functions.inc"
Código:
;Nota: Este archivo es para ser usado por programas externos,
;      para que sepan dónde encontrar las funciones que desean
;      usar, desde la tabla de funciones exportadas del kernel.

;Estos son los ÍNDICES de cada puntero en la tabla de exportes.
;Las convertimos a posiciones de 4 bytes para programas de 32 bits,
;y a posiciones de 8 bytes para programas de 64 bits.
;;

                           clrscr@KernelCore equ 0
           console_kprint_visible@KernelCore equ 1
           console_doCRLFeffect@KernelCore   equ 2
cmdLine__findFirstBlankSpace@KernelCore      equ 3
cmdLine__findFirstNonBlankSpace@KernelCore   equ 4
adjustCommandLineFATname@KernelCore          equ 5
getCommandLineArgsCount@KernelCore           equ 6
copyCommandLineArg@KernelCore                equ 7
cmdLine__charTypeAt@KernelCore               equ 8
cmdLine__skipNonBlankSpaces@KernelCore       equ 9
cmdLine__skipBlankSpaces@KernelCore          equ 10






"exports.asm"
Código:

;Aquí anteponemos un guión bajo a todas las
;funciones de ensamblador a ser llamadas desde C;
;esto es llamado "mangling" o decoración, y es
;una convención de C/el enlazador, para enlazar
;funciones externas al código de C, o entre módulos,
;especialmente entre lenguajes que no comparten las
;convenciones de C, como el caso del ensamblador.
;;

%include "include/external_exported_functions.inc"


global myImportsFunctionsTable
       global _clrscr
       global _console_kprint_visible
       global _console_doCRLFeffect
       global _adjustCommmandLineFATname
       global _getCommandLineArgsCount
       global _copyCommandLineArg
       global _cmdstrbuff


myImportsFunctionsTable:
                   ImportsCount dd 6
                           _clrscr dd clrscr@KernelCore
           _console_kprint_visible dd console_kprint_visible@KernelCore
           _console_doCRLFeffect   dd console_doCRLFeffect@KernelCore
_adjustCommandLineFATname          dd adjustCommandLineFATname@KernelCore
_getCommandLineArgsCount           dd getCommandLineArgsCount@KernelCore
_copyCommandLineArg                dd copyCommandLineArg@KernelCore






_cmdstrbuff times 1024 db 0



"header.asm"
Código:

;Aquí hemos optado por establecer a APPBASE
;simplemente a la ubicación actual en el código,
;representada por el signo de dólar, $.
;
;Ahora no es nuestro código en ensamblador el que
;debe encargarse de establecer la dirección base
;del ejecutable, sino que el enlazador LD, por el
;hecho de que estamos usando varios módulos por
;separado, y en esta complejidad solamente el enlazador
;puede llevar a cabo esto eficientemente.
;;
 ;APPBASE equ 1048576*2
 APPBASE equ $

extern myImportsFunctionsTable

;Nuestro kernel debe leer esta dirección, saltar
;a esta y luego saltarse la cabecera del ejecutable:
;;
 IntendedBaseAddress  dq APPBASE
 NumberOfFieldsBelow  dq 7
 CommandLinePtr       dq 0
 CommandLineLength    dq 0
 CommandLineParamsPtr dq 0
 KernelFnExportsTable dq 0
 KernelVrExportsTable dq 0
 AppFnImportsTable    dq myImportsFunctionsTable
 AppVrImportsTable    dq 0


"start.asm"
Código:
;Si hay código presente, debemos usar
;BITS 32:
;;
 bits 32


;La etiqueta llamada "start"
;debe ser globalmente conocida, para que
;LD o más bien otros módulos de este
;programa pueda encontrarla adecuadamente.
;
;Este es el punto de entrada de nuestra
;aplicación.
;
;"start" es el símbolo que el script LD
;para esta aplicación busca para establecer
;el punto de entrada de la misma, y debe
;declararse como global.
;;
 global start



;Estas son las funciones del kernel, con
;un guión bajo al inicio ("mangled") para
;que C pueda encontrarlas.
;
;Indicamos que son externas porque estos símbolos
;están definidos en "exports.asm":
;;
 extern _clrscr
 extern _console_kprint_visible
 extern _console_doCRLFeffect
 extern _adjustCommmandLineFATname
 extern _getCommandLineArgsCount
 extern _copyCommandLineArg






start:

;Limpiamos la pantalla como siempre:
;;
 call dword[_clrscr]

;Declaramos el símbolo "_main" como
;externo, ya que se encuentra en "main.c",
;y llamamos a main, que está declarado como
;int main(void), al menos de acuerdo a nuestro
;kernel actual y también para esta aplicación
;específica.
;
;Debemos aprender a implementar
;int main(int argc, char **argv)
;por nosotros mismos:
;;
 extern _main
 call _main


;Aquí le devolvemos el control
;al kernel:
;;
 ret



"main.c"
Código:
//Ya que nuestras funciones importadas
//están en una tabla de punteros de funciones,
//debemos declarar la función como un puntero
//a una función, para generar el código adecuado:
///
 extern void (*console_doCRLFeffect)(void);


int main(void)
{
 //Aquí colocaremos una lista de instrucciones
 //para demostrar la sintaxis de AT&T de GAS
 //para ensamblador inline, de todas las
 //combinaciones de opcodes y expresiones que
 //sea útil recordar:
 ///
  __asm__ __volatile__ ("pushl %eax");
  __asm__ __volatile__ ("popl %eax");


 //Llamamos nuestra función proveniente
 //del kernel. Ya que esta no toma parámetros,
 //podemos usarla libremente sin temor a
 //un mal manejo de los parámetros y la pila.
 //Pero esto será un problema para funciones de
 //la librería del kernel que usen parámetros, a
 //menos que estén totalmente de conformidad a
 //las convenciones de C (cdecl):
 ///
  console_doCRLFeffect();

 //Devolvemos 0 desde main, que en las x86
 //lo coloca en AX/EAX/RAX:
 ///
  return 0;
}


"link.ld"
Código:
OUTPUT_FORMAT("binary")
ENTRY(start)
phys = 0x0000000000200000;
SECTIONS
{
  .text phys : AT(phys) {
    code = .;
    *(.text)
    *(.rodata)
    . = ALIGN(0);
  }
  .data : AT(phys + (data - code))
  {
    data = .;
    *(.data)
    . = ALIGN(0);
  }
  .bss : AT(phys + (bss - code))
  {
    bss = .;
    *(.bss)
    . = ALIGN(0);
  }
  end = .;
}


"build.bat"
Código:
nasm -f aout -o header.o header.asm
nasm -f aout -o start.o start.asm
gcc -Wall -O -fstrength-reduce -fomit-frame-pointer -finline-functions -nostdinc -fno-builtin -c -o main.o main.c
nasm -f aout -o exports.o exports.asm

ld -T link.ld -o kern0 header.o start.o main.o exports.o


En "build.bat" (o un script Bash de Linux), el orden en el que se compilan los diferentes componentes es arbitrario; pero el orden en que se enlazan con LD es físicamente el mismo que el especificado en la línea de comandos, y debe estar en el orden mostrado aquí (cabecera del programa, inicializador en ensamblador, programa principal, otros archivos de código opcionales, y la tabla de exportes).

link.ld está configurado para generar un archivo binario crudo, y usar una alineación de 0 entre secciones.

Con un script más personalizado, podríamos indicar un ejecutable crudo pero encargarnos nosotros mismos de generar a mano el formato del ejecutable (PE, ELF, AOUT, MZ-EXE, etc.).

Ya que estamos usando el formato AOUT para crear el código objeto de cada pieza del programa mostrado aquí, también deberíamos aprender a manejar dicho formato a bajo nivel, para eventualmente crear herramientas propias y llevar a cabo tareas de depuración y desarrollo más potentes.


Título: Re: Robusteciendo las Prácticas de Programación con Estándares de Lenguajes y C
Publicado por: Khronos14 en 4 Julio 2012, 15:36 pm
Yo también estoy empezando con esto de programar tu propio Sistema Operativo, cree un programa en C que permite crear imágenes de disquetes con el formato FAT12. Después hice un bootloader en ASM que ocupa 512 bytes y lo metí en el sector 0 de mi imagen floppy. Y ahora ando a vueltas para cargar el stage2 del bootloader del sistema de archivos FAT12, y me va a llevar tiempo...

Saludos.