2014-01-17 40 views
0

我在使用gcc編譯器鏈接兩個.o文件時遇到問題。我有我的引導程序,它是用程序集和C++對象編寫的,它是用來加載的。當連接這兩個文件,編譯器會抱怨未定義參考kernel_main,我想不通爲什麼...簡單內核編譯問題

//kernel.cpp 
    #include <stddef.h> //we can use it: it doesnt use any platform-related api    functions 
    #include <stdint.h> //include it to get int16_t and some integer types 

    /* Hardware text mode color constants. */ 
    enum vga_color{ 

    COLOR_BLACK = 0, 
    COLOR_BLUE = 1, 
    COLOR_GREEN = 2, 
    COLOR_CYAN = 3, 
    COLOR_RED = 4, 
    COLOR_MAGENTA = 5, 
    COLOR_BROWN = 6, 
    COLOR_LIGHT_GREY = 7, 
    COLOR_DARK_GREY = 8, 
    COLOR_LIGHT_BLUE = 9, 
    COLOR_LIGHT_GREEN = 10, 
    COLOR_LIGHT_CYAN = 11, 
    COLOR_LIGHT_RED = 12, 
    COLOR_LIGHT_MAGENTA = 13, 
    COLOR_LIGHT_BROWN = 14, 
    COLOR_WHITE = 15, 
}; 

uint8_t make_color(enum vga_color fg, enum vga_color bg) 
{ 
    return fg | bg << 4; 
} 

uint16_t make_vgaentry(char c, uint8_t color) 
{ 
    uint16_t c16 = c; 
    uint16_t color16 = color; 
    return c16 | color16 << 8; 
} 

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

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

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

void terminal_initialize() 
{ 
    terminal_row = 0; 
    terminal_column = 0; 
    terminal_color = make_color(COLOR_LIGHT_GREY, 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] = make_vgaentry(' ', 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] = make_vgaentry(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_writestring(const char* data) 
{ 
    size_t datalen = strlen(data); 
    for (size_t i = 0; i < datalen; i++) 
     terminal_putchar(data[i]); 
} 

extern "C" void kernel_main() 
{ 
terminal_initialize(); 
terminal_writestring("wellcome to my first operating system!"); 
for(;;); 
} 

繼承人我總成:

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

    ; Declare a header as in the Multiboot Standard. We put this into a special 
    ; section so we can force the header to be in the start of the final program. 
    ; You don't need to understand all these details as it is just magic values that 
    ; is documented in the multiboot standard. The bootloader will search for this 
    ; magic sequence and recognize us as a multiboot kernel. 
    section .multiboot 
    align 4 
     dd MAGIC 
     dd FLAGS 
     dd CHECKSUM 

     ; Currently the stack pointer register (esp) points at anything and using it may 
     ; cause massive harm. Instead, we'll provide our own stack. We will allocate 
     ; room for a small temporary stack by creating a symbol at the bottom of it, 
     ; then allocating 16384 bytes for it, and finally creating a symbol at the top. 
     section .bootstrap_stack 
     align 4 
     stack_bottom: 
     times 16384 db 0 
     stack_top: 

     ; The linker script specifies _start as the entry point to the kernel and the 
     ; bootloader will jump to this position once the kernel has been loaded. It 
     ; doesn't make sense to return from this function as the bootloader is gone. 
     section .text 
     global _start 
     _start: 
    ; Welcome to kernel mode! We now have sufficient code for the bootloader to 
    ; load and run our operating system. It doesn't do anything interesting yet. 
    ; Perhaps we would like to call printf("Hello, World\n"). You should now 
    ; realize one of the profound truths about kernel mode: There is nothing 
    ; there unless you provide it yourself. There is no printf function. There 
    ; is no <stdio.h> header. If you want a function, you will have to code it 
    ; yourself. And that is one of the best things about kernel development: 
    ; you get to make the entire system yourself. You have absolute and complete 
    ; power over the machine, there are no security restrictions, no safe 
    ; guards, no debugging mechanisms, there is nothing but what you build. 

    ; By now, you are perhaps tired of assembly language. You realize some 
    ; things simply cannot be done in C, such as making the multiboot header in 
    ; the right section and setting up the stack. However, you would like to 
    ; write the operating system in a higher level language, such as C or C++. 
    ; To that end, the next task is preparing the processor for execution of 
    ; such code. C doesn't expect much at this point and we only need to set up 
    ; a stack. Note that the processor is not fully initialized yet and stuff 
    ; such as floating point instructions are not available yet. 

    ; To set up a stack, we simply set the esp register to point to the top of 
    ; our stack (as it grows downwards). 
    mov esp, stack_top 

    ; We are now ready to actually execute C code. We cannot embed that in an 
    ; assembly file, so we'll create a kernel.c file in a moment. In that file, 
    ; we'll create a C entry point called kernel_main and call it here. 
    extern kernel_main 
    call kernel_main 

    ; In case the function returns, we'll want to put the computer into an 
    ; infinite loop. To do that, we use the clear interrupt ('cli') instruction 
    ; to disable interrupts, the halt instruction ('hlt') to stop the CPU until 
    ; the next interrupt arrives, and jumping to the halt instruction if it ever 
    ; continues execution, just to be safe. 
    cli 
     .hang: 
    hlt 
    jmp .hang 

我的鏈接腳本linker.ld

/* The bootloader will look at this image and start execution at the symbol 

指定爲入口點。 */ ENTRY(_start)

/* Tell where the various sections of the object files will be put in the final 
    kernel image. */ 
SECTIONS 
{ 
    /* Begin putting sections at 1 MiB, a conventional place for kernels to be 
     loaded at by the bootloader. */ 
    . = 1M; 

    /* First put the multiboot header, as it is required to be put very early 
     early in the image or the bootloader won't recognize the file format. 
     Next we'll put the .text section. */ 
    .text BLOCK(4K) : ALIGN(4K) 
    { 
     *(.multiboot) 
     *(.text) 
    } 

    /* Read-only data. */ 
.rodata BLOCK(4K) : ALIGN(4K) 
{ 
    *(.rodata) 
} 

/* Read-write data (initialized) */ 
.data BLOCK(4K) : ALIGN(4K) 
{ 
    *(.data) 
} 

/* Read-write data (uninitialized) and stack */ 
.bss BLOCK(4K) : ALIGN(4K) 
{ 
    *(COMMON) 
    *(.bss) 
    *(.bootstrap_stack) 
} 

/* The compiler may produce other sections, by default it will put them in 
    a segment with the same name. Simply add stuff here as needed. */ 

} 這兩個代碼的編譯和我沒有得到任何錯誤,直到我將它們連接在一起。任何幫助,將不勝感激 :)。 編輯:我的gcc運行在64位Ubuntu的虛擬框中。我編譯彙編文件與

nasm -felf boot.asm -o boot.o 

而對於kernel.cpp我用

g++ -m32 -c kernel.cpp -o kernel.o -ffreestanding -O2 -Wall -Wextra -fno-exceptions -fno-rtti 

然後我用命令將它們鏈接

gcc -T linker.ld -o myos.bin -ffreestanding -O2 -nostdlib boot.o kernel.o -lgcc -m32 
+0

其中kernel_main已定義?它看起來不像kernel.cpp,好像你需要在kernel.cpp中創建一個kernel_main,或者像彙編註釋所示的那樣在一個名爲kernel.c的新文件中創建。 – pje

回答

2

你的自舉程序調用一個叫做kernel_main的函數,但是你沒有定義這樣的函數。也許你的main()函數應該是kernel_main()?

但是,請記住,C++編譯器會破壞符號(如函數名稱)。因此,使main/kernel_main函數extern "C"或在kernel.o文件上使用objdump或nm來找出C++編譯器賦予這些函數的函數名稱,並從您的bootloader中調用該名稱。

+0

是的。我改變了這些,現在我用32位模式下的gcc編譯,但我怎樣才能在32位模式下鏈接它們? -m32不起作用...現在它給我kernel.o的不兼容錯誤架構與i386 x86-64輸出不兼容。它說boot.o程序集文件的錯誤。 –

+0

我不知道你已經運行了哪些命令來編譯/彙編和鏈接。 - 以及你得到錯誤的地方,所以請發佈如何:1.組裝你的bootloader 2.編譯.cpp文件,3.將它們鏈接在一起4.輸出錯誤 – nos

+0

已經將編譯參數添加到我的文章。 Bootloader被認爲是GRUB。 –

0

看起來你需要重命名你main()在kernel.cpp裏面到kernel_main()。程序集文件試圖引用並調用kernel_main,但是您尚未在您的cpp文件中的任何位置定義該文件。鏈接器報告錯誤,因爲它找不到kernel_main(),並且找不到kernel_main(),因爲它不存在。