操作系统项目:从Boot Loader加载ELF内C函数的方式是否正确?
Let's walk through your setup and pinpoint where things might be going wrong with loading the ELF kernel and calling your kmain function.
First: Is Your Entry Address Calculation Correct?
Your current code tries to compute the entry point like this:
xor edx, edx mov edx, 24 add edx, IMAGE_PMODE_BASE add ebx, dword[edx] add ebx, IMAGE_PMODE_BASE call ebx
This has a critical mistake: you're double-adding IMAGE_PMODE_BASE to the entry address.
In a 32-bit ELF executable, the e_entry field (at offset 24 in the ELF header) already represents the linear address of the entry point (start in your case) if you linked the kernel correctly to load at IMAGE_PMODE_BASE. Adding the base address again will point you to an invalid memory location, which explains why the call fails.
Key Fixes to Get It Working
1. Fix Your Linker Script
Your current linker script only sets ENTRY(start)—it doesn't specify where the kernel should be loaded in memory. This means ld will use a default base address (like 0x08048000), which almost certainly doesn't match your IMAGE_PMODE_BASE.
Update your linker script to explicitly set the load address to IMAGE_PMODE_BASE (replace with your actual base address, e.g., 0x100000 for 1MB):
ENTRY(start) SECTIONS { . = IMAGE_PMODE_BASE; /* Set base address to where you'll load the kernel */ .text ALIGN(4) : { *(.text) } /* Code section, aligned to 4 bytes */ .data ALIGN(4) : { *(.data) } /* Initialized data */ .bss ALIGN(4) : { /* Uninitialized data */ __bss_start = .; *(.bss) *(COMMON) __bss_end = .; } }
This ensures the linker sets e_entry to the correct linear address (e.g., IMAGE_PMODE_BASE + offset of start if start is at the top of .text).
2. Correct the Entry Point Call in Your Bootloader
Once the linker script is fixed, you can directly use the e_entry value from the ELF header as the call address. Assuming ebx points to the start of the ELF file in memory (i.e., IMAGE_PMODE_BASE), simplify your code to:
; ebx = IMAGE_PMODE_BASE (start of ELF file in memory) mov eax, dword[ebx + 24] ; Load e_entry from ELF header offset 24 call eax ; Call the kernel entry point (start)
No need to add IMAGE_PMODE_BASE again—e_entry is already the full linear address.
3. Ensure Proper Stack Setup Before Calling C Code
Your kmain function uses a local variable (int a = 5), which requires a valid stack. If you haven't set up the stack pointer (ESP) in 32-bit protected mode, the C code will crash when trying to access the stack.
Add this code right after entering 32-bit protected mode (before calling the kernel entry point):
mov esp, 0x90000 ; Set stack to a safe location (e.g., 576KB mark) ; Make sure this doesn't overlap with your kernel's loaded address
4. Verify Protected Mode Segment Configuration
Double-check your GDT (Global Descriptor Table) setup. Your code segment descriptor should allow full access to the 4GB address space, with the correct privilege level (0, since this is kernel code). A minimal valid GDT might look like:
gdt_start: gdt_null: ; Mandatory null descriptor dd 0x0 dd 0x0 gdt_code: ; Kernel code segment dw 0xFFFF ; Limit (low 16 bits) dw 0x0 ; Base (low 16 bits) db 0x0 ; Base (middle 8 bits) db 0x9A ; Access: Present, Ring 0, Code segment, Readable db 0xCF ; Flags: 4KB granularity, 32-bit mode, Limit (high 8 bits) db 0x0 ; Base (high 8 bits) gdt_data: ; Kernel data segment dw 0xFFFF dw 0x0 db 0x0 db 0x92 ; Access: Present, Ring 0, Data segment, Writable db 0xCF db 0x0 gdt_end: gdt_descriptor: dw gdt_end - gdt_start - 1 ; GDT size dd gdt_start ; GDT base address
When entering protected mode, load the GDTR and set CS to the code segment selector (e.g., 0x08), and DS, ES, SS to the data segment selector (e.g., 0x10).
Final Checks
- Recompile all components with the updated linker script:
cc -m32 -nostdlib -nostdinc -fno-builtin -fno-stack-protector -c -o kmain.o kmain.c nasm -f elf loader.asm -o loader.o ld -m elf_i386 -T link.ld -o kernel loader.o kmain.o - Confirm your bootloader correctly loads the entire ELF file into memory at
IMAGE_PMODE_BASE(not just the header—make sure you parse the program headers to load all segments to their correct addresses).
With these fixes, your bootloader should correctly call start, which in turn calls kmain without issues.
内容的提问来源于stack exchange,提问作者snehm

