c++ - 简单的内核编译问题

标签 c++ assembly cross-compiling

我在使用 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

指定为入口点。 */ 进入(_开始)

/* 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

最佳答案

您的引导加载程序调用一个名为 kernel_main 的函数,但您没有定义这样一个函数。也许您的 main() 函数应该是 kernel_main() ?

但是请记住,C++ 编译器会破坏符号(例如函数名)。所以让你的 main/kernel_main 函数 extern "C",或者在 kernel.o 文件上使用 objdump 或 nm 来找出 C++ 编译器给这些函数的函数名称,并从你的 Bootstrap 。

关于c++ - 简单的内核编译问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21194358/

相关文章:

C++返回指向成员的指针

assembly - 用 x86 Assembly 编写的 Bootloader 如何写入 USB 闪存驱动器并同时支持 BIOS 和 UEFI?

c++ - 为 arm-none-eabi-gcc 交叉编译库

python - 如何为 ARM 交叉编译 python 包(例如 Numpy)

compilation - 如何使用 libmp3lame 构建 LGPL 版本的 ffmpeg?

c++ - 防止通过派生类调用基类中的静态方法?

c++ - std::map 初始化列表构造函数

windows - 汇编中的非法指令

c++ - 在makefile中规避重复库头和typedef的方法

assembly - x86 程序集 pushl/popl 不适用于 "Error: suffix or operands invalid"