xv6, line by line
user/user.ld

user/user.ld

linker script · 39 lines · annotated 100% · user program / library · upstream

About this file

The linker script for every xv6 user program (except _forktest, which the Makefile links with command-line options instead, Makefile:120). It tells the linker (ld) how to lay out the program’s code and data in its virtual address space. Read it side by side with the kernel’s kernel/kernel.ld; the structure is the same, the differences are instructive:

kernel/kernel.ld user/user.ld
starts at 0x80000000, where RAM is 0, the bottom of the process’s own address space
entry point ENTRY( _entry ) none; ld falls back to the symbol start
special code the trampoline page none
before .data nothing (code end etext is page-aligned) ALIGN(0x1000)
.eh_frame not mentioned collected after .rodata
end symbol used by kinit defined, but no xv6 program uses it

The result for user/_cat (from ${TOOLPREFIX}readelf -l user/_cat; TOOLPREFIX is your RISC-V toolchain’s prefix; see Tour 1: From make qemu to a disk image and a kernel):

0x0000   .text    code               ┐ segment 1: read + execute
0x09a8   .rodata  constants, strings ┘
0x1000   .data    initialized vars   ┐ segment 2: read + write
0x1000   .bss     zeroed vars        ┘ (0x220 bytes)

kexec loads each segment at the address the linker chose, then places the stack (and later the heap) above them. See user memory layout.

Read before: kernel/kernel.ld, kernel/exec.c.

1OUTPUT_ARCH( "riscv" )
3SECTIONS
4{
5 . = 0x0;
6
7 .text : {
8 *(.text .text.*)
9 }
12 . = ALIGN(16);
13 *(.srodata .srodata.*) /* do not need to distinguish this from .rodata */
14 . = ALIGN(16);
15 *(.rodata .rodata.*)
16 }
19 *(.eh_frame)
20 *(.eh_frame.*)
21 }
23 . = ALIGN(0x1000);
24 .data : {
25 . = ALIGN(16);
26 *(.sdata .sdata.*) /* do not need to distinguish this from .data */
27 . = ALIGN(16);
28 *(.data .data.*)
29 }
31 .bss : {
32 . = ALIGN(16);
33 *(.sbss .sbss.*) /* do not need to distinguish this from .bss */
34 . = ALIGN(16);
35 *(.bss .bss.*)
36 }
38 PROVIDE(end = .);