链接器错误设置使用内联汇编装载GDT寄存器和LGDT指令 [英] Linker error setting loading GDT register with LGDT instruction using Inline assembly

查看:211
本文介绍了链接器错误设置使用内联汇编装载GDT寄存器和LGDT指令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在编译内核原型的原型(听起来很奇怪,但实际上并不重要),并且在安装时我需要将ASM文件链接到用gcc编译的C文件以获取可用作核心。
问题在于,在从实模式实现交换到保护模式之后,在链接kernel.c和loader.asm脚本时出现此错误:



代码:

  kernel.c :(。text + 0x1e1):未定义的引用`gdtr'
code>

我将解释所有安装过程是如何的,并且我将把代码放在下面。
Instalation步骤:
1:编译asm:
代码:

  nasm -f elf32 loader.asm -o kasm.o 



<2>:编译.c:
代码:

  gcc -m32 -freestanding -c kernel.c -o kc.o 



<3>:链接两者:
代码:

  ld -m elf_i386 -T linker.ld -o kernel kasm.o kc.o 

完整的错误输出是:
代码:

  kc.o:函数`k_enter_protected_mode':
内核.c :( .text + 0x1e1):对`gdtr'的未定义引用

代码如下所示:
代码:

  / * 
*
* kernel.c - version 0.0.1
*此脚本属于分发软件包许可证,此软件包
*可在软件包本身中找到
*由CristianSimón编写的CKA Proyect脚本
* ---
*许可证:GNU GPL v3
*编码器:CristianSimón
* Pro yect:CKA
*
* /
/ *输出定义* /
#define BLACK_BGROUND 0X07 / *黑色背景* /
#define WHITE_TXT 0x07 / *浅灰色在黑色文本上* /
#define GREEN_TXT 0x02 / *黑色文本上浅绿色* /
#define RED_TXT 0x04 / *黑色文本上浅红色* /
#define CYAN_TXT 0x03 / * light青色黑色文字* /
#include
#include< stdint.h>
#include< cpuid.h>
void k_clear_screen();
void k_sleep_3sec();
unsigned int k_printf(char * message,unsigned int line,float color);
void k_malloc(size_t sz);
void k_free(void * mem);
/ * k_clear_screen:清除整个文本屏幕* /
void k_clear_screen()
{
char * vidmem =(char *)0xC00B8000;
unsigned int i = 0;
while(i <(80 * 25 * 2))
{
vidmem [i] ='';
i ++;
vidmem [i] = BLACK_BGROUND;
i ++;
};

$ b $ * k_printf:消息和行#* /
unsigned int k_printf(char * message,unsigned int line,float color)
{
char * vidmem =(char *)0xC00B8000;
unsigned int i = 0;

i =(line * 80 * 2); $ *
$ b while(* message!= 0)
{
if(* message =='\\\
')/ *检查换行* /
{
line ++;
i =(line * 80 * 2);
* message ++;
} else {
vidmem [i] = * message;
* message ++;
i ++;
vidmem [i] = color;
i ++;
};
};

return(1);

$ b $ * b $ b * k_sleep_3sec:简单延迟约3秒,因为是令人讨厌的睡眠,
*的持续时间将会变化
*从系统到系统
* /
void k_sleep_3sec()
{
int c = 1,d = 1;对于(d = 1; d< = 20000; d ++)
{}
} $ b $(c = 1; c< = 20000; c ++)

b / *
*这个内核的malloc和自由函数
*可能会在将来改变,肯定
* /
static unsigned char our_memory [1024 * 1024]; / *为malloc保留1 MB * /
static size_t next_index = 0;
int k_malloc_err;
void k_malloc(size_t sz)
{
void * mem;
if(size of our_memory - next_index< sz){
return NULL;
k_malloc_err = 1;
}

mem =& our_memory [next_index];
next_index + = sz;
返回mem;
}
void k_free(void * mem)
{
/ *我们作弊,并且不释放任何东西。 * /
}
/ * Schreduler * /
/ * --- * /
/ *
*我们的schreduler是一个RTC(Run to Completion)
*将来我们将添加更多的schredulers或更改类型
*,但现在这是我们得到的
* /
int proc_number_count = 0;
void k_schreduler(char * proc_name,unsigned int proc_prior)
{
proc_number_count = proc_number_count + 1;
int proc_number = proc_number_count;
}
void k_enter_protected_mode()
{
__asm__ volatile(cli;
lgdt(gdtr);​​
mov%eax,cr0 ;
或%al,1;
mov cr0,%eax;
jmp 0x8,PModeMain;
PModeMain:);

main函数* /
void k_main()
{
k_clear_screen();
k_printf(Wellcome to,0,WHITE_TXT);
k_printf(CKA!,1,GREEN_TXT);
k_printf(==============>,2,WHITE_TXT);
k_printf(CKA代表装有C的内核,3,WHITE_TXT);
k_printf(Version 0.0.1,=>基于Debashis Barman的工作,4,WHITE_TXT);
k_printf(Contact => assemblyislaw@gmail.com / blueshell@mail2tor.com,5,WHITE_TXT);
k_printf(or in the github repository page,6,WHITE_TXT);
k_sleep_3sec();
k_clear_screen();
/ *这里开始魔术* /
k_printf(!===>开始检查< ===!,0,WHITE_TXT);
k_printf(= - = - = - = - = - = - = - = - = - - ,1,WHITE_TXT);
k_printf([KernelInfo]哇!现在没有内核恐慌!那么,让我们修复...,2,CYAN_TXT);
k_printf([Proc1]检查k_malloc()和k_free()内核函数,3,WHITE_TXT);
k_malloc(15);
if(k_malloc_err == 1){
k_printf([F-ERROR]无法使用k_malloc,你有足够的内存吗?,4,RED_TXT);
while(1){
int error_stayer = 1;

$ b} else {
k_printf([Proc1]找到了k_malloc和k_free,恢复启动...,4,GREEN_TXT);
}
k_enter_protected_mode();
k_printf([KernelInfo]成功切换到保护模式,5,CYAN_TXT);




$ h $这是kernel.c

代码:

  ENTRY(loader)
OUTPUT_FORMAT(elf32-i386)

SECTIONS {
/ *在虚拟
地址空间内核的生命值将为3GB + 1MB,这将在
物理地址空间映射到1MB。 * /
。 = 0xC0100000;
$ b .text:AT(ADDR(.text) - 0xC0000000){
*(.text)
*(。rodata *)
}

.data ALIGN(0x1000):AT(ADDR(.data) - 0xC0000000){
*(。data)
}

.bss:AT(ADDR (.bss) - 0xC0000000){
_sbss =。;
*(公共)
*(。bss)
_ebss =。;
}
}



这是linker.ld



代码:

  global _loader;让入口点对链接器可见。 
extern k_main; _main在别处定义

;设置Multiboot头文件 - 有关详细信息,请参阅GRUB文档
MODULEALIGN equ 1 <<0;对齐页面边界上的已加载模块
MEMINFO equ 1 FLAGS equ MODULEALIGN | MEMINFO;这是Multiboot'flag'字段
MAGIC equ 0x1BADB002; 'magic number'让bootloader找到头部
CHECKSUM equ - (MAGIC + FLAGS);校验和需要

;这是内核空间的虚拟基地址。它必须用于转换虚拟
;地址转换为物理地址,直到启用分页。请注意,这不是
;内核映像本身加载的虚拟地址 - 只需要
;从虚拟地址中减去以获得物理地址。
KERNEL_VIRTUAL_BASE equ 0xC0000000; 3GB
KERNEL_PAGE_NUMBER equ(KERNEL_VIRTUAL_BASE>> 22);内核的4MB PTE的页目录索引。


部分.data
对齐0x1000
BootPage目录:
;此页目录条目标识 - 映射32位物理地址空间的前4MB。
;所有位都清除,除了以下内容:
;位7:PS内核页面是4MB。
;位1:RW内核页面是可读写的。
;位0:P内核页面存在。
;这个条目必须在这里 - 否则内核将在分页为
后立即崩溃;因为它不能获取下一条指令!稍后取消映射此页面可以。
dd 0x00000083
times(KERNEL_PAGE_NUMBER - 1)dd 0;内核空间之前的页面。
;该页面目录条目定义了一个包含内核的4MB页面。
dd 0x00000083
times(1024 - KERNEL_PAGE_NUMBER - 1)dd 0;内核映像后的页面。


部分.text
align 4
MultiBootHeader:
dd MAGIC
dd FLAGS
dd CHECKSUM

;保留初始内核堆栈空间 - 这是16k。
STACKSIZE equ 0x4000

;为链接器设置入口点
loader equ(_loader - 0xC0000000)
全局加载器

_loader:
;注意:在分页设置之前,代码必须是位置无关的,并使用物理
;地址,而不是虚拟的!
mov ecx,(BootPageDirectory - KERNEL_VIRTUAL_BASE)
mov cr3,ecx;加载页面目录基址寄存器。

mov ecx,cr4
或ecx,0x00000010;在CR4中设置PSE位以启用4MB页面。
mov cr4,ecx

mov ecx,cr0
或ecx,0x80000000;将CR0中的PG位置1以启用分页。
mov cr0,ecx

;开始获取内核空间中的指令。
;由于此时eip拥有该命令的物理地址(大约为0x00100000)
;我们需要长时间跳到StartInHigherHalf的正确虚拟地址
;大约为0xC0100000。
lea ecx,[StartInHigherHalf]
jmp ecx;注意:必须是绝对跳跃!

StartInHigherHalf:
;取消映射身份映射的前4MB物理地址空间。不应该需要
;了。
mov dword [BootPageDirectory],0
invlpg [0]

;注意:从现在开始,应该启用分页。第一个4MB的物理地址空间是
;从KERNEL_VIRTUAL_BASE开始映射。一切都链接到这个地址,所以没有更多的
;与位置无关的代码或有趣的业务与虚拟实体地址转换
;应该是必要的。我们现在拥有更高的内核。
mov esp,stack + STACKSIZE;设置堆栈
push eax;通过Multiboot magic number

;传递多引导信息结构 - 警告:这是一个物理地址,可能不是
;在第一个4MB!
push ebx

call k_main;调用内核本身
hlt;暂停机器应该返回内核


部分.bss
align 32
stack:
resb STACKSIZE;在uint64_t边界上保留16k堆栈



这是loader.asm



我试图解决这个问题,它转换高级ASM块中的ASM块并解析gdtr作为参数,但我不明白这最后一个方法
如何解决错误?

解决方案

您的错误:


  kc.o:在函数`k_enter_protected_mode'中:
kernel.c :(。text + 0x1e1):未定义的`gdtr'引用


正因为这一行汇编代码而生成:

 lgdt(gdtr); 

gdtr 是一个内存操作数,代表标签到可以找到GDT记录的内存地址。你没有用这个名字定义的结构。这会导致未定义的引用。



您需要创建包含GDT 大小和长度的 GDT 表。这个记录是由 LGDT 指令加载到 GDT 寄存器的内容。您还没有创建 GDT 表。 gdtr 应该是一个6字节的结构,由GDT的长度减1(存储在一个16位字中)和一个32位线性地址可以找到 GDT 表。






而不是做你想做的事 C 我建议你只需在调用k_main 之前的程序集代码中进行此操作,但在分页设置完成后。

C 代码中完全删除您的 k_enter_protected_mode 函数。然后在程序集文件 loader.asm 中放置此代码以在 StartInHigherHalf 代码的开始加载新的GDT。所以它看起来像:

  StartInHigherHalf:
;设置我们自己的GDT,不能依赖GDT寄存器在bootloader之后有效
;将控制权转移到我们的入口点
lgdt [gdtr];用GDT记录加载GDT寄存器
mov eax,DATA_SEG
mov ds,eax;用数据选择器(第二个参数)重新加载所有数据描述符
mov es,eax
mov gs,eax
mov fs,eax
mov ss,eax

jmp CODE_SEG:.setcs
;做FAR JMP到下一条指令来设置CS和代码选择器,
;设置EIP(指令指针)来抵消setcs
.setcs:

左边是定义GDT表。一个简单的具有所需的NULL描述符和一个32位平面代码和数据描述符的文件可以放在 .data 部分,方法是将其更改为:

  section .data 
对齐0x1000
BootPageDirectory:
;此页目录条目标识 - 映射32位物理地址空间的前4MB。
;所有位都清除,除了以下内容:
;位7:PS内核页面是4MB。
;位1:RW内核页面是可读写的。
;位0:P内核页面存在。
;这个条目必须在这里 - 否则内核将在分页为
后立即崩溃;因为它不能获取下一条指令!稍后取消映射此页面可以。
dd 0x00000083
times(KERNEL_PAGE_NUMBER - 1)dd 0;内核空间之前的页面。
;该页面目录条目定义了一个包含内核的4MB页面。
dd 0x00000083
times(1024 - KERNEL_PAGE_NUMBER - 1)dd 0;内核映像后的页面。


; 32位GDT替换由多引导加载程序创建的一个
;根据多引导规范,我们不能依赖于GDTR
;有效,所以我们需要我们自己的,如果我们打算
;重新加载任何段寄存器(这可能是
;带有保护模式中断的问题)。
align 8
gdt_start:
dd 0;空描述符
dd 0

gdt32_code:
dw 0FFFFh;限制低
dw 0;基数低
db 0;基础中间
db 10011010b;访问
db 11001111b; 32位大小,4kb粒度,限制0xfffff页面
db 0;基本高

gdt32_data:
dw 0FFFFh;限制低(与代码相同)
dw 0;基数低
db 0;基础中间
db 10010010b;访问
db 11001111b; 32位大小,4kb粒度,限制0xfffff页面
db 0;基本高
end_of_gdt:

gdtr:
dw end_of_gdt - gdt_start - 1
;限制(GDT的大小 - 1)
dd gdt_start; GDT的基础

CODE_SEG等于gdt32_code - gdt_start
DATA_SEG等于gdt32_data - gdt_start

现在我们添加了所需的GDT结构并创建了一个名为 gdtr 的记录,可以使用 LGDT 指令。

由于您使用 OSDev 作为资源,我建议查看 GDT 教程,了解有关创建GDT的信息。英特尔手册也是一个很好的信息来源。




其他观察结果



您的 loader.asm 会设置一个Multiboot标头,因此您可以使用符合多重引导的引导程序。当你使用Multiboot兼容的引导加载程序时,你的CPU将被放置在32位保护模式之前,它开始以 _loader 开始运行你的代码。您的问题表明您认为您处于实模式,但实际上您已处于保护模式。使用Mulitboot加载程序时,不需要将 CR0 位0设置为1.它保证已经为1(set)。在我上面的代码中,我在设置完 后删除了它。


I was compiling my prototype of prototype of a kernel (sounds weird, but it really doesnt matter) and in the instalation I need to link the ASM file to a C file compiled with gcc to get a executable that could be used as kernel. The problem is that, after implementing a swap to protected mode from real mode, I get this error at linking the kernel.c and loader.asm scripts:

Code:

kernel.c:(.text+0x1e1): undefined reference to `gdtr'

I will explain how all process of instalation is and I will put the codes below. Instalation steps: 1: Compile asm: Code:

nasm -f elf32 loader.asm -o kasm.o

2: Compile .c : Code:

gcc -m32 -ffreestanding -c kernel.c -o kc.o

3: Link both: Code:

ld -m elf_i386 -T linker.ld -o kernel kasm.o kc.o

The complete error output is: Code:

kc.o: In function `k_enter_protected_mode':
kernel.c:(.text+0x1e1): undefined reference to `gdtr'

The code looks like: Code:

/*
 *
 * kernel.c - version 0.0.1
 * This script is under the license of the distributed package, this license
 * can be found in the package itself
 * Script coded by Cristian Simón for the CKA Proyect
 * ----
 * License: GNU GPL v3
 * Coder: Cristian Simón
 * Proyect: CKA 
 * 
 */
/* Output defines */
#define BLACK_BGROUND 0X07 /* black background */
#define WHITE_TXT 0x07 /* light gray on black text */
#define GREEN_TXT 0x02 /* light green on black text */
#define RED_TXT 0x04 /* light red on black text*/
#define CYAN_TXT 0x03 /*light cyan on black text */
#include <stddef.h>
#include <stdint.h>
#include <cpuid.h>
void k_clear_screen();
void k_sleep_3sec();
unsigned int k_printf(char *message, unsigned int line, float color);
void k_malloc(size_t sz);
void k_free(void *mem);
/* k_clear_screen : to clear the entire text screen */
void k_clear_screen()
{
    char *vidmem = (char *) 0xC00B8000;
    unsigned int i=0;
    while(i < (80*25*2))
    {
        vidmem[i]=' ';
        i++;
        vidmem[i]=BLACK_BGROUND;
        i++;
    };
}

/* k_printf : the message and the line # */
unsigned int k_printf(char *message, unsigned int line, float color)
{
    char *vidmem = (char *) 0xC00B8000;
    unsigned int i=0;

    i=(line*80*2);

    while(*message!=0)
    {
        if(*message=='\n') /* check for a new line */
        {
            line++;
            i=(line*80*2);
            *message++;
        } else {
            vidmem[i]=*message;
            *message++;
            i++;
            vidmem[i]=color;
            i++;
        };
    };

    return(1);
}

/* 
* k_sleep_3sec : to make a simple delay of aprox 3 sec, since is a nasty sleep, 
* duration will vary
* from system to system
*/
void k_sleep_3sec()
{
    int c = 1, d = 1;
    for ( c = 1 ; c <= 20000 ; c++ )
    for ( d = 1 ; d <= 20000 ; d++ )
    {}
}
/* 
* Malloc and free functions for this kernel
* Maybe change in the future, sure
*/
static unsigned char our_memory[1024 * 1024]; /* reserve 1 MB for malloc */
static size_t next_index = 0;
int k_malloc_err;
void k_malloc(size_t sz)
{
    void *mem;
    if(sizeof our_memory - next_index < sz){
        return NULL;
        k_malloc_err = 1;
    }

    mem = &our_memory[next_index];
    next_index += sz;
    return mem;
}
void k_free(void *mem)
{
   /* we cheat, and don't free anything. */
}
/* Schreduler */
/*---*/
/*
* Our schreduler is a RTC (Run to Completion)
* In the future we will add more schredulers or change the type
* but for now this is what we got
*/
int proc_number_count = 0;
void k_schreduler(char *proc_name, unsigned int proc_prior)
{
    proc_number_count = proc_number_count + 1;
    int proc_number = proc_number_count;
}
void k_enter_protected_mode()
{
    __asm__ volatile ("cli;"          
        "lgdt (gdtr);"  
        "mov %eax, cr0;" 
        "or %al, 1;"    
        "mov cr0, %eax;" 
        "jmp 0x8,PModeMain;"
        "PModeMain:");
}
/*main function*/
void k_main() 
{
    k_clear_screen();
    k_printf(" Wellcome to", 0, WHITE_TXT);
    k_printf(" CKA!", 1, GREEN_TXT);
    k_printf("==============>", 2, WHITE_TXT);
    k_printf(" CKA stands for C Kernel with Assembly", 3, WHITE_TXT);
    k_printf(" Version 0.0.1, => based in the job of Debashis Barman", 4, WHITE_TXT);
    k_printf(" Contact => assemblyislaw@gmail.com / blueshell@mail2tor.com", 5, WHITE_TXT);
    k_printf("           or in the github repository page", 6, WHITE_TXT);
    k_sleep_3sec();
    k_clear_screen();
    /* here start the magic */
    k_printf(" !===> Starting Checkup <===!", 0, WHITE_TXT);
    k_printf(" =-=-=-=-=-=-=-=-=-=-=-=-=-=-", 1, WHITE_TXT);
    k_printf("[KernelInfo] Woah! No Kernel Panic for now! Well, lets fix that...", 2, CYAN_TXT);
    k_printf("[Proc1] Checking for k_malloc() and k_free() kernel functions", 3, WHITE_TXT);
    k_malloc(15);
    if (k_malloc_err == 1){
        k_printf("[F-ERROR] Unable to use k_malloc, do you have enough memory?", 4, RED_TXT);
        while(1){
            int error_stayer = 1;
        }

    } else{ 
        k_printf("[Proc1] k_malloc and k_free found, resuming boot...", 4, GREEN_TXT);
    }
    k_enter_protected_mode();
    k_printf("[KernelInfo] Switched to protected mode successfully", 5, CYAN_TXT);

}

This was kernel.c

Code:

ENTRY(loader)
OUTPUT_FORMAT(elf32-i386)

SECTIONS {
   /* The kernel will live at 3GB + 1MB in the virtual
      address space, which will be mapped to 1MB in the
      physical address space. */
   . = 0xC0100000;

   .text : AT(ADDR(.text) - 0xC0000000) {
       *(.text)
       *(.rodata*)
   }

   .data ALIGN (0x1000) : AT(ADDR(.data) - 0xC0000000) {
       *(.data)
   }

   .bss : AT(ADDR(.bss) - 0xC0000000) {
       _sbss = .;
       *(COMMON)
       *(.bss)
       _ebss = .;
   }
}

This was the linker.ld

Code:

global _loader                          ; Make entry point visible to linker.
extern k_main                           ; _main is defined elsewhere

; setting up the Multiboot header - see GRUB docs for details
MODULEALIGN equ  1<<0             ; align loaded modules on page boundaries
MEMINFO     equ  1<<1             ; provide memory map
FLAGS       equ  MODULEALIGN | MEMINFO  ; this is the Multiboot 'flag' field
MAGIC       equ    0x1BADB002     ; 'magic number' lets bootloader find the header
CHECKSUM    equ -(MAGIC + FLAGS)  ; checksum required

; This is the virtual base address of kernel space. It must be used to convert virtual
; addresses into physical addresses until paging is enabled. Note that this is not
; the virtual address where the kernel image itself is loaded -- just the amount that must
; be subtracted from a virtual address to get a physical address.
KERNEL_VIRTUAL_BASE equ 0xC0000000                  ; 3GB
KERNEL_PAGE_NUMBER equ (KERNEL_VIRTUAL_BASE >> 22)  ; Page directory index of kernel's 4MB PTE.


section .data
align 0x1000
BootPageDirectory:
    ; This page directory entry identity-maps the first 4MB of the 32-bit physical address space.
    ; All bits are clear except the following:
    ; bit 7: PS The kernel page is 4MB.
    ; bit 1: RW The kernel page is read/write.
    ; bit 0: P  The kernel page is present.
    ; This entry must be here -- otherwise the kernel will crash immediately after paging is
    ; enabled because it can't fetch the next instruction! It's ok to unmap this page later.
    dd 0x00000083
    times (KERNEL_PAGE_NUMBER - 1) dd 0                 ; Pages before kernel space.
    ; This page directory entry defines a 4MB page containing the kernel.
    dd 0x00000083
    times (1024 - KERNEL_PAGE_NUMBER - 1) dd 0  ; Pages after the kernel image.


section .text
align 4
MultiBootHeader:
    dd MAGIC
    dd FLAGS
    dd CHECKSUM

; reserve initial kernel stack space -- that's 16k.
STACKSIZE equ 0x4000

; setting up entry point for linker
loader equ (_loader - 0xC0000000)
global loader

_loader:
    ; NOTE: Until paging is set up, the code must be position-independent and use physical
    ; addresses, not virtual ones!
    mov ecx, (BootPageDirectory - KERNEL_VIRTUAL_BASE)
    mov cr3, ecx                                        ; Load Page Directory Base Register.

    mov ecx, cr4
    or ecx, 0x00000010                          ; Set PSE bit in CR4 to enable 4MB pages.
    mov cr4, ecx

    mov ecx, cr0
    or ecx, 0x80000000                          ; Set PG bit in CR0 to enable paging.
    mov cr0, ecx

    ; Start fetching instructions in kernel space.
    ; Since eip at this point holds the physical address of this command (approximately 0x00100000)
    ; we need to do a long jump to the correct virtual address of StartInHigherHalf which is
    ; approximately 0xC0100000.
    lea ecx, [StartInHigherHalf]
    jmp ecx                                                     ; NOTE: Must be absolute jump!

StartInHigherHalf:
    ; Unmap the identity-mapped first 4MB of physical address space. It should not be needed
    ; anymore.
    mov dword [BootPageDirectory], 0
    invlpg [0]

    ; NOTE: From now on, paging should be enabled. The first 4MB of physical address space is
    ; mapped starting at KERNEL_VIRTUAL_BASE. Everything is linked to this address, so no more
    ; position-independent code or funny business with virtual-to-physical address translation
    ; should be necessary. We now have a higher-half kernel.
    mov esp, stack+STACKSIZE           ; set up the stack
    push eax                           ; pass Multiboot magic number

    ; pass Multiboot info structure -- WARNING: This is a physical address and may not be
    ; in the first 4MB!
    push ebx

    call  k_main                 ; call kernel proper
    hlt                          ; halt machine should kernel return


section .bss
align 32
stack:
    resb STACKSIZE      ; reserve 16k stack on a uint64_t boundary

This was loader.asm

I tried to solve this transforming the ASM block in an advanced ASM block and parsing gdtr as an argument but I dont understand this last method How can I solve the error? Thanks in advance!

解决方案

Your error:

kc.o: In function `k_enter_protected_mode':
kernel.c:(.text+0x1e1): undefined reference to `gdtr'

Is being generated because of this line of assembly code:

"lgdt (gdtr);"

gdtr is a memory operand and represents a label to a memory address where a GDT record can be found. You don't have such a structure defined with that name. That causes the undefined reference.

You need to create GDT record that contains the size and length of a GDT table. This record is what will get loaded into the GDT register by the LGDT instruction. You also haven't created a GDT table. gdtr should be a 6 byte structure consisting of the length of a GDT minus 1 (stored in a 16-bit word) and a 32-bit linear address where the GDT table can be found.


Rather than doing what you want in C I recommend just doing this in your assembly code prior to call k_main but after paging is set up.

Remove your k_enter_protected_mode function altogether in the C code. Then in the assembly file loader.asm place this code to load a new GDT at the start of your StartInHigherHalf code. So it would look like:

StartInHigherHalf:
    ; Set our own GDT, can't rely GDT register being valid after bootloader
    ; transfers control to our entry point
    lgdt [gdtr]         ; Load GDT Register with GDT record
    mov eax, DATA_SEG
    mov ds, eax         ; Reload all the data descriptors with Data selector (2nd argument)
    mov es, eax
    mov gs, eax
    mov fs, eax
    mov ss, eax

    jmp CODE_SEG:.setcs
                        ; Do the FAR JMP to next instruction to set CS with Code selector, and
                        ;    set the EIP (instruction pointer) to offset of setcs
.setcs:

The only thing left is to define the GDT table. A simple one with a required NULL descriptor and a flat 32-bit code and data descriptor can be placed in your .data section by changing it to this:

section .data
align 0x1000
BootPageDirectory:
    ; This page directory entry identity-maps the first 4MB of the 32-bit physical address space.
    ; All bits are clear except the following:
    ; bit 7: PS The kernel page is 4MB.
    ; bit 1: RW The kernel page is read/write.
    ; bit 0: P  The kernel page is present.
    ; This entry must be here -- otherwise the kernel will crash immediately after paging is
    ; enabled because it can't fetch the next instruction! It's ok to unmap this page later.
    dd 0x00000083
    times (KERNEL_PAGE_NUMBER - 1) dd 0                 ; Pages before kernel space.
    ; This page directory entry defines a 4MB page containing the kernel.
    dd 0x00000083
    times (1024 - KERNEL_PAGE_NUMBER - 1) dd 0  ; Pages after the kernel image.


; 32-bit GDT to replace one created by multiboot loader
; Per the multiboot specification we Can't rely on GDTR
; being valid so we need our own if we ever intend to
; reload any of the segment registers (this may be an
; issue with protected mode interrupts).
align 8
gdt_start:
    dd 0                ; null descriptor
    dd 0

gdt32_code:
    dw 0FFFFh           ; limit low
    dw 0                ; base low
    db 0                ; base middle
    db 10011010b        ; access
    db 11001111b        ; 32-bit size, 4kb granularity, limit 0xfffff pages
    db 0                ; base high

gdt32_data:
    dw 0FFFFh           ; limit low (Same as code)
    dw 0                ; base low
    db 0                ; base middle
    db 10010010b        ; access
    db 11001111b        ; 32-bit size, 4kb granularity, limit 0xfffff pages
    db 0                ; base high
end_of_gdt:

gdtr:
    dw end_of_gdt - gdt_start - 1
                        ; limit (Size of GDT - 1)
    dd gdt_start        ; base of GDT

CODE_SEG equ gdt32_code - gdt_start
DATA_SEG equ gdt32_data - gdt_start

We've now added the required GDT structure and created a record called gdtr that can be loaded with the LGDT instruction.

Since you are using OSDev as a resource, I recommend looking at the GDT tutorial for information on creating a GDT. The Intel manuals are also an excellent source of information.


Other Observations

Your loader.asm sets up a Multiboot header so it is a good bet you are using a Multiboot compliant bootloader. When you use a Multiboot compliant bootloader your CPU will be placed into 32-bit protected mode before it starts running your code starting at _loader. Your question suggests that you think you are in real mode, but you are actually already in protected mode. With a Mulitboot loader it isn't necessary to set CR0 bit 0 to a value of 1. It is guaranteed to already be 1 (set). In my code above I have removed it after setting up the GDT.

这篇关于链接器错误设置使用内联汇编装载GDT寄存器和LGDT指令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆