c - 查看可执行二进制文件的链接器 'uses' 的确切内存范围

标签 c x86 linker kernel osdev

我正在做一些 OSdev,并且我一直在尝试在我的内核中实现内存管理。我从物理内存管理器开始(这是一个 32 位操作系统)。这个想法是保留一个位表,我们在其中为每个 4K 物理内存块分配一个位。如果该位为“1”,则该 block 正在使用,如果为“0”,则未使用。我认为这个表应该位于内核之后。这是我的内核代码(最小):

#include<stdint.h> 

#define PMMAP 0x1000 //This contains information from int 15h/E820
#define BLOCK_SIZE 4096
#define SECTOR_SIZE 512
#define SECTORS_PER_BLOCK 8
#define BLOCK_SIZE_B 12
#define SECTOR_SIZE_B 9
#define SECTORS_PER_BLOCK_B 3

void  pmmngr_init(uint32_t kernelsize,uint32_t mapentrycount);
uint32_t* _physical_memory_table;

void kmain(uint32_t size,uint32_t mmapentrycount)   //This size is passed on by the bootloader, where it has a filesystem driver-ish code that determines this. The size unit is 512 bytes
{
    pmmngr_init(size,mmapentrycount);
    return;
}
struct mmap_entry {
    uint32_t  startLo;
    uint32_t  startHi;
    uint32_t  sizeLo;
    uint32_t  sizeHi;
    uint32_t  type;
    uint32_t  acpi_3_0;
  };
void  pmmngr_init(uint32_t kernelsize,uint32_t mapentrycount)
{ 
    struct mmap_entry* map_ptr= (struct mmap_entry*)PMMAP;

    _physical_memory_table = (uint32_t*)(KERNEL_P + kernelsize*SECTOR_SIZE); 

    for (uint32_t i=0;i<0x8000;i++)  //Why 0x8000? This is the size of the table (* 32 of course)
            _physical_memory_table[i] = 0xffffffff;

}

最初,我将所有内容设置为 0xffffffff。然后我读取内存映射(来自 E820)并分配和释放(稍后)。

我编译:

i686-elf-gcc kernel.c -c -g -o kernel.o --ffreestanding
i686-elf-ld kernel.o -Ttext 0x100000 -o kernel.elf
objcopy -O binary kernel.elf kernel.bin

请注意,内核应在 1M 内存空间加载。 这一切都是对这个问题的介绍。 这是主要问题..

这里,我的_physical_memory_table是在内核之后加载/创建的,它的创建位置取决于从引导加载程序获得的kernel.bin文件的大小。

假设kernel.bin文件的大小约为1K,该表将放置在内存中的1M + 1K处(0x100400)。这是核心问题.. 变量 _physical_memory_table 指针并未真正被链接器“放置”在 0x100000 - 0x100400 范围内。它属于 .bss 部分,就我而言,位于此范围之外!指针出现在创建表的位置,存在重叠,因此,这是一个错误。

那么我该如何解决这个问题呢?我需要公开内核的“控制范围”,即内核及其所有部分在内存中的范围,并将此表放在之后

那我该怎么办? (我猜测链接器脚本有什么问题)

最佳答案

您将需要为内核使用自定义链接描述文件。在脚本中将有 .text、.rodata、.data、.bss 和其他一些的正常部分。习惯是为链接过程中当前地址的开始和结束的每个部分定义符号,例如.text 部分中的成员周围的 _text_start = ._text_end = .

然后您可以在 C 代码中声明变量:

extern void *_text_start[], *_text_end[];

然后,链接器将填充地址,并告诉您内核的每个部分的开始和结束位置。通常,所有部分后面都有一个 _end 符号。通常这与 _bss_end 相同,.bss 是最后一部分。

您的内核会将 _physical_memory_table 放置在 _end 之后,以避免与其自身重叠。

尽管大多数人在他们的 .data 或 .bss 部分中包含一个固定的初始 _physical_memory_table,它只是 1:1 映射 4GB 内存。一旦 MMU 启动并运行并且您切换到 kernel_start(),就可以使用 C 代码更轻松地设置正确的细粒度内存表。

在我的内核中,我还在 .bss 部分中包含了 64KB 未使用的内存,用于启动内存管理。因此从一开始就有 64KB 的内存可供分配。然后,解析内存映射的代码可以在将空闲内存区域添加到分配器之前从该池中分配数据结构。

关于c - 查看可执行二进制文件的链接器 'uses' 的确切内存范围,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60097907/

相关文章:

assembly - GCC 内联汇编器,混合寄存器大小 (x86)

linux-kernel - 在 linux 2.6 中将通用寄存器保存在 switch_to() 中

C - 为什么向字符添加数字会重置为 255?

c - 在不使用任何数据结构(如数组等)和外部库的情况下,在 C 中存储 128 位和 256 位数据类型(整数和 float )

Android 什么是工具链兼容性问题?

linux - 找不到 -lcrypto

c - 按降序对gcc makefile中的目标文件进行排序

c++ - Visual Studio 2015 : v120 vs v140?

C 结构体内存存储顺序

c - 对用 C 编码的 4 个整数大小的数组进行 SSE 操作