查看链接程序“使用"可执行二进制文件的确切内存范围 [英] Viewing the exact memory range that a linker 'uses' for a executable binary

查看:163
本文介绍了查看链接程序“使用"可执行二进制文件的确切内存范围的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am doing a bit of OSdev, and I've been trying to implement memory management in my kernel. I have started off with a physical memory manager (this is a 32 bit OS). The idea is to keep a table of bits where we allocate a bit per 4K physical memory block. If the bit is '1', the block is in use and if '0', it isn't. I thought that this table should come after the kernel. So here is my kernel code (minimal):

#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)并进行了分配和取消分配(以后).

Initally, I make everything 0xffffffff. Then I read the Memory Map (from E820) and allocate and deallocate (later).

我使用:

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的内存空间中加载. 所有这一切都是对这个问题的介绍. 这是主要问题.

Note that kernel is meant to be loaded at memory space 1M. All this was the introduction to this problem. Here is the main issue..

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

Here, my _physical_memory_table is loaded/created after the kernel, and where it is created depends upon the size of the kernel.bin file it got from the bootloader.

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

Suppose the size of the kernel.bin file is ~1K, The table would be placed at 1M + 1K in memory (0x100400). Here is the core problem.. the variable _physical_memory_table pointer isn't really 'placed' in the 0x100000 - 0x100400 range by the linker. It belongs to the .bss section and, in my case, is placed outside this range! The pointer is present in the location where the table is created, there is an overlap, and thus, it's a bug.

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

So how would I solve this problem? I need to expose the 'range of control' of the kernel, i.e, the range of the kernel and all its pieces in memory, and place this table after that.

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

So what do I do? (I'm guessing something with the linker script)

推荐答案

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

You will want to use a custom linker script for a kernel. In the script would would have the normal sections for .text, .rodata, .data, .bss and a few others. The custom is to define symbols for each section for the start and end for the current address in the linking process, e.g. _text_start = . and _text_end = . around the members in the .text section.

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

In your C code you can then declare variables:

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

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

Those will then have the addresses filled in by the linker and will tell you where each section of your kernel starts and ends. Often you also have a _end symbol that's after all sections. Usually that is identical to _bss_end, with .bss being the last section.

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

Your kernel would place _physical_memory_table after _end to avoid any overlaps with itself.

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

Although most people include a fixed initial _physical_memory_table in their .data or .bss sections that simply maps 4GB of memory 1:1. Once the MMU is up and running and you switched to kernel_start() a proper, fine grained memory table can be setup more easily with C code.

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

In my kernel I also include 64KB of unused memory in the .bss section that is used to prime the memory management. So right from the start there are 64KB of memory available for allocations. The code that parses the memory map can then allocate data structures from that pool before it adds free memory regions to the allocator.

这篇关于查看链接程序“使用"可执行二进制文件的确切内存范围的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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