I am writing a small 64-bit bootloader to explore assembly language and its interaction with C code. I am compiling the assembly part with NASM and the C part in GCC, then linking all together with ld, and extracting the pure code with objcopy. The code is meant to run without Grub or ony other bootloader: it is loading itself from floppy disk into memory. Currently, I am looking into how C functions can use symbols defined in NASM, and I am struggling in something I thought it was "easy":
I have defined a global variable in NASM that is placed in a custom section. The reason for this is that I want this variable to have a virtual address in the range > 0xffff800000000000 (the kernel space). I am taking care of the addressing in my linker script, see below. The variable is defined in the assembly file like this:
section .kdata
global xyz_foo_bar
xyz_foo_bar:
dq 0
In the C code, I have declared a function that just increments that global variable:
extern unsigned long xyz_foo_bar;
void test_xyz_inc() {
xyz_foo_bar++;
}
This gets compiled and linked successfully - apparently. However, when I look at the disassembled function, I don't understand what I see.
objdump.exe -M intel -d boot1.elf
...
ffff800000008f73 <test_xyz_inc>:
ffff800000008f73: 55 push rbp
ffff800000008f74: 48 89 e5 mov rbp,rsp
ffff800000008f77: 48 8b 05 00 00 00 00 mov rax,QWORD PTR [rip+0x0] # ffff800000008f7e <test_xyz_inc+0xb>
ffff800000008f7e: 48 8b 00 mov rax,QWORD PTR [rax]
ffff800000008f81: 48 8d 50 01 lea rdx,[rax+0x1]
ffff800000008f85: 48 8b 05 00 00 00 00 mov rax,QWORD PTR [rip+0x0] # ffff800000008f8c <test_xyz_inc+0x19>
ffff800000008f8c: 48 89 10 mov QWORD PTR [rax],rdx
ffff800000008f8f: 90 nop
ffff800000008f90: 5d pop rbp
ffff800000008f91: c3 ret
Address 0xffff800000008f77: Am I right when I interpret that it is trying to dereference RIP with no displacement and use the resulting qword as an input for RAX? How does it make sense? My guess is that the displacement has not been calculated correctly by the compiler / linker.
Here is how I compile the code:
nasm -o boot1.o -l boot1.lst -f elf64 boot1.asm
gcc -ffreestanding -static-pie -c -mabi=sysv -Wall -o c_functions.o c_functions.c
ld -melf_x86_64 --build-id=none -static --unresolved-symbols=report-all -T boot1.ld boot1.o c_functions.o -o boot1.elf
objcopy -O binary boot1.elf boot1.bin
And just for the sake of completeness, here is the linker script:
OUTPUT_FORMAT("elf64-x86-64");
/* We define an entry point to keep the linker quiet. This entry point
* has no meaning with a bootloader in the binary image we will eventually
* generate. Bootloader will start executing at whatever is at 0x07c00 */
ENTRY(main);
INCLUDE boot1-vars.ldinc;
SECTIONS
{
. = load_offset;
.text : {
/* Place the code in boot1.o before all other code */
boot1.o(.text);
}
_text_end = .;
. += code_virtaddr;
.ktext : AT(_ktext_physStart) {
_ktext_physStart = . - code_virtaddr;
boot1.o(.ktext);
c_*.o(.text);
}
.kdata : {
boot1.o(.kdata);
}
. -= code_virtaddr;
/* Place the data after the code */
.data : AT(_data_physStart) {
_data_physStart = .;
*(.data);
*(.rodata*);
}
/* Place the uninitialised data in the area after our bootloader
* The BIOS only reads the 512 bytes before this into memory */
.bss : SUBALIGN(4) {
__bss_start = .;
*(COMMON);
*(.bss)
. = ALIGN(4);
__bss_end = .;
}
__bss_sizeb = SIZEOF(.bss);
/* Remove sections that won't be relevant to us */
/DISCARD/ : {
c_*.o(.*);
}
_end = .;
}
Is there anything basic I am missing?
PE: The contents of boot1-vars.ldinc, as requested:
load_offset = 0x7C00;
load_page = load_offset >> 12;
load_page_expand = load_page << 12;
pages_to_load = ((_end - load_page) >> 12) + 1;
sectors_to_load = ((_end - load_offset) >> 9) + 1;
mmap_special_page = load_page - 1;
mmap_special_page_virtaddr = mmap_special_page << 12;
mmap_special_page_pagetable = load_page - 2;
mmap_special_page_pagetable_virtaddr = mmap_special_page_pagetable << 12;
pmmalloc_special_page = load_page - 3;
pmmalloc_special_page_virtaddr = pmmalloc_special_page << 12;
pmmalloc_special_page_pagetable = load_page - 4;
pmmalloc_special_page_pagetable_virtaddr = pmmalloc_special_page_pagetable << 12;
mm_pml4_rm_segment = (load_page + pages_to_load) << 8;
mm_pml4_offset = 0;
mm_pml4_offset_0 = (mm_pml4_rm_segment << 4) + mm_pml4_offset;
mm_pml4_offset_1003 = mm_pml4_offset_0 + 0x1003;
mm_pml4_offset_2003 = mm_pml4_offset_0 + 0x2003;
mm_pml4_offset_3003 = mm_pml4_offset_0 + 0x3003;
mm_pml4_offset_4007 = mm_pml4_offset_0 + 0x4007;
mm_pml4_offset_5007 = mm_pml4_offset_0 + 0x5007;
mm_pml4_offset_6003 = mm_pml4_offset_0 + 0x6003;
/* kernel_stack_size = 0x2000; */
trap_div0_virtual = trap_div0;
trap_div0_virtual_16 = trap_div0_virtual & 0xffff;
trap_div0_virtual_shr16 = (trap_div0_virtual >> 16) & 0xffff;
trap_div0_virtual_shr32 = trap_div0_virtual >> 32;
trap_doubleFault_virtual = trap_doubleFault;
trap_doubleFault_virtual_16 = trap_doubleFault_virtual & 0xffff;
trap_doubleFault_virtual_shr16 = (trap_doubleFault_virtual >> 16) & 0xffff;
trap_doubleFault_virtual_shr32 = trap_doubleFault_virtual >> 32;
trap_invalidTSS_virtual = trap_invalidTSS;
trap_invalidTSS_virtual_16 = trap_invalidTSS_virtual & 0xffff;
trap_invalidTSS_virtual_shr16 = (trap_invalidTSS_virtual >> 16) & 0xffff;
trap_invalidTSS_virtual_shr32 = trap_invalidTSS_virtual >> 32;
trap_generalProtectionFault_virtual = trap_generalProtectionFault;
trap_generalProtectionFault_virtual_16 = trap_generalProtectionFault_virtual & 0xffff;
trap_generalProtectionFault_virtual_shr16 = (trap_generalProtectionFault_virtual >> 16) & 0xffff;
trap_generalProtectionFault_virtual_shr32 = trap_generalProtectionFault_virtual >> 32;
trap_pageFault_virtual = trap_pageFault;
trap_pageFault_virtual_16 = trap_pageFault_virtual & 0xffff;
trap_pageFault_virtual_shr16 = (trap_pageFault_virtual >> 16) & 0xffff;
trap_pageFault_virtual_shr32 = trap_pageFault_virtual >> 32;
trap_invalidSyscall_virtual = trap_invalidSyscall;
trap_invalidSyscall_virtual_16 = trap_invalidSyscall_virtual & 0xffff;
trap_invalidSyscall_virtual_shr16 = (trap_invalidSyscall_virtual >> 16) & 0xffff;
trap_invalidSyscall_virtual_shr32 = trap_invalidSyscall_virtual >> 32;
isr_spurious_virtual = isr_spurious;
isr_spurious_virtual_16 = isr_spurious_virtual & 0xffff;
isr_spurious_virtual_shr16 = (isr_spurious_virtual >> 16) & 0xffff;
isr_spurious_virtual_shr32 = isr_spurious_virtual >> 32;
isr_dummytmr_virtual = isr_dummytmr;
isr_dummytmr_virtual_16 = isr_dummytmr_virtual & 0xffff;
isr_dummytmr_virtual_shr16 = (isr_dummytmr_virtual >> 16) & 0xffff;
isr_dummytmr_virtual_shr32 = isr_dummytmr_virtual >> 32;
isr_userDummy_virtual = isr_userDummy;
isr_userDummy_virtual_16 = isr_userDummy_virtual & 0xffff;
isr_userDummy_virtual_shr16 = (isr_userDummy_virtual >> 16) & 0xffff;
isr_userDummy_virtual_shr32 = isr_userDummy_virtual >> 32;
tss_virtual = code_virtaddr + TSS;
tss_virtual_16 = tss_virtual & 0xffff;
tss_virtual_shr16_8 = (tss_virtual >> 16) & 0xff;
tss_virtual_shr24_8 = (tss_virtual >> 24) & 0xff;
tss_virtual_shr32 = tss_virtual >> 32;