more bloody elf issues [SOLVED]
more bloody elf issues [SOLVED]
I am not the best programmer in the world, but i like to think (and many have said) that im better than most my age. I cannot however work out how to get ELF relocation working.
I check all the flags to make sure that its for the correct architecture etc.
I then pass it to my relocation function, which i know to be at fault.
My problem is that my paging setup uses fixed (4KB) pages, and that they are assigned into a linked list, and therefore you cannot chose the address you want.
This means that i cannot simply use malloc() to allocate a certain size of memory (memsz) at the address called for in vaddress of the ELF program header.
I know in order to continue i have to get some space allocated, then memcpy the segments data into the newely allocated memory.
my malloc() function takes the size as its only parameter, it returns a pointer to the start of the memory allocated, just as any other malloc() would.
How could i go about implementing a function to allocate to a set virtual address?
I check all the flags to make sure that its for the correct architecture etc.
I then pass it to my relocation function, which i know to be at fault.
My problem is that my paging setup uses fixed (4KB) pages, and that they are assigned into a linked list, and therefore you cannot chose the address you want.
This means that i cannot simply use malloc() to allocate a certain size of memory (memsz) at the address called for in vaddress of the ELF program header.
I know in order to continue i have to get some space allocated, then memcpy the segments data into the newely allocated memory.
my malloc() function takes the size as its only parameter, it returns a pointer to the start of the memory allocated, just as any other malloc() would.
How could i go about implementing a function to allocate to a set virtual address?
Last edited by lukem95 on Sun Jan 20, 2008 7:24 am, edited 1 time in total.
- thepowersgang
- Member
- Posts: 734
- Joined: Tue Dec 25, 2007 6:03 am
- Libera.chat IRC: thePowersGang
- Location: Perth, Western Australia
- Contact:
I am persuming that this memory manger is the one attached to the MMU (Memory Management Unit).
Paging on the x86 requires a table of values that indicate the mappings between physical and virtual memory.
e.g.
With this system you can point a specified virtual address to an unused physical location. e.g.
There is a very useful tutorial on the wiki (http://www.osdev.org/wiki/Memory_Management) and some others on OsDever.net
Paging on the x86 requires a table of values that indicate the mappings between physical and virtual memory.
e.g.
Code: Select all
page_table[0] = <some random address>
page_table[1] = <another random address>
Code: Select all
mm_map(vaddress, malloc(memsz), memsz)
Step 1:
Get your physical memory manager to allocate a frame of memory.
Step 2:
Map a 4K region starting at the virtual address (page) to that physical address (frame):
And you're done. Just do that for every 4K you want to map.
Get your physical memory manager to allocate a frame of memory.
Code: Select all
u32int frame = alloc_frame();
Map a 4K region starting at the virtual address (page) to that physical address (frame):
Code: Select all
void map(u32int *page_directory, u32int frame_addr, u32int page_addr)
{
u32int page = page_addr/4096;
u32int *page_table = (u32int*)page_directory[page/1024];
// NOTE: This assumes the page table above is non-NULL!
page_table[page%1024] = frame_addr | 0x7; // User mode, read/writeable, present.
}
this is really wierd, but i have no idea whats going wrong.
paging is memory protection yeah, but i thought that everything in the same paging directory could access each other?
when i try and memcpy() the data from the app to the allocated virtual pages i get a page fault. How would i tell the CPU i can access this page, while still keeping the data there without overiding it?
thanks, lukem95
paging is memory protection yeah, but i thought that everything in the same paging directory could access each other?
when i try and memcpy() the data from the app to the allocated virtual pages i get a page fault. How would i tell the CPU i can access this page, while still keeping the data there without overiding it?
thanks, lukem95
- Attachments
-
- Error shot (lots of debugging)
- cake.jpg (37.74 KiB) Viewed 2809 times
The easiest way to invalidate the TLB (translation lookaside buffer, an on-chip cache that stores page table entries) is just to do:
That way, you "change" the page directory (as far as the CPU is concerned), so it flushes the entire TLB. This isn't the most efficient way in the long run, but as a debugging tool is perfect.
Could you post some code?
Code: Select all
unsigned int tmp;
asm volatile("mov %%cr3, %0" : "=r" (tmp));
asm volatile("mov %0, %%cr3" : : "r" (tmp));
Could you post some code?
sure, ill post some if that trick doesn't work.
its actually mostly used from your tutorial lol (the paging bit anyway), so you should be familiar with it [EDIT]Props to JamesM for his tutorials btw, they vastly improved the paging code from my previous kernel, and i actually understand the process now[/EDIT]
... ok it didnt work. im not sure if im using it right though, i've never heard of having to use that before, what context do i use it in?
my code:
its actually mostly used from your tutorial lol (the paging bit anyway), so you should be familiar with it [EDIT]Props to JamesM for his tutorials btw, they vastly improved the paging code from my previous kernel, and i actually understand the process now[/EDIT]
... ok it didnt work. im not sure if im using it right though, i've never heard of having to use that before, what context do i use it in?
my code:
Code: Select all
/* CakeOS */
#include <system.h>
/* Adapted from a tutorial by James Molloy */
/* From link.ld */
extern unsigned int end;
unsigned int placement_address = (unsigned int)&end;
/* From heap.c */
extern heap_t *kheap;
unsigned int kmalloc_int(unsigned int sz, int align, unsigned int *phys)
{
// This will eventually call malloc() on the kernel heap.
// For now, though, we just assign memory at placement_address
// and increment it by sz. Even when we've coded our kernel
// heap, this will be useful for use before the heap is initialised.
if (align == 1 && (placement_address & 0xFFFFF000) )
{
// Align the placement address;
placement_address &= 0xFFFFF000;
placement_address += 0x1000;
}
if (phys)
{
*phys = placement_address;
}
unsigned int tmp = placement_address;
placement_address += sz;
return tmp;
}
unsigned int kmalloc_a(unsigned int sz)
{
return kmalloc_int(sz, 1, 0);
}
unsigned int kmalloc_p(unsigned int sz, unsigned int *phys)
{
return kmalloc_int(sz, 0, phys);
}
unsigned int kmalloc_ap(unsigned int sz, unsigned int *phys)
{
return kmalloc_int(sz, 1, phys);
}
unsigned int kmalloc(unsigned int sz)
{
return kmalloc_int(sz, 0, 0);
}
// A bitset of frames - used or free.
unsigned int *frames;
unsigned int nframes;
//For later
page_directory_t* kernel_directory;
page_directory_t* current_directory;
// Macros used in the bitset algorithms.
#define INDEX_FROM_BIT(a) (a/(8*4))
#define OFFSET_FROM_BIT(a) (a%(8*4))
// Static function to set a bit in the frames bitset
static void set_frame(unsigned int frame_addr)
{
unsigned int frame = frame_addr/0x1000;
unsigned int idx = INDEX_FROM_BIT(frame);
unsigned int off = OFFSET_FROM_BIT(frame);
frames[idx] |= (0x1 << off);
}
// Static function to clear a bit in the frames bitset
static void clear_frame(unsigned int frame_addr)
{
unsigned int frame = frame_addr/0x1000;
unsigned int idx = INDEX_FROM_BIT(frame);
unsigned int off = OFFSET_FROM_BIT(frame);
frames[idx] &= ~(0x1 << off);
}
// Static function to test if a bit is set.
//static unsigned int test_frame(unsigned int frame_addr)
//{
// unsigned int frame = frame_addr/0x1000;
// unsigned int idx = INDEX_FROM_BIT(frame);
// unsigned int off = OFFSET_FROM_BIT(frame);
// return (frames[idx] & (0x1 << off));
//}
// Static function to find the first free frame.
static unsigned int first_frame()
{
unsigned int i, j;
for (i = 0; i < INDEX_FROM_BIT(nframes); i++)
{
if (frames[i] != 0xFFFFFFFF) // nothing free, exit early.
{
// at least one bit is free here.
for (j = 0; j < 32; j++)
{
unsigned int toTest = 0x1 << j;
if ( !(frames[i]&toTest) )
{
return i*4*8+j;
}
}
}
}
}
// Function to allocate a frame.
void alloc_frame(page_t *page, int is_kernel, int is_writeable)
{
if (page->frame != 0)
{
return; // Frame was already allocated, return straight away.
}
else
{
unsigned int idx = first_frame(); // idx is now the index of the first free frame.
if (idx == (unsigned int)-1)
{
printf("NO FREE MEMORY");
}
set_frame(idx*0x1000); // this frame is now ours!
page->present = 1; // Mark it as present.
page->rw = (is_writeable)?1:0; // Should the page be writeable?
page->user = (is_kernel)?0:1; // Should the page be user-mode?
page->frame = idx;
}
}
// Function to deallocate a frame.
void free_frame(page_t *page)
{
unsigned int frame;
if (!(frame=page->frame))
{
return; // The given page didn't actually have an allocated frame!
}
else
{
clear_frame(frame); // Frame is now free again.
page->frame = 0x0; // Page now doesn't have a frame.
}
}
void initialise_paging(unsigned int memorysz)
{
unsigned int mem_end_page = memorysz;
nframes = mem_end_page / 0x1000;
frames = (unsigned int*)kmalloc(INDEX_FROM_BIT(nframes));
memset(frames, 0, INDEX_FROM_BIT(nframes));
// Let's make a page directory.
kernel_directory = (page_directory_t*)kmalloc_a(sizeof(page_directory_t));
current_directory = kernel_directory;
/* heap **** */
int i = 0;
for (i = KHEAP_START; i < KHEAP_START+KHEAP_INITIAL_SIZE; i += 0x1000)
get_page(i, 1, kernel_directory);
// We need to identity map (phys addr = virt addr) from
// 0x0 to the end of used memory, so we can access this
// transparently, as if paging wasn't enabled.
// NOTE that we use a while loop here deliberately.
// inside the loop body we actually change placement_address
// by calling kmalloc(). A while loop causes this to be
// computed on-the-fly rather than once at the start.
i = 0;
while (i < placement_address+0x1000)
{
// Kernel code is readable but not writeable from userspace.
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += 0x1000;
}
for (i = KHEAP_START; i < KHEAP_START+KHEAP_INITIAL_SIZE; i += 0x1000)
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
// Before we enable paging, we must register our page fault handler.
irq_install_handler(14, page_fault);
// Now, enable paging!
switch_page_directory(kernel_directory);
// Initialise the kernel heap.
kheap = create_heap(KHEAP_START, KHEAP_START+KHEAP_INITIAL_SIZE, 0xCFFFF000, 0, 0);
}
void switch_page_directory(page_directory_t *dir)
{
current_directory = dir;
__asm__ __volatile__("mov %0, %%cr3":: "r"(&dir->tablesPhysical));
unsigned int cr0;
__asm__ __volatile__("mov %%cr0, %0": "=r"(cr0));
cr0 |= 0x80000000; // Enable paging!
__asm__ __volatile__("mov %0, %%cr0":: "r"(cr0));
}
page_t *get_page(unsigned int address, int make, page_directory_t *dir)
{
// Turn the address into an index.
address /= 0x1000;
// Find the page table containing this address.
unsigned int table_idx = address / 1024;
if (dir->tables[table_idx]) // If this table is already assigned
{
return &dir->tables[table_idx]->pages[address%1024];
}
else if(make)
{
unsigned int tmp;
dir->tables[table_idx] = (page_table_t*)kmalloc_ap(sizeof(page_table_t), &tmp);
dir->tablesPhysical[table_idx] = tmp | 0x7; // PRESENT, RW, US.
return &dir->tables[table_idx]->pages[address%1024];
}
else
{
return 0;
}
}
void invalidate_tlb()
{
unsigned int tmp;
__asm__ __volatile__("mov %%cr3, %0" : "=r" (tmp));
__asm__ __volatile__("mov %0, %%cr3" : : "r" (tmp));
}
Code: Select all
/* CakeOS */
#include <system.h>
extern page_directory_t* kernel_directory;
int openModule_elf(char* name, int offset)
{
elfHeader* header;
/* Put header information into header struct */
header = (elfHeader*) offset;
/* Check for any errors */
if(header->ident[0] != 0x7f || header->ident[1] != 'E' || header->ident[2] != 'L' || header->ident[3] != 'F')
{
printf("Error opening %s! %s is not ELF\n",name,name);
return 1;
}
if(header->ident[4] != 1)
{
printf("Error opening %s! ELF not designed for x86-32 architecture!\n",name);
return 1;
}
if(header->ident[5] != 1)
{
printf("Error opening %s! ELF uses wrong encoding type. LSB ONLY supported!\n",name);
return 1;
}
if(header->flags)
{
printf("Error opening %s! ELF must not contain flags in header!\n",name);
return 1;
}
if(relocModule_elf(header,offset) != 1)
{
printf("Error opening %s! Cannot relocate ELF image!\n",name);
return 1;
}
/* Call the program */
printf("calling executable\n");
__asm__ __volatile("call %0" : : "a" (header->entry));
return 0;
}
int relocModule_elf(elfHeader* header, int offset)
{
int i;
programHeader* pHeader;
page_t* page;
for(i = 0; i < header->phnum; i++)
{
pHeader = (programHeader*) ((unsigned long) header + header->phoff + i * header->phentsize);
/* Check if its loadable */
if( pHeader->type == PT_LOAD ) /* PT_LOAD == 1 */
{
if(pHeader->vaddr < 0x1000)
{
printf("Virtual address (0x%x) below 1MB\n",pHeader->vaddr,i);
return 0;
}
printf("Allocate some space at vaddr (0x%x)\n",pHeader->vaddr);
page = get_page(pHeader->vaddr, 1, kernel_directory);
alloc_frame(page, 0, 1);
printf("Page's location: 0x%x\n",page->frame);
printf("memcpy() the program data there\n");
printf("0x%x - 0x%x - 0x%x\n",pHeader->vaddr,offset+pHeader->offset,pHeader->fileSize);
printf("header:0x%x pHeader->offset:0x%x\n",offset,pHeader->offset);
/* THIS LINE IS FAULTY */
invalidate_tlb();
memcpy((unsigned int*)pHeader->vaddr, offset+pHeader->offset, pHeader->fileSize);
/* !!!!!!!!!!!!!!!!!! */
if(pHeader->fileSize < pHeader->memSize)
printf("Filesize was less than memory size\n");
/* memset() the remaining memory with zeroes */
memset((char *)pHeader->vaddr+pHeader->fileSize, 0, pHeader->memSize-pHeader->fileSize);
}
}
return 1;
}
Hi,
OK, there are a couple of things that need addressing. Firstly: "Page's location: 0x3", taken from your screenshot.
Can you see what's wrong with that? You've asked the memory manager to allocate you a frame, and it's replied with physical address 0x3000. That's right bang in the middle of lower memory, and is certainly incorrect! There is something seriously awry there, because I see you have hardly changed my alloc_frame code and yet I *know* that bug doesn't exist in my code.
Secondly, you try to memcpy(vaddr, offset+pHeader->offset). The page fault is occurring not while trying to *write* data (to vaddr), but while trying to *read* data (from offset+pHeader->offset)! now, I *assume* taht the passed in elfHeader* is actually equal to
Or something similar. In which case, There is some problem, as your kernel can access header->*, but not offset+header->*! have you loaded the entire ELF binary into memory? Have you adjusted placement_address so that the elf binary actually gets identity-mapped when initialise_paging is called?
The first problem is the one that worries me. You need to work out why the physical frame at 0x3000 ISNT being allocated immediately in the loop:
, because it should be!
cheers,
James
OK, there are a couple of things that need addressing. Firstly: "Page's location: 0x3", taken from your screenshot.
Can you see what's wrong with that? You've asked the memory manager to allocate you a frame, and it's replied with physical address 0x3000. That's right bang in the middle of lower memory, and is certainly incorrect! There is something seriously awry there, because I see you have hardly changed my alloc_frame code and yet I *know* that bug doesn't exist in my code.
Secondly, you try to memcpy(vaddr, offset+pHeader->offset). The page fault is occurring not while trying to *write* data (to vaddr), but while trying to *read* data (from offset+pHeader->offset)! now, I *assume* taht the passed in elfHeader* is actually equal to
Code: Select all
elfHeader *header = (elfHeader*)offset;
The first problem is the one that worries me. You need to work out why the physical frame at 0x3000 ISNT being allocated immediately in the loop:
Code: Select all
i = 0;
while (i < placement_address+0x1000)
{
// Kernel code is readable but not writeable from userspace.
alloc_frame( get_page(i, 1, kernel_directory), 0, 0);
i += 0x1000;
}
cheers,
James
Thankyou for your detailed anyalysis JamesM:
i haven't touched my placement_address, it is used in the same way as detailed in your tutorials, i defined it as a symbol in my linker file.
As to the pages location, i assumed that 0x3 was the virtual address, and as such fine (as this was vaddr in the pHeader). I load it from a ramfs loaded as a grub module, again using code mostly from your tutorial. I then get the offset in the ramfs, and add it to the ramfs's offset in memory, to get the actual location of the ELF. Then i pass this to my loading code and you have the source from there.
Yeah the problem is when memcpy() tries to read the data from the offset detailed in the second arg. If i didnt specify this before it was either because i hadn't debugged it that far, or because i forgot (sorry).
Initilise_paging() is called before my RAMFS is initialised (although it has been loaded by GRUB before obviously), i dont 100% understand what you are asking me about placement_address and adjusting it, but i have left it alone, as it worked fine until now.
oh.. i just looked at my elf programs linker, the virtual address is set to load at 0x3000. I changed the loading address to 0x40000 and i get the same problem.
I'm sure the answer to this is probably something very simple i am just not understanding, and that ill come back in 6 months and kick myself, but im just not understanding why this isnt working
thankyou all for your help, its much appreciated, and sorry for the funny order of the reply, im trying to answer the questions as best as i can
[EDIT]sorry, ignore previous edit, bochs went funny :S[/EDIT]
i haven't touched my placement_address, it is used in the same way as detailed in your tutorials, i defined it as a symbol in my linker file.
As to the pages location, i assumed that 0x3 was the virtual address, and as such fine (as this was vaddr in the pHeader). I load it from a ramfs loaded as a grub module, again using code mostly from your tutorial. I then get the offset in the ramfs, and add it to the ramfs's offset in memory, to get the actual location of the ELF. Then i pass this to my loading code and you have the source from there.
Yeah the problem is when memcpy() tries to read the data from the offset detailed in the second arg. If i didnt specify this before it was either because i hadn't debugged it that far, or because i forgot (sorry).
Initilise_paging() is called before my RAMFS is initialised (although it has been loaded by GRUB before obviously), i dont 100% understand what you are asking me about placement_address and adjusting it, but i have left it alone, as it worked fine until now.
oh.. i just looked at my elf programs linker, the virtual address is set to load at 0x3000. I changed the loading address to 0x40000 and i get the same problem.
I'm sure the answer to this is probably something very simple i am just not understanding, and that ill come back in 6 months and kick myself, but im just not understanding why this isnt working
thankyou all for your help, its much appreciated, and sorry for the funny order of the reply, im trying to answer the questions as best as i can
[EDIT]sorry, ignore previous edit, bochs went funny :S[/EDIT]
Wrong! It's wrong! It should NOT be allocating a physical address at anywhere less than 1MB!JamesM wrote:physical address 0x3000
The placement_address has to be adjusted. I can't remember if my code does this or not. &end is alright if you're not loading a GRUB module, but if you are loading a GRUB module, you need to move placement address to past the end of it! The same goes if you load multiple grub modules. Dump out what placement_address is at the time of initialise_paging being called, and write it here.i dont 100% understand what you are asking me about placement_address and adjusting it, but i have left it alone, as it worked fine until now.
Oh ok, placement_address is 0x142000.
EDIT: your code does update placement_address slightly, by extending it past mbd->mods_addr (mbd is my multiboot structure)
EDIT2: mbd->mods_addr is 0x1b920 if its useful, i can see how it might be
EDIT: your code does update placement_address slightly, by extending it past mbd->mods_addr (mbd is my multiboot structure)
Code: Select all
unsigned int initrd_location = *((unsigned int*)mbd->mods_addr);
unsigned int initrd_end = *(unsigned int*)(mbd->mods_addr+4);
placement_address = initrd_end;
edit. i really need to read stuff more before posting.
but if anybody has any clues id really appreciate any ideas, if i can get this going ill be immensly happy.
i printed out everything i could think of to try and make sense of this, i'll attach the screenshot. It appears that it is trying to memcpy() from something above placement_address... i guess i have to raise this to above it then?
im doing it as detailed in an my above post (i think 2 back), so this means that GRUB is incorrectly reporting the size? or possibly that my ELF relocater is trying to access the wrong address:
but if anybody has any clues id really appreciate any ideas, if i can get this going ill be immensly happy.
i printed out everything i could think of to try and make sense of this, i'll attach the screenshot. It appears that it is trying to memcpy() from something above placement_address... i guess i have to raise this to above it then?
im doing it as detailed in an my above post (i think 2 back), so this means that GRUB is incorrectly reporting the size? or possibly that my ELF relocater is trying to access the wrong address:
Code: Select all
(const void*)offset+pHeader->offset
- Attachments
-
- Debugging stuff
- cakenting.jpg (44.09 KiB) Viewed 2703 times