Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
nexos
Member
Posts: 1081 Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos
Post
by nexos » Wed Mar 18, 2020 6:59 pm
Hello,
I am currently making an operating system called NexOS, and am working a program loader that can load PE programs made by MinGW for Linux. It has an Image Base of 4MB. When i execute it, it page faults. Here is the program loading code:
Code: Select all
int entryPoint;
int stack;
extern void execute_internal();
int create_process(char* path)
{
IMAGE_DOS_HEADER* dosHeader;
IMAGE_NT_HEADERS* peHeader;
int size;
process* proc;
pdirectory* addressSpace;
void* buffer = read_file(path, &size);
dosHeader =(IMAGE_DOS_HEADER*)buffer;
if(dosHeader->e_magic != 0x5A4D)
{
return -1;
}
peHeader = (dosHeader->e_lfanew + (uint32_t)buffer);
addressSpace = get_directory();
proc = kernel_heap_alloc(sizeof(process));
proc->addressSpace = addressSpace;
proc->threads = kernel_heap_alloc(sizeof(thread) * 10);
proc->threads[0].state.eip = peHeader->OptionalHeader.ImageBase + peHeader->OptionalHeader.AddressOfEntryPoint;
proc->threads[0].state.eflags = 0x200;
if(!(size & 0xFFFFF000))
{
size &= 0xFFFFF000;
size += 0x1000;
}
for(int i = 0; i < size; i += 4096)
{
void* block = alloc_block();
map_address(addressSpace, peHeader->OptionalHeader.ImageBase + i, (uint32_t)block, I86_PTE_PRESENT | I86_PTE_WRITABLE | I86_PTE_USER);
memcpy(peHeader->OptionalHeader.ImageBase + i, buffer, 4096);
buffer += 4096;
}
void* stackVirt = peHeader->OptionalHeader.ImageBase + peHeader->OptionalHeader.SizeOfImage + 4096;
void* stackPhys = alloc_block();
map_address(addressSpace, (uint32_t)stackVirt, (uint32_t)stackPhys, I86_PTE_PRESENT | I86_PTE_WRITABLE | I86_PTE_USER);
proc->threads[0].stack = stackVirt;
proc->threads[0].state.esp = (uint32_t)stackVirt;
proc->threads[0].state.ebp = (uint32_t)stackVirt;
entryPoint = proc->threads[0].state.eip;
stack = proc->threads[0].state.esp;
execute_internal();
return 0;
}
The execute_internal function looks like this:
Code: Select all
extern entryPoint
extern stack
global execute_internal
execute_internal:
cli
mov ax, 0x23
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push 0x23
mov eax, [stack]
push eax
push 0x200
push 0x1b
mov eax, [entryPoint]
push eax
iretd
The full source repo is at
https://github.com/NexSuite/NexOS
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects:
NexNix |
libnex |
nnpkg
Gigasoft
Member
Posts: 856 Joined: Sat Nov 21, 2009 5:11 pm
Post
by Gigasoft » Wed Mar 18, 2020 11:03 pm
The initial stack pointer points to the beginning of the stack instead of the end.
nexos
Member
Posts: 1081 Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos
Post
by nexos » Thu Mar 19, 2020 12:16 pm
I changed that and it still page faults. The value in CR2 is 0x401000.
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects:
NexNix |
libnex |
nnpkg
eekee
Member
Posts: 892 Joined: Mon May 22, 2017 5:56 am
Location: Kerbin
Discord: eekee
Contact:
Post
by eekee » Fri Mar 20, 2020 10:41 am
@Octocontrabass: That's rounding up to the next 4KiB boundary.
Kaph — a modular OS intended to be easy and fun to administer and code for.
"May wisdom, fun, and the greater good shine forth in all your work." — Leo Brodie
nexos
Member
Posts: 1081 Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos
Post
by nexos » Fri Mar 20, 2020 10:44 am
I changed it up a little. It now looks like:
Code: Select all
int entryPoint;
int stack;
extern void execute_internal();
int create_process(char* path)
{
IMAGE_DOS_HEADER* dosHeader;
IMAGE_NT_HEADERS* peHeader;
int size;
process* proc;
pdirectory* addressSpace;
void* buffer = read_file(path, &size);
dosHeader =(IMAGE_DOS_HEADER*)buffer;
if(dosHeader->e_magic != 0x5A4D)
{
return -1;
}
peHeader = (dosHeader->e_lfanew + (uint32_t)buffer);
addressSpace = get_directory();
proc = kernel_heap_alloc(sizeof(process));
proc->addressSpace = addressSpace;
proc->threads = kernel_heap_alloc(sizeof(thread)* 10);
proc->threads[0].state.eip = peHeader->OptionalHeader.ImageBase + peHeader->OptionalHeader.AddressOfEntryPoint;
proc->threads[0].state.eflags = 0x200;
for(int i = 0; i < size; i += 4096)
{
void* block = alloc_block();
map_address(addressSpace, peHeader->OptionalHeader.ImageBase + i, (uint32_t)block, I86_PTE_PRESENT | I86_PTE_WRITABLE | I86_PTE_USER);
memcpy(peHeader->OptionalHeader.ImageBase + i, buffer, 4096);
buffer += 4096;
}
void* stackVirt = peHeader->OptionalHeader.ImageBase + peHeader->OptionalHeader.SizeOfImage;
void* stackPhys = alloc_block();
map_address(addressSpace, (uint32_t)stackVirt, (uint32_t)stackPhys, I86_PTE_PRESENT | I86_PTE_WRITABLE | I86_PTE_USER);
proc->threads[0].stack = stackVirt;
proc->threads[0].state.esp = (uint32_t)stackVirt;
asm("cli");
switch_pdirectory(addressSpace);
entryPoint = proc->threads[0].state.eip;
stack = proc->threads[0].state.esp;
void (*entry) () = (entryPoint);
execute_internal();
return 0;
}
and execute_internal is:
Code: Select all
extern entryPoint
extern stack
global execute_internal
execute_internal:
mov ax, 0x23
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push 0x23
lea eax, [stack]
push eax
push 0x200
push 0x1b
lea eax, [entryPoint]
push eax
iretd
I also tried compiling it under Visual Studio,
and yet it still page faults at address 0x-3FEF7754
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects:
NexNix |
libnex |
nnpkg
Octocontrabass
Member
Posts: 5575 Joined: Mon Mar 25, 2013 7:01 pm
Post
by Octocontrabass » Sat Mar 21, 2020 12:36 am
eekee wrote: That's rounding up to the next 4KiB boundary.
Maybe that's what it's supposed to do, but that's not what it does.
iansjack
Member
Posts: 4703 Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK
Post
by iansjack » Sat Mar 21, 2020 2:30 am
eekee
Member
Posts: 892 Joined: Mon May 22, 2017 5:56 am
Location: Kerbin
Discord: eekee
Contact:
Post
by eekee » Sat Mar 21, 2020 4:47 am
Oh of course! If it's meant to round up, the condition is wrong. Hmm...
Kaph — a modular OS intended to be easy and fun to administer and code for.
"May wisdom, fun, and the greater good shine forth in all your work." — Leo Brodie
iansjack
Member
Posts: 4703 Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK
Post
by iansjack » Sat Mar 21, 2020 4:50 am
Still not quite correct. What if size was 0x12345000?
This is where using a debugger is such a useful tool. You would have spotted the problem there as soon as you stepped through the relevant code. That's why I risk boring people (and upsetting some) who ask the community to debug their code by suggesting they learn how to use a debugger. Effort spent up front, with simple user programs, learning how to use gdb, or the like, effectively is the best investment you can make.
Last edited by
iansjack on Sat Mar 21, 2020 5:03 am, edited 1 time in total.
eekee
Member
Posts: 892 Joined: Mon May 22, 2017 5:56 am
Location: Kerbin
Discord: eekee
Contact:
Post
by eekee » Sat Mar 21, 2020 5:01 am
derp...
Knew I shouldn't have posted. I not has a brain today.
Kaph — a modular OS intended to be easy and fun to administer and code for.
"May wisdom, fun, and the greater good shine forth in all your work." — Leo Brodie
nexos
Member
Posts: 1081 Joined: Tue Feb 18, 2020 3:29 pm
Libera.chat IRC: nexos
Post
by nexos » Sat Mar 21, 2020 2:34 pm
I got it to run in kernel mode, but it won't run in user mode
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects:
NexNix |
libnex |
nnpkg
Gigasoft
Member
Posts: 856 Joined: Sat Nov 21, 2009 5:11 pm
Post
by Gigasoft » Mon Mar 23, 2020 10:43 am
The PDEs must have the User bit set.