[Solved] Linker label does not match section in memory

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.
Post Reply
user182941
Posts: 2
Joined: Mon Jul 28, 2025 11:34 pm

[Solved] Linker label does not match section in memory

Post by user182941 »

Hi everyone,

in my masters thesis, I am working on a research operating system in Rust. Currently, I'm trying to hide most kernel memory from userspace to prevent attacks like Meltdown. Therefore, I've created a section .visible_from_usermode that contains relevant kernel code that's required in user space like switching the address space on interrupts etc. Therefore, my linker script contains:

Code: Select all

ENTRY(start)

MEMORY {
    high : ORIGIN = 25M, LENGTH = 0x7000000
}

SECTIONS {
   [...]
    
    . = ALIGN (4K);
    .data : { *(.data*) } AT> high
    ___KERNEL_DATA_END__ = .;
    
    . = ALIGN (4K);
    ___VISIBLE_FROM_USERMODE_START__  = .;
    
    .visible_from_usermode 0x8000000000 : { *(.visible_from_usermode*) } AT> high
    
    ___VISIBLE_FROM_USERMODE_END__ = ___VISIBLE_FROM_USERMODE_START__ + SIZEOF (.visible_from_usermode);
    
}
And relevant rust code is linked into that section using

Code: Select all

#[unsafe(link_section = ".visible_from_usermode")]
fn handle_interrupt(frame: InterruptStackFrame, index: u8, _error: Option<u64>) { ... }
After compiling, I can see that my section got the address 0x1fef000:

Code: Select all

$ readelf -Ws loader/kernel.elf | grep ___VISIBLE_FROM_USERMODE_START__
  9570: 0000000001fef000     0 NOTYPE  GLOBAL DEFAULT 43492 ___VISIBLE_FROM_USERMODE_START__
And I can read those labels from the OS as well:

Code: Select all

[0.000][DBG][boot.rs]   ___VISIBLE_FROM_USERMODE_START__: 0x1fef000
[0.000][DBG][boot.rs]   ___VISIBLE_FROM_USERMODE_END__: 0x1ff0cf6
That location contains the following bytecode:

Code: Select all

(gdb) x/100bx 0x1fef000
0x1fef000:	0x08	0x48	0x8d	0x7c	0x24	0x18	0x48	0x8d
Which I can map to 0x8000000000 using page tables:

Code: Select all

(gdb) x/100bx 0x8000000000
0x8000000000 <_ZN6kernel6memory3vmm19VirtualAddressSpace18load_address_space17had51e0a1f58e9345E>:	0x08	0x48	0x8d	0x7c	0x24	0x18	0x48	0x8d
However, when I try to call a function in that section, the OS resets. And when disassembling the ELF file, I get different bytecode:

Code: Select all

$objdump -Dj .visible_from_usermode loader/kernel.elf
Disassembly of section .visible_from_usermode:

0000008000000000 <_ZN6kernel6memory3vmm19VirtualAddressSpace18load_address_space17had51e0a1f58e9345E>:
  8000000000:   48 83 ec 18             sub    $0x18,%rsp
  8000000004:   48 8d 05 f9 ff ff ff    lea    -0x7(%rip),%rax        # 8000000004 <_ZN6kernel6memory3vmm19VirtualAddressSpace18load_address_space17had51e0a1f58e9345E+0x4>
This bytecode is also loaded in memory, but 1208 bytes earlier:

Code: Select all

(gdb) x/100bx 0x1fef000-1208
0x1feeb48:	0x48	0x83	0xec	0x18	0x48	0x8d	0x05	0xf9
That gives me the feeling that for some reason, the ___VISIBLE_FROM_USERMODE_START__ label isn't really where the section starts. What am I missing, do you have any ideas? Feel free to ask if you need more information, the complete code is available here: https://codeberg.org/LukeLR/D3OS/src/br ... evelopment

Thanks a lot in advance!
Last edited by user182941 on Tue Jul 29, 2025 8:25 am, edited 1 time in total.
user182941
Posts: 2
Joined: Mon Jul 28, 2025 11:34 pm

Re: Linker label does not match section in memory

Post by user182941 »

I found the issue, aparrently the linker ignores the ALIGN(4K)-statement when specifying an absolute virtual address (0x8000000000) in this case. The corrected link.ld looks like this:

Code: Select all

ENTRY(start)

MEMORY {
    high : ORIGIN = 25M, LENGTH = 0x7000000
}

SECTIONS {
    [...]
    . = ALIGN (4K);
    .data : { *(.data*) } AT> high
    ___KERNEL_DATA_END__ = .;
    
    .visible_from_usermode 0x8000000000 : ALIGN (4K) { *(.visible_from_usermode*) } AT> high
    
    ___VISIBLE_FROM_USERMODE_START__  = LOADADDR(.visible_from_usermode);
    ___VISIBLE_FROM_USERMODE_END__ = ___VISIBLE_FROM_USERMODE_START__ + SIZEOF (.visible_from_usermode);
}
Now the .visible_from_usermode-section actually gets loaded at 0x1fef000, as the program headers show:

Code: Select all

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000160 0x0000000001900000 0x0000000001900000
                 0x0000000000678191 0x0000000000688850  RWE    0x10
  LOAD           0x0000000000678310 0x0000000001f88850 0x0000000001f88850
                 0x00000000000662f8 0x00000000000662f8  RW     0x40
  LOAD           0x00000000006df000 0x0000008000000000 0x0000000001fef000
                 0x0000000000001cf6 0x0000000000001cf6  R E    0x1000
  LOAD           0x00000000006e0cf6 0x0000000001ff0cf6 0x0000000001ff0cf6
                 0x000000000023cf04 0x000000000023cf04  R      0x8
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x10
Post Reply