Viewing the exact memory range that a linker 'uses' for a executable binary

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;

}

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

I compile with:

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

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..

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.

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)

c
x86
linker
kernel
osdev
asked on Stack Overflow Feb 6, 2020 by Suraaj K S

1 Answer

1

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.

In your C code you can then declare variables:

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

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.

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

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.

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.


User contributions licensed under CC BY-SA 3.0