リンカの仕方、Makefileの書き方が分からないんです。
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 .~*