summaryrefslogtreecommitdiff
path: root/na/linker.ld
blob: 54f619c288e36c3f51eecbec1643ceda58a8a52d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (C) 2026 Richard Qin */

/*
 * LumeOS Kernel Linker Script
 *
 * Memory Layout (SV39 Higher-Half Kernel):
 *   Virtual Base:  0xFFFFFFC080200000
 *   Physical Base: 0x80200000
 *   VA-PA Offset:  0xFFFFFFC000000000 (PAGE_OFFSET)
 *
 * The AT() directives produce correct LMA (Load Memory Address) so the
 * bootloader loads sections at their physical addresses while the kernel
 * code references their virtual addresses.
 *
 * Reference: docs/specs/linker_and_entry.md
 */

OUTPUT_ARCH(riscv)

PAGE_OFFSET  = 0xFFFFFFC000000000;
BASE_ADDRESS = 0xFFFFFFC080200000;
PHYS_BASE    = 0x80200000;

/* ELF entry point must be the PHYSICAL address because OpenSBI/QEMU
 * jumps to it before MMU is enabled. _start is at VMA, so we use
 * the computed physical equivalent. */
ENTRY(_start)

SECTIONS {
    . = BASE_ADDRESS;

    /* ===== .head.text: Image Header (must be at byte 0) ===== */
    .head.text : AT(PHYS_BASE) {
        _start = .;
        KEEP(*(.head.text))
    }

    /* ===== .text: Executable Code ===== */
    .text : AT(ADDR(.text) - PAGE_OFFSET) {
        _stext = .;
        *(.text .text.*)
        _etext = .;
    } = 0

    . = ALIGN(4096);

    /* ===== .rodata: Read-Only Data ===== */
    .rodata : AT(ADDR(.rodata) - PAGE_OFFSET) {
        _srodata = .;
        *(.rodata .rodata.*)

        /* C++ global constructor table (.init_array) */
        . = ALIGN(8);
        __init_array_start = .;
        KEEP(*(.init_array .init_array.*))
        __init_array_end = .;

        _erodata = .;
    }

    . = ALIGN(4096);

    /* ===== .data: Initialized Read-Write Data ===== */
    .data : AT(ADDR(.data) - PAGE_OFFSET) {
        _sdata = .;
        *(.data .data.*)

        /* GP anchor: RISC-V medany model uses gp-relative addressing
         * for .sdata/.sbss within +/- 2KB of __global_pointer$ */
        . = ALIGN(16);
        PROVIDE(__global_pointer$ = . + 0x800);
        *(.sdata .sdata.*)

        _edata = .;
    }

    . = ALIGN(4096);

    /* ===== .bss: Uninitialized Data (zeroed by entry.S) ===== */
    .bss : AT(ADDR(.bss) - PAGE_OFFSET) {
        _sbss = .;
        *(.bss .bss.*)
        *(.sbss .sbss.*)

        /* Early page table: 4KB aligned, used by entry.S before VMM */
        . = ALIGN(4096);
        early_pgdir = .;
        . += 4096;

        /* Per-CPU boot stacks: 8KB per CPU, max 8 CPUs */
        . = ALIGN(4096);
        boot_stack = .;
        . += 8192 * 8;      /* NCPU_MAX = 8, BOOT_STACK_SIZE = 8KB */
        boot_stack_top = .;

        _ebss = .;
    }

    . = ALIGN(4096);

    /* ===== End of kernel image ===== */
    _kernel_end = .;

    /* Discard unwanted sections */
    /DISCARD/ : {
        *(.eh_frame .eh_frame_hdr)
        *(.note .note.*)
        *(.comment)
    }
}