alpaca2unix blog

UNIXやGnu/Linuxの勉強と備忘録です。

リンカの仕方、Makefileの書き方が分からないんです。

f:id:alpaca2unix:20170324090156p:plain

 

boot.S

    //    Declare constants for the multiboot header.
    .set ALIGN,         1<<0                    # align loaded modules on page boundaries
    .set MEMINFO,        1<<1                    # provide memory map
    .set FLAGS,            ALIGN | MEMINFO            # this is the multiboot 'flag' field
    .set MAGIC,            0x1BADB002                # 'magic number' lets bootloader find the header
    .set CHECKSUM,        -(MAGIC + FLAGS)        # checksum of above, to prove we are multiboot

   /*
    *    Declare a multiboot header that marks the program as a kernel.
    *    These are mafic Values that are documented in the multiboot standerd.
    *    The bootloader will search for this signature in the first 8KiB of the Kernel file, aligned at a 32-bit boundary.
    *    The signature is in its own section so the header can be forced to be within the first 8KiB of the Kernel file.
    */

    .section         .multiboot
    .align            4
    .long            MAGIC
    .long            FLAGS
    .long            CHECKSUM

   /*
    *    The multiboot standard does not define the value of the stack pointer register (esp)
    *    and it is up to the kernel to provide a stack.
    *    This allocates room for a small stack by creating a symbol at the bottom of it,
    *    then allocating 16384bytes for it, and finaly creating a symbol at the top.
    *    The stack grows downwards on x86.
    *    The stack is its own section so it can be marked nobits,
    *    which means the kernel file is smaller because it does not contain an uninitialized stack.
    *    The stack on x86 must be 16-byte aligned according to the System V ABI and de-facto extentions.
    *    The compiler will assume the stack is properly aligned and failure to align the stack will result in undefined behavior.
    */

    .section     .bss
    .align        16
stack_bottom:
    .skip        16384            # 16 KiB
stack_top:

   /*
    *    The linker scripts specifies _start as the entry point to the kernel and
    *    the bootloader will jump to this position once the kernel has been loaded.
    *    It desen't make sense to return from this function as the bootloader is gone.
    */

    .section    .text
    .global        _start
    .type        _start, @function
    _start:

   /*
    *    The bootloader has loaded us int 32-bit protected mode on a x86 machine.
    *    Interrupts ar disabled. Paging is disabled/
    *    The processor state is as defined in the multiboot standard.
    *    The kernel has full contorl of the CPU.
    *    The kernel can only make use of hardware features
    *    and any code it provides as part of itself.
    *    There's no printf function,
    *    unless the kernel provides its own <stdio.h> header and a printf implementation.
    *    There are no security restricions, no safeguards, no debugging mechanism,
    *    only what the kernel provides itself.
    *    It has absolute and complete power over the machine.
    *
    *    To set up a stack, we set the esp register to point to the top of our stack
    *    (as it grows downwards on x86 systems).
    *    This is necessarily done in assembly as languages such as C cannot function without a stack.
    */

    mov        $stack_top, %esp

   /*
    *    This is a good place to initialize crucial processor state before the high-level kernel is entered.
    *    It's best to minimize the earily environment where crucial features are offline.
    *    Note that the processor is not fully initialized set extensions are not initialized yet:
    *    Features such as floating point instructions and instruction set extensions are not initialized yet.
    *    The GDT should be loaded here.
    *    Paging should be enabled here.
    *    C++ features such as global constructors and exceptions will require runtime support to work as well.
    */
    
   /*
    *    Enter the high-level kernel. The ABI requires the stack is 16-byte
    *    aligned at the time of call instruction (which afterwards pushes the return pointer of size 4 bytes).
    *    The stack was originally 16-byte aligned above and we've since pushed a multiple of 16 bytes to the stack since
    *    (pushed 0 bytes so far) and the alignment is thus preserved and the call is well defined.
    */
    
    call kernel_main
    
   /*
    *    If the system has nothing more to do,
    *    put the computer into an infinite loop. to that:
    *    1) Disable interrupts with cli (clear interrupt enable in eflags)/
    *       They are already disabled by the bootloader, so this is not needed.    
    *       Mind that you might later enable interrupts and return from kernel_main (which is sort of nonsensical to do).
    *    2) Wait for the next interrupt to arrive with hlt (hlt instruction).
    *       Since they are disabled, this will lock up the computer.
    *    3) Jump to the hlt instruction if it ever wakes up due to a non-maskable interrupt occurring
    *       or due to system management mode.
    */
    
    cli
1:    hlt
    jmp        1b
    
//    Set the size of the _start symbol to the current location '.' minus its start.
//    This is useful when debugging or when you implement call tracing
    
.size     _start, . - _start

# as boot.s -o boot.o

これで、boot.oをゲット!

kernel.c

/* Surely you will remove the processor conditionals and this comment
   appropriately depending on whethre or not you use C++. */
#if    !defined(__cplusplus)
#include <stdbool.h>    /* C doesm't booleans by default. */
#endif
#include <stddef.h>
#include <stdint.h>

/* Check if the compiler thinks we are targeting the wrong operating system    */
#if defined(__linux__)
/* error "You are not using a cross-compiler, you will most certainly run into trouble" */
#endif

/* This tutorial will only work for the 32-bit ix86 targets. */
#if !defined(__i386__)
/* error "This tutorial needs to be compiled with a ix86-elf compiler" */
#endif

/* Hardware text mode color constants */
enum vga_color {
    VGA_COLOR_BLACK = 0,
    VGA_COLOR_BLUE = 1,
    VGA_COLOR_GREEN = 2,
    VGA_COLOR_CYAN = 3,
    VGA_COLOR_RED = 4,
    VGA_COLOR_MAGENTA = 5,
    VGA_COLOR_BROWN = 6,
    VGA_COLOR_LIGHT_GREY = 7,
    VGA_COLOR_DARK_GREY = 8,
    VGA_COLOR_LIGHT_BLUE = 9,
    VGA_COLOR_LIGHT_GREEN = 10,
    VGA_COLOR_LIGHT_CYAN = 11,
    VGA_COLOR_LIGHT_RED = 12,
    VGA_COLOR_LIGHT_MAGENTA = 13,
    VGA_COLOR_LIGHT_BROWN = 14,
    VGA_COLOR_WHITE = 15,
};

static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg) {
    return fg | bg << 4;
}

static inline uint16_t vga_entry(unsigned char uc, uint8_t color) {
    return (uint16_t) uc | (uint16_t) color << 8;
}

size_t strlen(const char* str) {
    size_t len = 0;    
    while (str[len])
        len++;
    return len;
}

static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 25;

size_t terminal_row;
size_t terminal_column;
uint8_t terminal_color;
uint16_t* terminal_buffer;

void terminal_initialize(void) {
    terminal_row = 0;
    terminal_column = 0;
    terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);
    terminal_buffer = (uint16_t*) 0xB8000;
    for (size_t y = 0; y < VGA_HEIGHT; y++) {
        for (size_t x = 0; x < VGA_WIDTH; x++) {
            const size_t index = y * VGA_WIDTH + x;
            terminal_buffer[index] = vga_entry(' ', terminal_color);
        }
    }
}
 
void terminal_setcolor(uint8_t color) {
    terminal_color = color;
}
 
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y) {
    const size_t index = y * VGA_WIDTH + x;
    terminal_buffer[index] = vga_entry(c, color);
}
 
void terminal_putchar(char c) {
    terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
    if (++terminal_column == VGA_WIDTH) {
        terminal_column = 0;
        if (++terminal_row == VGA_HEIGHT)
            terminal_row = 0;
    }
}
 
void terminal_write(const char* data, size_t size) {
    for (size_t i = 0; i < size; i++)
        terminal_putchar(data[i]);
}
 
void terminal_writestring(const char* data) {
    terminal_write(data, strlen(data));
}
 
#if defined(__cplusplus)
extern "C" /* Use C linkage for kernel_main. */
#endif
void kernel_main(void) {
    /* Initialize terminal interface */
    terminal_initialize();
 
    /* Newline support is left as an exercise. */
    terminal_writestring("Hello, kernel World!\n");
}

# gcc -c kernel.c -o kernel.o -std=gnu99 -ffreestanding -O2 -Wall -Wextra
これで、kernel.oをゲット!

boot.oとkernel.oをリンクして、AlpacaOS.imgを作ることができると、qemuで起動できるのですが、ソースは書けてもリンク方法が分からないでいます。いま調べているところなのですが、Makefileの勉強はやっぱりした方がいいですね。これから先、プログラミングをしていく上で確実に必要な感じ。あと、ちょっとなのになー。早く、qemuでMy_kernelの起動を見たいのですが・・・ちなみにこのカーネルは「OS自作入門」のkernelとは異なります。

kayo .~*