Bare Metal

Table of Contents

1. Bare Metal

1.1. overview

baremetal(以及工具链)与 linux 的主要区别:

  1. libc

    linux 使用 glibc, baremetal 使用 newlib, 并且会使用 newlib 提供的 libgloss, libnosys 等支持 io, malloc 等原本需要 linux 支持的功能

  2. crt

    linux 下使用 gcc 提供的 crt, baremetal 使用了 libgloss 提供的 crt, 但 libgloss 只提供了 crt{0,i,n,begin,end}.o, 没有提供 crt{beginS,endS}.o 等

  3. multilib

    baremetal 环境下 multilib 的配置会有些区别, 例如会针对适用于嵌入式环境的扩展 (e,c,…) 定义 multilib

  4. 动态链接

    baremetal 不支持动态链接(以及 PIE)

  5. 其它

    baremetal 有时会使用自定义的 linker script, 并禁用 crt 及 libc

1.2. crt

1.2.1. crt0.o

以 riscv 的 crt0.o 为例, 它提供 _start 做为程序入口, _start 的主要功能有:

  1. 初始化 GP
  2. 清空 BSS
  3. 通过 atexit 注册 _libc_fini_array, 以便在程序结束时调用 finit_array
  4. 通过 _libc_init_array 以调用 init_array
  5. 调用 main
  6. exit

它的代码是这样的:

    .text
    .global _start _start :
    # 初始化 GP
    .option push
    .option norelax
1:  auipc gp,% pcrel_hi(__global_pointer$)
    addi gp, gp, % pcrel_lo(1b)
    .option pop

    # 清空 bss
    la a0,_edata
    la a2, _end
    sub a2, a2, a0
    li a1, 0
    call memset

    # atexit 注册 __libc_fini_array
    .weak atexit
    la a0, atexit
    beqz a0,.Lweak_atexit
    .weak __libc_fini_array
    la a0,__libc_fini_array #Register global termination functions call atexit
    call    atexit

    # 调用 __libc_init_array
    call __libc_init_array

    # 调用 main
    lw a0, 0(sp) #a0 = argc
    addi a1, sp, __SIZEOF_POINTER__ #a1 = argv
    li a2, 0 #a2 = envp = NULL
    call main

    # exit
    tail exit
    .size _start,.- _start

1.2.2. crti.o, crtn.o

for init/finit

1.2.3. crtbegin.o, crtendi.o

for c++

1.2.4. crtbeginS.o, crtendS.o

for c++ shared library

1.3. 实现

baremetal 与 linux 工具链的区别主要体现在 spec 中, spec 的定义和处理是在 `gcc.cc`, 它会使用 config.gcc 中定义的头文件:

riscv*-*-elf* | riscv*-*-rtems*)
    tm_file="elfos.h newlib-stdint.h ${tm_file} riscv/elf.h"
    tmake_file="${tmake_file} riscv/t-riscv"

riscv*-*-linux*)
    tm_file="elfos.h gnu-user.h linux.h glibc-stdint.h ${tm_file} riscv/linux.h"
    tmake_file="${tmake_file} riscv/t-riscv riscv/t-linux"

其中 linux.h 和 elf.h 会定义不同的 spec, 让 gcc/ld 使用不同的 STARTFILE_SPEC 和 LIB_SPEC 等

1.4. 其它

1.4.1. 禁用 crt/libc

baremetal 有时会使用下面的 flag 禁用 crt/libc/libgcc:

  1. nodefaultlibs

    不使用 libc, libgcc

  2. nostartfiles

    不使用 crt0.o 等 startfiles

  3. nostdlib

    nodefaultlibs + nostartfiles

1.4.2. qemu arm bare metal example

https://balau82.wordpress.com/2010/02/28/hello-world-for-bare-metal-arm-using-qemu/

为了验证 kws 在 mips bare metal 上的情况, 使用 qemu arm bare metal example 来测试 bare metal 的基本概念

支持 bare metal 的方法:

  1. 针对板子写 start.s, 初始化 sp 等, 然后跳转到 main
  2. linker script 需要把 start.o 放在板子要求的地址上, 同时需要提供一些符号, 例如 newlib 中的 _sbrk 需要 linker script 定义 `end` 以后才能支持 malloc
  3. 通过 nosys.specs 使用 libnosys 给 _fork, _exit 等函数提供 dummy 实现, 避免编译不过

Author: [email protected]
Date: 2020-09-29 Tue 00:00
Last updated: 2024-08-17 Sat 15:42

知识共享许可协议