Inconsistency in Kernel memory address
Closed this issue · 13 comments
Hey! This is likely an error on my part but after digging for a while I think I should report it just in case.
I have been following your os blog. I set up my own mechanism to determine the start and end of the Kernel but it doesn't line up with the values from this crate. I set up my linker.ld script as follows:
ENTRY(_start)
SECTIONS
{
. = 1M;
KERNEL_START = .;
/* All sections */
KERNEL_END = .;
}
And then in my rust kernel:
enum Void {}
extern "C" {
static KERNEL_START: Void;
static KERNEL_END: Void;
}
pub fn kernel_memory_start() -> u64 {
&KERNEL_START as *const _ as u64
}
pub fn kernel_memory_end() -> u64 {
&KERNEL_END as *const _ as u64
}The values I get:
Kernel start: 1048576
Kernel end: 1114112
The values from this crate:
Kernel start: 1048576
Kernel end: 1211936
From my limited understanding there is no reason why these should not be the same? I'm also unsure as to why my kernel doesn't start at 1M but I'm overlooking that for now :)
First guess: 1048576 is 1MiB, it's just in decimal instead of hex. And the different end values are due to debug sections. The linker appends all remaining sections at the very end. So there might be some sections behind your kernel end symbol.
I'll take a closer look tomorrow.
First guess: 1048576 is 1MiB, it's just in decimal instead of hex.
Hah, of course! Thanks.
The linker appends all remaining sections at the very end.
That sounds pretty reasonable. Thanks for taking the time to respond.
You could try to insert a wildcard section just before your KERNEL_END symbol:
.other :
{
*(*)
}
So all remaining input section go into the .other output section. Now both kernel end addresses should be equal.
(If not, you could check the ELF sections through objdump -h build/kernel-x86_64.bin in order to see which value is correct.)
I added the other tag and they got closer:
Mine: 0x100000 - 0x123a4a
Multiboot2: 0x100000 - 0x1266b0
And the elf headers dump:
Idx Name Size VMA LMA File off Algn
0 .rodata 00002000 0000000000100000 0000000000100000 00001000 2**4
CONTENTS, ALLOC, LOAD, READONLY, DATA
1 .text 00008000 0000000000102000 0000000000102000 00003000 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
2 .data 00001000 000000000010a000 000000000010a000 0000b000 2**3
CONTENTS, ALLOC, LOAD, DATA
3 .bss 00004000 000000000010b000 000000000010b000 0000c000 2**12
ALLOC
4 .other 00014a4a 000000000010f000 000000000010f000 0000c000 2**4
CONTENTS, ALLOC, LOAD, CODE
Sadly I'm not sure how to confirm from these headers which values are correct. Adding the File offset and size of the .other section gives me a number different to both the above (0x20a4a). Perhaps you can confirm?
Sorry that this is turning into debugging my kernel rather than confirming the existence of a bug!
Your KERNEL_END symbol seems to be correct. The other section has a virtual memory offset of 0x10f0000 and a size of 0x14a4a, which equals 0x123a4a.
I'm not quite sure why the multiboot2 are different. Could you post your multiboot2 code too?
Ah ok, as you can tell I'm still figuring out how to read all these various formats..
Here is my code with multiboot2:
let boot_info = unsafe{ multiboot2::load(multiboot_info_address) };
let elf_sections_tag = boot_info.elf_sections_tag()
.expect("Elf-sections tag required");
let kernel_start = elf_sections_tag.sections().map(|s| s.addr)
.min().unwrap();
let kernel_end = elf_sections_tag.sections().map(|s| s.addr + s.size)
.max().unwrap();
println!("Start: 0x{0:x}\nEnd: 0x{1:x}", kernel_start, kernel_end);Sorry for the long delay!
I think I found a solution: Try to change the other section to:
.other : {
*(.debug*)
}So now only a debug section wildcard instead of the full *(*) wildcard. This fixes the problem for me, but I don't know why :D.
Alternatively you could filter out the debug sections in your multiboot code, since they're not loaded anyway:
let kernel_start = elf_sections_tag.sections()
.filter(|s| s.is_allocated()) // new
.map(|s| s.addr)
.min()
.unwrap();
let kernel_end = elf_sections_tag.sections()
.filter(|s| s.is_allocated()) // new
.map(|s| s.addr + s.size)
.max()
.unwrap();This filters out all sections that don't have the ALLOC flag (visible in the elf headers dump).
Sorry for the long delay!
No problem - thank you for getting back to me with a potential solution!
I'll try this out later this week and let you know how it goes. Thanks again!
Any news on this, @AtheMathmo ?
Sorry for not getting back to you on this.
I just tried to run this but I have some issues on my end. For some reason I can no longer compile the OS - I guess something on the newer nightlies broke it? I get the following error when trying to link:
undefined reference to '_Unwind_Resume'
This is with both your suggested change and my previous linker file (which worked before).
If you know what this error is and how to fix it I'll go ahead and try to confirm that this fixes my issue. Otherwise I'm afraid I don't have the time to figure out and fix the issue :(.
Thank you for taking so much time to help me resolve this! Feel free to close this out if the Unwind_Resume error is as mysterious to you as me.
Thanks for the quick response!
The _Unwind_Resume function is (somehow) required now. I assume that the core, alloc, or collections libraries link to it. You can easiliy fix this by creating an empty dummy function:
#[allow(non_snake_case)]
#[no_mangle]
pub extern "C" fn _Unwind_Resume() -> ! {
loop {}
}Ah that did the trick, thanks!
And also with the modified .other section the two do indeed agree! Sorry for the delay but thank you for helping me figure this out!
Great! So I think we can close this as resolved. Thanks for reporting this and for your quick replies!