import{_ as n,c as e,e as a,o as i}from"./app-CjdILvTf.js";const l={};function t(p,s){return i(),e("div",null,s[0]||(s[0]=[a(`<h1 id="kernel-image" tabindex="-1"><a class="header-anchor" href="#kernel-image"><span>Kernel Image</span></a></h1><p>To start simple, we&#39;ll build a flat binary kernel image instead of using an executable format like PE or ELF. This makes the job of the bootloader easier, since it doesn&#39;t have to parse a complex executable format or fix up relocations. All it has to do is load the kernel image into memory and jump to the entry point.</p><h2 id="project-structure" tabindex="-1"><a class="header-anchor" href="#project-structure"><span>Project structure</span></a></h2><p>Before we start writing the kernel, let&#39;s organize our project to separate the kernel from the bootloader modules, so that we can build them separately. Under the <code>src</code> directory we&#39;ll create a <code>boot</code> directory for the bootloader modules, and a <code>kernel</code> directory for the kernel modules. We&#39;ll also create a <code>common</code> directory for shared modules. Here&#39;s what the project structure looks like:</p><div class="language-text line-numbers-mode" data-highlighter="prismjs" data-ext="text" data-title="text"><pre><code><span class="line">.</span>
<span class="line">├── build</span>
<span class="line">├── src</span>
<span class="line">│   ├── boot</span>
<span class="line">│   ├── common</span>
<span class="line">│   └── kernel</span>
<span class="line">└── nim.cfg</span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Now let&#39;s move the existing modules into their respective directories. Let&#39;s also create an empty <code>nim.cfg</code> file in the <code>boot</code> and <code>kernel</code> directories. We&#39;ll use these files to customize the build for the bootloader and the kernel. Nim will automatically pick up the <code>nim.cfg</code> file in the directory of the module that we&#39;re compiling. It also will recursively look for <code>nim.cfg</code> files in the parent directories. This allows us to have a common <code>nim.cfg</code> file in the project root directory, and provide specific configurations in the <code>nim.cfg</code> files in the <code>boot</code> and <code>kernel</code> directories.</p><div class="language-text line-numbers-mode" data-highlighter="prismjs" data-ext="text" data-title="text"><pre><code><span class="line">.</span>
<span class="line">├── build</span>
<span class="line">├── src</span>
<span class="line">│   ├── boot</span>
<span class="line">│   │   ├── bootx64.nim</span>
<span class="line">│   │   └── nim.cfg</span>
<span class="line">│   ├── common</span>
<span class="line">│   │   ├── libc.nim</span>
<span class="line">│   │   ├── malloc.nim</span>
<span class="line">│   │   └── uefi.nim</span>
<span class="line">│   └── kernel</span>
<span class="line">│       ├── main.nim</span>
<span class="line">│       └── nim.cfg</span>
<span class="line">└── nim.cfg</span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Let&#39;s move the following part of the <code>nim.cfg</code> file into the <code>nim.cfg</code> file in the <code>boot</code> directory:</p><div class="language-properties line-numbers-mode" data-highlighter="prismjs" data-ext="properties" data-title="properties"><pre><code><span class="line"><span class="token comment"># src/boot/nim.cfg</span></span>
<span class="line"></span>
<span class="line"><span class="token key attr-name">--passc</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-target x86_64-unknown-windows&quot;</span></span>
<span class="line"><span class="token key attr-name">--passc</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-ffreestanding&quot;</span></span>
<span class="line"></span>
<span class="line"><span class="token key attr-name">--passl</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-target x86_64-unknown-windows&quot;</span></span>
<span class="line"><span class="token key attr-name">--passl</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-fuse-ld=lld-link&quot;</span></span>
<span class="line"><span class="token key attr-name">--passl</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-nostdlib&quot;</span></span>
<span class="line"><span class="token key attr-name">--passl</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-Wl,-entry:EfiMain&quot;</span></span>
<span class="line"><span class="token key attr-name">--passl</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-Wl,-subsystem:efi_application&quot;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Let&#39;s also tell Nim to add the <code>src</code> directory to its search path, so that we can import modules from <code>boot</code>, <code>common</code>, and <code>kernel</code> without using relative paths. We&#39;ll put this in the top-level <code>nim.cfg</code> file:</p><div class="language-properties line-numbers-mode" data-highlighter="prismjs" data-ext="properties" data-title="properties"><pre><code><span class="line"><span class="token comment"># nim.cfg</span></span>
<span class="line">...</span>
<span class="line"></span>
<span class="line"><span class="token key attr-name">--path</span><span class="token punctuation">:</span><span class="token value attr-value">src</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>We&#39;ll work on what to use in the kernel&#39;s <code>nim.cfg</code> file later.</p><p>Let&#39;s also add a task in our <code>justfile</code> to build the the kernel:</p><div class="language-justfile line-numbers-mode" data-highlighter="prismjs" data-ext="justfile" data-title="justfile"><pre><code><span class="line"># justfile</span>
<span class="line"></span>
<span class="line">nimflags := &quot;--os:any&quot;</span>
<span class="line"></span>
<span class="line">bootloader:</span>
<span class="line">  nim c {{nimflags}} src/boot/bootx64.nim --out:build/bootx64.efi</span>
<span class="line"></span>
<span class="line highlighted">kernel:</span>
<span class="line highlighted">  nim c {{nimflags}} src/kernel/main.nim --out:build/kernel.bin</span>
<span class="line"></span>
<span class="line">run: bootloader</span>
<span class="line">  mkdir -p diskimg/efi/boot</span>
<span class="line">  cp build/bootx64.efi diskimg/efi/boot/bootx64.efi</span>
<span class="line">  qemu-system-x86_64 \\</span>
<span class="line">    -drive if=pflash,format=raw,file=ovmf/OVMF_CODE.fd,readonly=on \\</span>
<span class="line">    -drive if=pflash,format=raw,file=ovmf/OVMF_VARS.fd \\</span>
<span class="line">    -drive format=raw,file=fat:rw:diskimg \\</span>
<span class="line">    -machine q35 \\</span>
<span class="line">    -net none</span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="debug-output" tabindex="-1"><a class="header-anchor" href="#debug-output"><span>Debug output</span></a></h2><p>We cannot rely on any UEFI services in the kernel; the bootloader will exit UEFI Boot Services before jumping to the kernel. This means that we will not be able to use the UEFI console to print to the screen. The kernel will have to write directly to the graphics framebuffer, but we&#39;ll get to that later.</p><p>Typically, at this early stage of the kernel startup, the serial port is used to print debug messages. But I don&#39;t want to implement a serial port driver yet. Since we&#39;re using QEMU, we can leverage its debug console <code>debugcon</code> to print messages by configuring it to send its output to <code>stdio</code> using the switch <code>-debugcon stdio</code>. This will print the debug messages to the terminal that we&#39;re running QEMU from. The way this feature works is by sending characters to port <code>0xE9</code>, which is the debug port. Let&#39;s create a <code>debugcon</code> module implement procedures that prints a string to the debug console:</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/debugcon.nim</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">const</span></span>
<span class="line">  DebugConPort <span class="token operator">=</span> <span class="token number">0xE9</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">proc</span> <span class="token function">portOut8</span><span class="token punctuation">(</span>port<span class="token operator">:</span> uint16<span class="token punctuation">,</span> data<span class="token operator">:</span> uint8<span class="token punctuation">)</span> <span class="token operator">=</span></span>
<span class="line">  <span class="token keyword">asm</span> <span class="token string">&quot;&quot;&quot;</span>
<span class="line">    out %0, %1</span>
<span class="line">    :</span>
<span class="line">    :&quot;Nd&quot;(\`port\`), &quot;a&quot;(\`data\`)</span>
<span class="line">  &quot;&quot;&quot;</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">proc</span> <span class="token function">debug<span class="token operator">*</span></span><span class="token punctuation">(</span>msgs<span class="token operator">:</span> varargs<span class="token punctuation">[</span>string<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">=</span></span>
<span class="line">  <span class="token comment">## Send messages to the debug console.</span></span>
<span class="line">  <span class="token keyword">for</span> msg <span class="token operator">in</span> msgs<span class="token operator">:</span></span>
<span class="line">    <span class="token keyword">for</span> ch <span class="token operator">in</span> msg<span class="token operator">:</span></span>
<span class="line">      <span class="token function">portOut8</span><span class="token punctuation">(</span>DebugConPort<span class="token punctuation">,</span> ch<span class="token operator">.</span>uint8<span class="token punctuation">)</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">proc</span> <span class="token function">debugln<span class="token operator">*</span></span><span class="token punctuation">(</span>msgs<span class="token operator">:</span> varargs<span class="token punctuation">[</span>string<span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token operator">=</span></span>
<span class="line">  <span class="token comment">## Send messages to the debug console. A newline is appended at the end.</span></span>
<span class="line">  <span class="token function">debug</span><span class="token punctuation">(</span>msgs<span class="token punctuation">)</span></span>
<span class="line">  <span class="token function">debug</span><span class="token punctuation">(</span><span class="token string">&quot;\\r\\n&quot;</span><span class="token punctuation">)</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>We can now use the <code>debug</code> and <code>debugln</code> procedures to print messages to the debug console.</p><h2 id="entry-point" tabindex="-1"><a class="header-anchor" href="#entry-point"><span>Entry point</span></a></h2><p>The kernel entry point is the first function that is executed by the bootloader. We&#39;ll call this function <code>KernelMain</code>. For now, it will just print a message to the debug console and halt the CPU.</p><div class="language-nim line-numbers-mode" data-highlighter="prismjs" data-ext="nim" data-title="nim"><pre><code><span class="line"><span class="token comment"># src/kernel/main.nim</span></span>
<span class="line"></span>
<span class="line"><span class="token keyword">import</span> debugcon<span class="token punctuation">,</span> libc<span class="token punctuation">,</span> malloc</span>
<span class="line"></span>
<span class="line"><span class="token keyword">proc</span> <span class="token function">KernelMain</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{.</span>exportc<span class="token punctuation">.}</span> <span class="token operator">=</span></span>
<span class="line">  debugln <span class="token string">&quot;Hello, world!&quot;</span></span>
<span class="line">  <span class="token function">quit</span><span class="token punctuation">(</span><span class="token punctuation">)</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Similar to what we did in the bootloader, we import <code>libc</code> and <code>malloc</code> since we&#39;re compiling for a freestanding environment. Now let&#39;s see how we can compile this minimal kernel.</p><h2 id="c-compiler-options" tabindex="-1"><a class="header-anchor" href="#c-compiler-options"><span>C compiler options</span></a></h2><p>Compiling a kernel is not the same as compiling a regular application. The C compiler has some default behaviour that will cause problems for us.</p><p>The first one is the way it saves and restores registers during function calls. By default, the compiler saves an extensive set of registers, including the SSE registers. This is not necessary for our kernel, since we&#39;re using only integer instructions and registers. This can be turned off using the <code>-mgeneral-regs-only</code> switch.</p><p>The other one is so-called red zone. Normally, the compiler allocates a stack frame for each function call. This stack frame is used to store local variables and function arguments. This is done by subtracting the necessary space from the <code>rsp</code> register. However, when the compiler detects that a function is a leaf function, i.e. it doesn&#39;t call any other functions, it does not allocate a stack frame (i.e. it doesn&#39;t subtract from <code>rsp</code>). Instead, it uses the 128 bytes below <code>rsp</code> as a scratch space for local variables. This is called the red zone, a performance optimization to avoid the overhead of allocating a stack frame. This is not a problem for regular applications, but for a kernel, if an interrupt is triggered while we&#39;re in kernel mode, the CPU will use the same stack, and will overwrite the red zone. This corrupts the state of the kernel, and can be extremely difficult to debug. To avoid this, we&#39;ll disable the red zone using the <code>-mno-red-zone</code> switch.</p><p>Let&#39;s add these switches to the compiler arguments in the kernel&#39;s <code>nim.cfg</code>:</p><div class="language-properties line-numbers-mode" data-highlighter="prismjs" data-ext="properties" data-title="properties"><pre><code><span class="line"><span class="token comment"># src/kernel/nim.cfg</span></span>
<span class="line">...</span>
<span class="line"></span>
<span class="line"><span class="token key attr-name">--passc</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-mgeneral-regs-only&quot;</span></span>
<span class="line"><span class="token key attr-name">--passc</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-mno-red-zone&quot;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="linking-the-kernel" tabindex="-1"><a class="header-anchor" href="#linking-the-kernel"><span>Linking the kernel</span></a></h2><p>Our goal is to build a raw binary kernel image. We can do this by passing the <code>--oformat=binary</code> switch to the linker. But before we do this, we have to understand how the bootloader will load the kernel image into memory and jump to the entry point.</p><p>A flat binary image doesn&#39;t have metadata to specify an entry point, so the bootloader and the kernel have to agree on a convention. The convention that we&#39;ll use is to place the entry point at the beginning of the image. This means that the bootloader will load the kernel image into memory and jump to the beginning of the image. Since the binary image is not relocatable, the kernel has to be linked at a specific address. We&#39;ll use the address <code>0x100000</code> (1 MiB) for the kernel image. The reason for this particular address is that below this address (specifically the region between 640 KiB to 1 MiB) is reserved for legacy BIOS compatibility (VGA memory, BIOS ROM, etc.) and is not accessible as RAM.</p><p>OK, how do we tell the linker to link the kernel at a specific address? We use a linker script. A linker script is a text file that tells the linker how to map sections from the input object files to sections in the output image, and in what order, and at what address. But before we use a linker script let&#39;s link the kernel without one, and see what sections are included in the output image.</p><p>The <code>lld-link</code> linker that we&#39;ve been using so far (to generate a PE image) doesn&#39;t seem to support linker scripts (at least I couldn&#39;t find a way to do it). That&#39;s OK; we don&#39;t want the PE format anymore, it was only needed for the UEFI bootloader. So for the kernel, we&#39;ll switch to using the <code>ld.lld</code> linker, which is the LLVM linker for Unix systems. The most widely used executable format on Unix systems is ELF, so we&#39;ll use that as well. We&#39;ll come back later to building a raw binary image.</p><p>Let&#39;s add some arguments in <code>src/kernel/nim.cfg</code> to use <code>ld.lld</code> and generate an ELF executable:</p><div class="language-properties line-numbers-mode" data-highlighter="prismjs" data-ext="properties" data-title="properties"><pre><code><span class="line"><span class="token comment"># src/kernel/nim.cfg</span></span>
<span class="line"></span>
<span class="line"><span class="token key attr-name">amd64.any.clang.linkerexe</span> <span class="token punctuation">=</span> <span class="token value attr-value">&quot;ld.lld&quot;</span></span>
<span class="line"></span>
<span class="line"><span class="token key attr-name">--passc</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-target x86_64-unknown-elf&quot;</span></span>
<span class="line"><span class="token key attr-name">--passc</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-ffreestanding&quot;</span></span>
<span class="line"></span>
<span class="line"><span class="token key attr-name">--passl</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-nostdlib&quot;</span></span>
<span class="line"><span class="token key attr-name">--passl</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-Map=build/kernel.map&quot;</span></span>
<span class="line"><span class="token key attr-name">--passl</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-entry KernelMain&quot;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>We&#39;re also passing the <code>-Map</code> switch to generate a linker map file. This is useful for showing us the address of each symbol in the output file. Now let&#39;s compile the kernel:</p><div class="language-sh-session line-numbers-mode" data-highlighter="prismjs" data-ext="sh-session" data-title="sh-session"><pre><code><span class="line"><span class="token command"><span class="token shell-symbol important">$</span> <span class="token bash language-bash">just kernel</span></span></span>
<span class="line"></span>
<span class="line"><span class="token command"><span class="token shell-symbol important">$</span> <span class="token bash language-bash"><span class="token function">file</span> build/kernel.bin</span></span></span>
<span class="line"><span class="token output">build/kernel.bin: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped</span>
<span class="line"></span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Great! We have an ELF executable kernel image. Let&#39;s see what&#39;s in it using <code>llvm-readelf</code> (I&#39;ve highlighted the interesting parts):</p><div class="language-sh-session line-numbers-mode" data-highlighter="prismjs" data-ext="sh-session" data-title="sh-session"><pre><code><span class="line"><span class="token command"><span class="token shell-symbol important">$</span> <span class="token bash language-bash">llvm-readelf <span class="token parameter variable">--headers</span> build/kernel.bin</span></span></span>
<span class="line"><span class="token output">ELF Header:</span>
<span class="line">  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00</span>
<span class="line">  Class:                             ELF64</span>
<span class="line">  Data:                              2&#39;s complement, little endian</span>
<span class="line">  Version:                           1 (current)</span>
<span class="line">  OS/ABI:                            UNIX - System V</span>
<span class="line">  ABI Version:                       0</span>
<span class="line">  Type:                              EXEC (Executable file)</span>
<span class="line">  Machine:                           Advanced Micro Devices X86-64</span>
<span class="line">  Version:                           0x1</span>
<span class="line highlighted">  Entry point address:               0x20D580</span>
<span class="line">  Start of program headers:          64 (bytes into file)</span>
<span class="line">  Start of section headers:          65648 (bytes into file)</span>
<span class="line">  Flags:                             0x0</span>
<span class="line">  Size of this header:               64 (bytes)</span>
<span class="line">  Size of program headers:           56 (bytes)</span>
<span class="line">  Number of program headers:         5</span>
<span class="line">  Size of section headers:           64 (bytes)</span>
<span class="line">  Number of section headers:         9</span>
<span class="line">  Section header string table index: 7</span>
<span class="line">There are 9 section headers, starting at offset 0x10070:</span>
<span class="line"></span>
<span class="line">Section Headers:</span>
<span class="line">  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al</span>
<span class="line">  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0</span>
<span class="line highlighted">  [ 1] .rodata           PROGBITS        0000000000200160 000160 000e90 00 AMS  0   0 16</span>
<span class="line highlighted">  [ 2] .text             PROGBITS        0000000000201ff0 000ff0 00b82b 00  AX  0   0 16</span>
<span class="line highlighted">  [ 3] .data             PROGBITS        000000000020e820 00c820 0000e0 00  WA  0   0  8</span>
<span class="line highlighted">  [ 4] .bss              NOBITS          000000000020e900 00c900 1004b0 00  WA  0   0 16</span>
<span class="line">  [ 5] .comment          PROGBITS        0000000000000000 00c900 00007d 01  MS  0   0  1</span>
<span class="line">  [ 6] .symtab           SYMTAB          0000000000000000 00c980 001b00 18      8 286  8</span>
<span class="line">  [ 7] .shstrtab         STRTAB          0000000000000000 00e480 00003d 00      0   0  1</span>
<span class="line">  [ 8] .strtab           STRTAB          0000000000000000 00e4bd 001bac 00      0   0  1</span>
<span class="line">Key to Flags:</span>
<span class="line">  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),</span>
<span class="line">  L (link order), O (extra OS processing required), G (group), T (TLS),</span>
<span class="line">  C (compressed), x (unknown), o (OS specific), E (exclude),</span>
<span class="line">  R (retain), l (large), p (processor specific)</span>
<span class="line"></span>
<span class="line">Elf file type is EXEC (Executable file)</span>
<span class="line">Entry point 0x20d580</span>
<span class="line">There are 5 program headers, starting at offset 64</span>
<span class="line"></span>
<span class="line">Program Headers:</span>
<span class="line">  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align</span>
<span class="line">  PHDR           0x000040 0x0000000000200040 0x0000000000200040 0x000118 0x000118 R   0x8</span>
<span class="line highlighted">  LOAD           0x000000 0x0000000000200000 0x0000000000200000 0x000ff0 0x000ff0 R   0x1000</span>
<span class="line highlighted">  LOAD           0x000ff0 0x0000000000201ff0 0x0000000000201ff0 0x00b82b 0x00b82b R E 0x1000</span>
<span class="line highlighted">  LOAD           0x00c820 0x000000000020e820 0x000000000020e820 0x0000e0 0x100590 RW  0x1000</span>
<span class="line">  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x0</span>
<span class="line"></span>
<span class="line"> Section to Segment mapping:</span>
<span class="line">  Segment Sections...</span>
<span class="line">   00     </span>
<span class="line highlighted">   01     .rodata </span>
<span class="line highlighted">   02     .text </span>
<span class="line highlighted">   03     .data .bss </span>
<span class="line">   04     </span>
<span class="line">   None   .comment .symtab .shstrtab .strtab</span>
<span class="line"></span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Here&#39;s what we can see from the output:</p><ul><li>The entry point is at address <code>0x20D580</code>, which is not what we wanted. We wanted the entry point to be at address <code>0x100000</code>. We&#39;ll fix this later.</li><li>The sections that we&#39;re interested in are <code>.text</code>, <code>.rodata</code>, <code>.data</code>, and <code>.bss</code>. The <code>.text</code> section contains the code, the <code>.rodata</code> section contains read-only data, the <code>.data</code> section contains initialized data, and the <code>.bss</code> section contains uninitialized data. These are the sections that we want to include in the kernel image.</li><li>There are other sections that we&#39;re not interested in (<code>.comment</code>, <code>.symtab</code>, <code>.shstrtab</code>, and <code>.strtab</code>). These sections are used for debugging information, and we don&#39;t need them in the output image. We&#39;ll discard these sections later.</li></ul><p>Keep in mind that these are the output sections as generated by the linker. The inputs sections from the object files are mapped to these output sections. So in order to write our own linker script, we need to know what sections are generated by the compiler for each object file. Let&#39;s take a look at one of the object files that were generated by the compiler:</p><div class="language-sh-session line-numbers-mode" data-highlighter="prismjs" data-ext="sh-session" data-title="sh-session"><pre><code><span class="line"><span class="token command"><span class="token shell-symbol important">$</span> <span class="token bash language-bash">llvm-objdump --section-headers build/@mmain.nim.c.o</span></span></span>
<span class="line"></span>
<span class="line"><span class="token output">build/@mmain.nim.c.o: file format elf64-x86-64</span>
<span class="line"></span>
<span class="line">Sections:</span>
<span class="line">Idx Name            Size     VMA              Type</span>
<span class="line">  0                 00000000 0000000000000000 </span>
<span class="line">  1 .strtab         0000028d 0000000000000000 </span>
<span class="line highlighted">  2 .text           0000029b 0000000000000000 TEXT</span>
<span class="line">  3 .rela.text      00000288 0000000000000000 </span>
<span class="line highlighted">  4 .rodata.str1.1  0000009f 0000000000000000 DATA</span>
<span class="line highlighted">  5 .rodata         00000050 0000000000000000 DATA</span>
<span class="line">  6 .rela.rodata    00000030 0000000000000000 </span>
<span class="line">  7 .comment        0000006a 0000000000000000 </span>
<span class="line">  8 .note.GNU-stack 00000000 0000000000000000 </span>
<span class="line">  9 .llvm_addrsig   00000015 0000000000000000 </span>
<span class="line"> 10 .symtab         00000270 0000000000000000 </span>
<span class="line"></span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>We can see that there are two read-only data sections (<code>.rodata.str1.1</code> and <code>.rodata</code>). The <code>rodata.str1.1</code> section contains string literals, so we&#39;ll need to include it in the kernel image. The other sections (other than <code>.text</code>) are not relevant to us.</p><p>OK, let&#39;s create a linker script called <code>kernel.ld</code> in the kernel directory, and map the sections that we&#39;re interested in to the output sections that we saw earlier, and discard all other sections:</p><div class="language-ld line-numbers-mode" data-highlighter="prismjs" data-ext="ld" data-title="ld"><pre><code><span class="line"><span class="token comment">/* src/kernel/kernel.ld */</span></span>
<span class="line"></span>
<span class="line">SECTIONS</span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">  <span class="token location-counter important">.</span> <span class="token operator">=</span> <span class="token number">0x100000</span><span class="token punctuation">;</span></span>
<span class="line">  <span class="token section keyword">.text</span>   <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.text</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.rodata</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.rodata</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.data</span>   <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.data</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.bss</span>    <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.bss</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">  <span class="token operator">/</span>DISCARD<span class="token operator">/</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>This tells the linker that the image will be loaded at address <code>0x100000</code>, and that the <code>.text</code> section (from all object files), the <code>.data</code> section, the <code>.rodata</code>, and the <code>.bss</code> section should be placed in the output file, in this order.</p><p>Let&#39;s add the linker script to the linker arguments in <code>nim.cfg</code>:</p><div class="language-properties line-numbers-mode" data-highlighter="prismjs" data-ext="properties" data-title="properties"><pre><code><span class="line"><span class="token comment"># src/kernel/nim.cfg</span></span>
<span class="line"></span>
<span class="line"><span class="token key attr-name">--passl</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-T src/kernel/kernel.ld&quot;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Let&#39;s compile the kernel again, this time using the linker script:</p><div class="language-sh-session line-numbers-mode" data-highlighter="prismjs" data-ext="sh-session" data-title="sh-session"><pre><code><span class="line"><span class="token command"><span class="token shell-symbol important">$</span> <span class="token bash language-bash">just kernel</span></span></span>
<span class="line"><span class="token output">...</span>
<span class="line">ld.lld: error: discarding .shstrtab section is not allowed</span>
<span class="line"></span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Oops, we can&#39;t discard the <code>.shstrtab</code> section. This section contains the names of the sections, and is required to identify the sections in the output file. Let&#39;s add an entry for it in the linker script:</p><div class="language-ld line-numbers-mode" data-highlighter="prismjs" data-ext="ld" data-title="ld"><pre><code><span class="line"><span class="token comment">/* src/kernel/kernel.ld */</span></span>
<span class="line"></span>
<span class="line">SECTIONS</span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">  <span class="token location-counter important">.</span> <span class="token operator">=</span> <span class="token number">0x100000</span><span class="token punctuation">;</span></span>
<span class="line">  <span class="token section keyword">.text</span>     <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.text</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.rodata</span>   <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.rodata</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.data</span>     <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.data</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.bss</span>      <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.bss</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.shstrtab</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.shstrtab</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">  <span class="token operator">/</span>DISCARD<span class="token operator">/</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Let&#39;s compile the kernel again:</p><div class="language-sh-session line-numbers-mode" data-highlighter="prismjs" data-ext="sh-session" data-title="sh-session"><pre><code><span class="line"><span class="token command"><span class="token shell-symbol important">$</span> <span class="token bash language-bash">ELF Header:</span></span></span>
<span class="line"><span class="token output">  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00</span>
<span class="line">  Class:                             ELF64</span>
<span class="line">  Data:                              2&#39;s complement, little endian</span>
<span class="line">  Version:                           1 (current)</span>
<span class="line">  OS/ABI:                            UNIX - System V</span>
<span class="line">  ABI Version:                       0</span>
<span class="line">  Type:                              EXEC (Executable file)</span>
<span class="line">  Machine:                           Advanced Micro Devices X86-64</span>
<span class="line">  Version:                           0x1</span>
<span class="line highlighted">  Entry point address:               0x10B590</span>
<span class="line">  Start of program headers:          64 (bytes into file)</span>
<span class="line">  Start of section headers:          55240 (bytes into file)</span>
<span class="line">  Flags:                             0x0</span>
<span class="line">  Size of this header:               64 (bytes)</span>
<span class="line">  Size of program headers:           56 (bytes)</span>
<span class="line">  Number of program headers:         4</span>
<span class="line">  Size of section headers:           64 (bytes)</span>
<span class="line">  Number of section headers:         6</span>
<span class="line">  Section header string table index: 5</span>
<span class="line">There are 6 section headers, starting at offset 0xd7c8:</span>
<span class="line"></span>
<span class="line">Section Headers:</span>
<span class="line">  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al</span>
<span class="line">  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0</span>
<span class="line highlighted">  [ 1] .text             PROGBITS        0000000000100000 001000 00b82b 00  AX  0   0 16</span>
<span class="line highlighted">  [ 2] .rodata           PROGBITS        000000000010b830 00c830 000e90 00 AMS  0   0 16</span>
<span class="line highlighted">  [ 3] .data             PROGBITS        000000000010c6c0 00d6c0 0000e0 00  WA  0   0  8</span>
<span class="line highlighted">  [ 4] .bss              NOBITS          000000000010c7a0 00d7a0 1004b0 00  WA  0   0 16</span>
<span class="line">  [ 5] .shstrtab         STRTAB          0000000000000000 00d7a0 000024 00      0   0  1</span>
<span class="line">Key to Flags:</span>
<span class="line">  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),</span>
<span class="line">  L (link order), O (extra OS processing required), G (group), T (TLS),</span>
<span class="line">  C (compressed), x (unknown), o (OS specific), E (exclude),</span>
<span class="line">  R (retain), l (large), p (processor specific)</span>
<span class="line"></span>
<span class="line">Elf file type is EXEC (Executable file)</span>
<span class="line">Entry point 0x10b590</span>
<span class="line">There are 4 program headers, starting at offset 64</span>
<span class="line"></span>
<span class="line">Program Headers:</span>
<span class="line">  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align</span>
<span class="line">  LOAD           0x001000 0x0000000000100000 0x0000000000100000 0x00b82b 0x00b82b R E 0x1000</span>
<span class="line">  LOAD           0x00c830 0x000000000010b830 0x000000000010b830 0x000e90 0x000e90 R   0x1000</span>
<span class="line">  LOAD           0x00d6c0 0x000000000010c6c0 0x000000000010c6c0 0x0000e0 0x100590 RW  0x1000</span>
<span class="line">  GNU_STACK      0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RW  0x0</span>
<span class="line"></span>
<span class="line"> Section to Segment mapping:</span>
<span class="line">  Segment Sections...</span>
<span class="line">   00     .text </span>
<span class="line">   01     .rodata </span>
<span class="line">   02     .data .bss </span>
<span class="line">   03     </span>
<span class="line">   None   .shstrtab</span>
<span class="line"></span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>OK, it looks like the section mapping worked as expected, but the entry point (<code>KernelMain</code>) is at <code>0x10B590</code> instead of <code>0x100000</code>. Let&#39;s take a look at the linker map file:</p><div class="language-sh-session line-numbers-mode" data-highlighter="prismjs" data-ext="sh-session" data-title="sh-session"><pre><code><span class="line"><span class="token command"><span class="token shell-symbol important">$</span> <span class="token bash language-bash"><span class="token function">head</span> <span class="token parameter variable">-n</span> <span class="token number">10</span> build/kernel.map</span></span></span>
<span class="line"><span class="token output">     VMA              LMA     Size Align Out     In      Symbol</span>
<span class="line">       0                0   100000     1 . = 0x100000</span>
<span class="line">  100000           100000     b82b    16 .text</span>
<span class="line">  100000           100000      9c3    16         .../fusion/build/@m..@s..@s..@s..@s..@s..@s.choosenim@stoolchains@snim-2.0.0@slib@ssystem@sexceptions.nim.c.o:(.text)</span>
<span class="line">  100000           100000       3b     1                 rttiDestroy__systemZexceptions_u56</span>
<span class="line">  100040           100040       38     1                 eqtrace___system_u4516</span>
<span class="line">  100080           100080       3b     1                 rttiDestroy__systemZexceptions_u60</span>
<span class="line">  1000c0           1000c0       38     1                 eqtrace___system_u4585</span>
<span class="line">  100100           100100       3b     1                 rttiDestroy__systemZexceptions_u62</span>
<span class="line">  100140           100140       38     1                 eqtrace___system_u4980</span>
<span class="line"></span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>We can see that the first object file in the output <code>.text</code> section is from Nim&#39;s standard library module <code>system/exceptions.nim</code>. This is because the linker uses the order of the object files in the command line to determine the order in the output image. We don&#39;t have much control over the order of the object files in the command line, since Nim generates the command line for us. What we can do is adjust the linker script a bit to tell it to put the kernel object file first.</p><div class="language-ld line-numbers-mode" data-highlighter="prismjs" data-ext="ld" data-title="ld"><pre><code><span class="line"><span class="token comment">/* src/kernel/kernel.ld */</span></span>
<span class="line"></span>
<span class="line">SECTIONS</span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">  <span class="token location-counter important">.</span> <span class="token operator">=</span> <span class="token number">0x100000</span><span class="token punctuation">;</span></span>
<span class="line highlighted">  <span class="token section keyword">.text</span>     <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span>kernel<span class="token operator">*</span>.o<span class="token punctuation">(</span><span class="token section keyword">.text</span><span class="token punctuation">)</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.text</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.rodata</span>   <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.rodata</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.data</span>     <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.data</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.bss</span>      <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.bss</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.shstrtab</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.shstrtab</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">  <span class="token operator">/</span>DISCARD<span class="token operator">/</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>I&#39;m using a wildcard pattern for the kernel object file, since the name is mangled. Let&#39;s compile the kernel again and see what the linker map file looks like:</p><div class="language-sh-session line-numbers-mode" data-highlighter="prismjs" data-ext="sh-session" data-title="sh-session"><pre><code><span class="line"><span class="token command"><span class="token shell-symbol important">$</span> <span class="token bash language-bash"><span class="token function">head</span> <span class="token parameter variable">-n</span> <span class="token number">10</span> build/kernel.map</span></span></span>
<span class="line"><span class="token output">        VMA              LMA     Size Align Out     In      Symbol</span>
<span class="line">          0                0   100000     1 . = 0x100000</span>
<span class="line">    100000           100000     b822    16 .text</span>
<span class="line">    100000           100000      29b    16         .../fusion/build/@mmain.nim.c.o:(.text)</span>
<span class="line">    100000           100000       c9     1                 KernelMain</span>
<span class="line">    1000d0           1000d0       87     1                 nimFrame</span>
<span class="line">    100160           100160       20     1                 nimErrorFlag</span>
<span class="line">    100180           100180       10     1                 NimMain</span>
<span class="line">    100190           100190       ad     1                 quit__system_u6343</span>
<span class="line">    100240           100240       19     1                 popFrame</span>
<span class="line"></span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Great! The kernel object file is now the first object file in the image, and our <code>KernelMain</code> proc is exactly at address <code>0x100000</code>. This should work, but there&#39;s a hidden issue here. The fact that the linker decided to put <code>KernelMain</code> at the beginning of the <code>.text</code> section is an implementation detail of the linker. If we add more code to the kernel, the linker might decide to put <code>KernelMain</code> at a different address. So how do we tell the linker to always put <code>KernelMain</code> at the beginning of the <code>.text</code> section? Linker scripts work at a section level, so we can&#39;t tell the linker to put a specific symbol at a specific address. One thing we can do is use a C compiler flag called <code>-ffunction-sections</code>, which tells the compiler to put each function in its own section. The generated section names are in the form <code>.text.&lt;function name&gt;</code>. This way we can tell the linker to put the <code>.text.KernelMain</code> section at the beginning of the <code>.text</code> section. Let&#39;s add this flag to the compiler arguments in <code>nim.cfg</code>:</p><div class="language-properties line-numbers-mode" data-highlighter="prismjs" data-ext="properties" data-title="properties"><pre><code><span class="line"><span class="token comment"># src/kernel/nim.cfg</span></span>
<span class="line">...</span>
<span class="line"></span>
<span class="line"><span class="token key attr-name">--passc</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;-ffunction-sections&quot;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Let&#39;s compile the kernel and take a look at the sections in the object file:</p><div class="language-sh-session line-numbers-mode" data-highlighter="prismjs" data-ext="sh-session" data-title="sh-session"><pre><code><span class="line"><span class="token command"><span class="token shell-symbol important">$</span> <span class="token bash language-bash">llvm-objdump --section-headers build/@mmain.nim.c.o</span></span></span>
<span class="line"></span>
<span class="line"><span class="token output">build/@mmain.nim.c.o:   file format elf64-x86-64</span>
<span class="line"></span>
<span class="line">Sections:</span>
<span class="line">Idx Name                          Size     VMA              Type</span>
<span class="line">  0                               00000000 0000000000000000 </span>
<span class="line">  1 .strtab                       00000224 0000000000000000 </span>
<span class="line">  2 .text                         00000000 0000000000000000 TEXT</span>
<span class="line highlighted">  3 .text.KernelMain              00000070 0000000000000000 TEXT  &lt;-- KernelMain is in its own section</span>
<span class="line">  4 .rela.text.KernelMain         00000090 0000000000000000 </span>
<span class="line">  5 .text.nimFrame                00000087 0000000000000000 TEXT</span>
<span class="line">  6 .rela.text.nimFrame           00000078 0000000000000000 </span>
<span class="line">  7 .text.NimMain                 00000010 0000000000000000 TEXT</span>
<span class="line">  8 .rela.text.NimMain            00000030 0000000000000000 </span>
<span class="line">  9 .text.quit__system_u6343      000000ad 0000000000000000 TEXT</span>
<span class="line">...</span>
<span class="line"></span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Looks good. We can now update the linker script to put the <code>.text.KernelMain</code> section at the beginning of the <code>.text</code> section. We&#39;ll follow it with the other function sections from the kernel main object file, and then all other function sections. The reason for this is that we want to keep the code from the kernel main object file together for better cache locality.</p><div class="language-ld line-numbers-mode" data-highlighter="prismjs" data-ext="ld" data-title="ld"><pre><code><span class="line"><span class="token comment">/* src/kernel/kernel.ld */</span></span>
<span class="line"></span>
<span class="line">SECTIONS</span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">  <span class="token location-counter important">.</span> <span class="token operator">=</span> <span class="token number">0x100000</span><span class="token punctuation">;</span></span>
<span class="line highlighted">  <span class="token section keyword">.text</span>     <span class="token operator">:</span> <span class="token punctuation">{</span></span>
<span class="line highlighted">    <span class="token operator">*</span>main<span class="token operator">*</span>.o<span class="token punctuation">(</span><span class="token section keyword">.text</span><span class="token section keyword">.KernelMain</span><span class="token punctuation">)</span></span>
<span class="line highlighted">    <span class="token operator">*</span>main<span class="token operator">*</span>.o<span class="token punctuation">(</span><span class="token section keyword">.text</span>.<span class="token operator">*</span><span class="token punctuation">)</span></span>
<span class="line highlighted">    <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.text</span>.<span class="token operator">*</span><span class="token punctuation">)</span></span>
<span class="line highlighted">  <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.rodata</span>   <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.rodata</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.data</span>     <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.data</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.bss</span>      <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.bss</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.shstrtab</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.shstrtab</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">  <span class="token operator">/</span>DISCARD<span class="token operator">/</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Let&#39;s compile the kernel again and see what the linker map file looks like:</p><div class="language-sh-session line-numbers-mode" data-highlighter="prismjs" data-ext="sh-session" data-title="sh-session"><pre><code><span class="line"><span class="token command"><span class="token shell-symbol important">$</span> <span class="token bash language-bash"><span class="token function">head</span> <span class="token parameter variable">-n</span> <span class="token number">10</span> build/kernel.map</span></span></span>
<span class="line"><span class="token output">             VMA              LMA     Size Align Out     In      Symbol</span>
<span class="line">               0                0   100000     1 . = 0x100000</span>
<span class="line">          100000           100000     b254    16 .text</span>
<span class="line highlighted">          100000           100000       70    16         .../fusion/build/@mmain.nim.c.o:(.text.KernelMain)</span>
<span class="line highlighted">          100000           100000       70     1                 KernelMain</span>
<span class="line">          100070           100070       87    16         .../fusion/build/@mmain.nim.c.o:(.text.nimFrame)</span>
<span class="line">          100070           100070       87     1                 nimFrame</span>
<span class="line">          100100           100100       10    16         .../fusion/build/@mmain.nim.c.o:(.text.NimMain)</span>
<span class="line">          100100           100100       10     1                 NimMain</span>
<span class="line">          100110           100110       ad    16         .../fusion/build/@mmain.nim.c.o:(.text.quit__system_u6343)</span>
<span class="line"></span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Looks good. Now we&#39;re guaranteed that <code>KernelMain</code> will always be at the beginning of the <code>.text</code> section. The order of other sections is not important to us (unless we want to optimize for cache locality, but let&#39;s not pre-optimize for now).</p><h2 id="building-a-raw-binary" tabindex="-1"><a class="header-anchor" href="#building-a-raw-binary"><span>Building a raw binary</span></a></h2><p>We have an ELF executable kernel image, but we want a raw binary image. Let&#39;s add the <code>--output-format=binary</code> switch to the linker arguments in <code>nim.cfg</code>:</p><div class="language-properties line-numbers-mode" data-highlighter="prismjs" data-ext="properties" data-title="properties"><pre><code><span class="line"><span class="token comment"># src/kernel/nim.cfg</span></span>
<span class="line"></span>
<span class="line">...</span>
<span class="line"><span class="token key attr-name">--passl</span><span class="token punctuation">:</span><span class="token value attr-value">&quot;--oformat=binary&quot;</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Let&#39;s compile the kernel again:</p><div class="language-sh-session line-numbers-mode" data-highlighter="prismjs" data-ext="sh-session" data-title="sh-session"><pre><code><span class="line"><span class="token command"><span class="token shell-symbol important">$</span> <span class="token bash language-bash">just kernel</span></span></span>
<span class="line"></span>
<span class="line"><span class="token command"><span class="token shell-symbol important">$</span> <span class="token bash language-bash"><span class="token function">file</span> build/kernel.bin</span></span></span>
<span class="line"><span class="token output">build/kernel.bin: data</span>
<span class="line"></span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Great! We have a raw binary kernel image. But there&#39;s one more thing. Let&#39;s take a look at the size of the kernel image:</p><div class="language-sh-session line-numbers-mode" data-highlighter="prismjs" data-ext="sh-session" data-title="sh-session"><pre><code><span class="line"><span class="token command"><span class="token shell-symbol important">$</span> <span class="token bash language-bash"><span class="token function">wc</span> <span class="token parameter variable">-c</span> build/kernel.bin</span></span></span>
<span class="line"><span class="token output">  51104 build/kernel.bin</span>
<span class="line"></span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div></div></div><p>The kernel image is about 51 KiB. But remember that we have a 1 MiB heap in the <code>malloc.nim</code> module. This is not persisted in the image, since it&#39;s in the <code>.bss</code> section, which is uninitialized data. This poses a problem for the bootloader, since we don&#39;t have section metadata in the image. Part of the reason for building a raw binary image is to make it dead simple for the loader to load it into memory without having to worry about initializing sections. One way to solve this problem is to move the <code>.bss</code> section into the output <code>.data</code> section. This will cause the linker to allocate space for the <code>.bss</code> section in the output file. Obviously this will increase the size of the image, but it&#39;s a price we&#39;re willing to pay to keep the bootloader simple.</p><p>Let&#39;s modify the linker script to move the <code>.bss</code> section into the <code>.data</code> section:</p><div class="language-ld line-numbers-mode" data-highlighter="prismjs" data-ext="ld" data-title="ld"><pre><code><span class="line"><span class="token comment">/* src/kernel/kernel.ld */</span></span>
<span class="line"></span>
<span class="line">SECTIONS</span>
<span class="line"><span class="token punctuation">{</span></span>
<span class="line">  <span class="token location-counter important">.</span> <span class="token operator">=</span> <span class="token number">0x100000</span><span class="token punctuation">;</span></span>
<span class="line">  <span class="token section keyword">.text</span>     <span class="token operator">:</span> <span class="token punctuation">{</span></span>
<span class="line">    <span class="token operator">*</span>main<span class="token operator">*</span>.o<span class="token punctuation">(</span><span class="token section keyword">.text</span><span class="token section keyword">.KernelMain</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">*</span>main<span class="token operator">*</span>.o<span class="token punctuation">(</span><span class="token section keyword">.text</span>.<span class="token operator">*</span><span class="token punctuation">)</span></span>
<span class="line">    <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.text</span>.<span class="token operator">*</span><span class="token punctuation">)</span></span>
<span class="line">  <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.rodata</span>   <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.rodata</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line highlighted">  <span class="token section keyword">.data</span>     <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.data</span><span class="token punctuation">)</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.bss</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line">  <span class="token section keyword">.shstrtab</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token section keyword">.shstrtab</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line"></span>
<span class="line">  <span class="token operator">/</span>DISCARD<span class="token operator">/</span> <span class="token operator">:</span> <span class="token punctuation">{</span> <span class="token operator">*</span><span class="token punctuation">(</span><span class="token operator">*</span><span class="token punctuation">)</span> <span class="token punctuation">}</span></span>
<span class="line"><span class="token punctuation">}</span></span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>Let&#39;s compile the kernel again and see what the size of the image is:</p><div class="language-sh-session line-numbers-mode" data-highlighter="prismjs" data-ext="sh-session" data-title="sh-session"><pre><code><span class="line"><span class="token command"><span class="token shell-symbol important">$</span> <span class="token bash language-bash">just kernel</span></span></span>
<span class="line"></span>
<span class="line"><span class="token command"><span class="token shell-symbol important">$</span> <span class="token bash language-bash"><span class="token function">wc</span> <span class="token parameter variable">-c</span> build/kernel.bin</span></span></span>
<span class="line"><span class="token output">  1100880 build/kernel.bin</span>
<span class="line"></span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>The image is now about 1.1 MiB, which means that the <code>.bss</code> section is now included in the image. Now the bootloader will be able to load the image into memory without having to worry about initializing sections.</p><p>Let&#39;s update our <code>justfile</code> to copy the kernel image to the disk image in a place where the bootloader can find it:</p><div class="language-justfile line-numbers-mode" data-highlighter="prismjs" data-ext="justfile" data-title="justfile"><pre><code><span class="line"># justfile</span>
<span class="line"></span>
<span class="line">nimflags := &quot;--os:any&quot;</span>
<span class="line"></span>
<span class="line">bootloader:</span>
<span class="line">  nim c {{nimflags}} src/boot/bootx64.nim --out:build/bootx64.efi</span>
<span class="line"></span>
<span class="line">kernel:</span>
<span class="line">  nim c {{nimflags}} src/kernel/main.nim --out:build/kernel.bin</span>
<span class="line"></span>
<span class="line highlighted">run: bootloader kernel</span>
<span class="line highlighted">  mkdir -p diskimg/efi/boot</span>
<span class="line highlighted">  mkdir -p diskimg/efi/fusion</span>
<span class="line">  cp build/bootx64.efi diskimg/efi/boot/bootx64.efi</span>
<span class="line highlighted">  cp build/kernel.bin diskimg/efi/fusion/kernel.bin</span>
<span class="line">  qemu-system-x86_64 \\</span>
<span class="line">    -drive if=pflash,format=raw,file=ovmf/OVMF_CODE.fd,readonly=on \\</span>
<span class="line">    -drive if=pflash,format=raw,file=ovmf/OVMF_VARS.fd \\</span>
<span class="line">    -drive format=raw,file=fat:rw:diskimg \\</span>
<span class="line">    -machine q35 \\</span>
<span class="line">    -net none</span>
<span class="line"></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>In the next section we will continue working on our bootloader. Specifically we will try to use UEFI services to locate the kernel image file, load it into memory address <code>0x100000</code>, and jump to it.</p>`,87)]))}const c=n(l,[["render",t],["__file","07-kernel-image.html.vue"]]),r=JSON.parse(`{"path":"/osdev/07-kernel-image.html","title":"Kernel Image","lang":"en-US","frontmatter":{},"headers":[{"level":2,"title":"Project structure","slug":"project-structure","link":"#project-structure","children":[]},{"level":2,"title":"Debug output","slug":"debug-output","link":"#debug-output","children":[]},{"level":2,"title":"Entry point","slug":"entry-point","link":"#entry-point","children":[]},{"level":2,"title":"C compiler options","slug":"c-compiler-options","link":"#c-compiler-options","children":[]},{"level":2,"title":"Linking the kernel","slug":"linking-the-kernel","link":"#linking-the-kernel","children":[]},{"level":2,"title":"Building a raw binary","slug":"building-a-raw-binary","link":"#building-a-raw-binary","children":[]}],"git":{"updatedTime":1723280488000},"filePathRelative":"osdev/07-kernel-image.md","excerpt":"\\n<p>To start simple, we'll build a flat binary kernel image instead of using an executable format like PE or ELF. This makes the job of the bootloader easier, since it doesn't have to parse a complex executable format or fix up relocations. All it has to do is load the kernel image into memory and jump to the entry point.</p>"}`);export{c as comp,r as data};
