Bootstrapping Freestanding, part 2
Kovacsics Robert
Parts: 1, 2
Time to start using the C library! Though this article is about all the things that can go wrong when you start to do that. First, let’s replace the infinite loop at the end of init.S with a jump to the C library’s entry point (in this case newlib).
init.S@@ -21,19 +28,38 @@ _entry: stmia r9!, {r0-r7} b 1b 2: -1: b 1b + b _mainCRTStartup
The first issue is:
.../arm-none-eabi-ld.bfd: .../init.S.obj: in function `_entry':
.../init.S:55:(.text+0x40): undefined reference to `_mainCRTStartup'This comes from the -nostdlib we put in the CMake file (there is an
expandablesection in the previous post about using CMake, but just using
arm-none-eabi-gcc to link an elf will also work).
gcc-arm-none-eabi.cmakediff --git a/src/bootloader/gcc-arm-none-eabi.cmake b/src/bootloader/gcc-arm-none-eabi.cmake index 6e91ff6..5a2f3c2 100644 --- a/src/bootloader/gcc-arm-none-eabi.cmake +++ b/src/bootloader/gcc-arm-none-eabi.cmake @@ -7,9 +7,9 @@ set(CMAKE_EXECUTABLE_SUFFIX_C .elf) set(CMAKE_EXECUTABLE_SUFFIX_CXX .elf) set(CMAKE_EXECUTABLE_SUFFIX_ASM .elf) -set(CMAKE_C_FLAGS "-mcpu=cortex-r5 -nostdlib") -set(CMAKE_CXX_FLAGS "-mcpu=cortex-r5 -nostdlib") -set(CMAKE_ASM_FLAGS "-mcpu=cortex-r5 -nostdlib") +set(CMAKE_C_FLAGS "-mcpu=cortex-r5") +set(CMAKE_CXX_FLAGS "-mcpu=cortex-r5") +set(CMAKE_ASM_FLAGS "-mcpu=cortex-r5") set(CMAKE_C_LINK_FLAGS -T${CMAKE_CURRENT_LIST_DIR}/link_script.ld) set(CMAKE_CXX_LINK_FLAGS -T${CMAKE_CURRENT_LIST_DIR}/link_script.ld)
Once we remove -nostdlib, we have a whole host of new errors
.../arm-none-eabi-ld.bfd: .../arm-none-eabi/lib/crt0.o: in function `_mainCRTStartup':
.../libgloss/arm/crt0.S:546:(.text+0x118): undefined reference to `main'
.../arm-none-eabi-ld.bfd: .../libgloss/arm/crt0.S:548:(.text+0x130): undefined reference to `__bss_start__'
.../arm-none-eabi-ld.bfd: .../libgloss/arm/crt0.S:548:(.text+0x134): undefined reference to `__bss_end__'
.../arm-none-eabi-ld.bfd: .../arm-none-eabi/lib/libg.a(libc_a-exit.o): in function `exit':
.../newlib/libc/stdlib/exit.c:65:(.text+0x40): undefined reference to `_exit'
.../arm-none-eabi-ld.bfd: .../arm-none-eabi/lib/libg.a(libc_a-closer.o): in function `_close_r':
.../newlib/libc/reent/closer.c:47:(.text+0x28): undefined reference to `_close'
.../arm-none-eabi-ld.bfd: .../arm-none-eabi/lib/libg.a(libc_a-lseekr.o): in function `_lseek_r':
.../newlib/libc/reent/lseekr.c:49:(.text+0x30): undefined reference to `_lseek'
.../arm-none-eabi-ld.bfd: .../arm-none-eabi/lib/libg.a(libc_a-readr.o): in function `_read_r':
.../newlib/libc/reent/readr.c:49:(.text+0x30): undefined reference to `_read'
.../arm-none-eabi-ld.bfd: .../arm-none-eabi/lib/libg.a(libc_a-writer.o): in function `_write_r':
.../newlib/libc/reent/writer.c:49:(.text+0x30): undefined reference to `_write'
.../arm-none-eabi-ld.bfd: .../arm-none-eabi/lib/libg.a(libc_a-sbrkr.o): in function `_sbrk_r':
.../newlib/libc/reent/sbrkr.c:51:(.text+0x28): undefined reference to `_sbrk'Debugging linker errors can be more difficult than debugging compiler errors,
something that helps is adding the -Map flag to the linker. Using the filename
% uses the executable base name (for GNU ld at least).
gcc-arm-none-eabi.cmakediff --git a/src/bootloader/gcc-arm-none-eabi.cmake b/src/bootloader/gcc-arm-none-eabi.cmake index 5a2f3c2..143b1ac 100644 --- a/src/bootloader/gcc-arm-none-eabi.cmake +++ b/src/bootloader/gcc-arm-none-eabi.cmake @@ -11,9 +11,9 @@ set(CMAKE_C_FLAGS "-mcpu=cortex-r5") set(CMAKE_CXX_FLAGS "-mcpu=cortex-r5") set(CMAKE_ASM_FLAGS "-mcpu=cortex-r5") -set(CMAKE_C_LINK_FLAGS -T${CMAKE_CURRENT_LIST_DIR}/link_script.ld) -set(CMAKE_CXX_LINK_FLAGS -T${CMAKE_CURRENT_LIST_DIR}/link_script.ld) -set(CMAKE_ASM_LINK_FLAGS -T${CMAKE_CURRENT_LIST_DIR}/link_script.ld) +set(CMAKE_C_LINK_FLAGS "-Wl,-Map=% -T${CMAKE_CURRENT_LIST_DIR}/link_script.ld") +set(CMAKE_CXX_LINK_FLAGS "-Wl,-Map=% -T${CMAKE_CURRENT_LIST_DIR}/link_script.ld") +set(CMAKE_ASM_LINK_FLAGS "-Wl,-Map=% -T${CMAKE_CURRENT_LIST_DIR}/link_script.ld") # To avoid test executable runs out of const section's size. set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
This gives the following map file
Archive member included to satisfy reference by file (symbol)
.../arm-none-eabi/lib/libg.a(libc_a-exit.o)
.../arm-none-eabi/lib/crt0.o (exit)
.../arm-none-eabi/lib/libg.a(libc_a-findfp.o)
.../arm-none-eabi/lib/libg.a(libc_a-exit.o) (__stdio_exit_handler)
.../arm-none-eabi/lib/libg.a(libc_a-fwalk.o)
.../arm-none-eabi/lib/libg.a(libc_a-findfp.o) (_fwalk_sglue)
.../arm-none-eabi/lib/libg.a(libc_a-stdio.o)
.../arm-none-eabi/lib/libg.a(libc_a-findfp.o) (__sread)
.../arm-none-eabi/lib/libg.a(libc_a-memset.o)
.../arm-none-eabi/lib/crt0.o (memset)
.../arm-none-eabi/lib/libg.a(libc_a-closer.o)
.../arm-none-eabi/lib/libg.a(libc_a-stdio.o) (_close_r)
.../arm-none-eabi/lib/libg.a(libc_a-reent.o)
.../arm-none-eabi/lib/libg.a(libc_a-closer.o) (errno)
.../arm-none-eabi/lib/libg.a(libc_a-impure.o)
.../arm-none-eabi/lib/libg.a(libc_a-reent.o) (_impure_ptr)
.../arm-none-eabi/lib/libg.a(libc_a-lseekr.o)
.../arm-none-eabi/lib/libg.a(libc_a-stdio.o) (_lseek_r)
.../arm-none-eabi/lib/libg.a(libc_a-readr.o)
.../arm-none-eabi/lib/libg.a(libc_a-stdio.o) (_read_r)
.../arm-none-eabi/lib/libg.a(libc_a-writer.o)
.../arm-none-eabi/lib/libg.a(libc_a-stdio.o) (_write_r)
.../arm-none-eabi/lib/libg.a(libc_a-init.o)
.../arm-none-eabi/lib/crt0.o (__libc_init_array)
.../arm-none-eabi/lib/libg.a(libc_a-lock.o)
.../arm-none-eabi/lib/libg.a(libc_a-findfp.o) (__retarget_lock_init_recursive)
.../arm-none-eabi/lib/libg.a(libc_a-freer.o)
.../arm-none-eabi/lib/libg.a(libc_a-reent.o) (_free_r)
.../arm-none-eabi/lib/libg.a(libc_a-mallocr.o)
.../arm-none-eabi/lib/libg.a(libc_a-findfp.o) (_malloc_r)
.../arm-none-eabi/lib/libg.a(libc_a-mlock.o)
.../arm-none-eabi/lib/libg.a(libc_a-freer.o) (__malloc_lock)
.../arm-none-eabi/lib/libg.a(libc_a-fflush.o)
.../arm-none-eabi/lib/libg.a(libc_a-findfp.o) (_fflush_r)
.../arm-none-eabi/lib/libg.a(libc_a-sbrkr.o)
.../arm-none-eabi/lib/libg.a(libc_a-mallocr.o) (_sbrk_r)This indicates that the static library libg.a, specifically the object file
libc_a-sbrkr.o, is used by libc_a-mallocr.o which uses the symbol _sbrk_r.
We can transform the above into a GraphViz file using some regex
import re
import sys
REQUIRES_RE = re.compile(r"^(?P<path>\S+)$")
PROVIDES_RE = re.compile(r"^\s+(?P<path>\S+) \((?P<symbol>\w+)\)$")
mappings = {}
requires = ""
aliases = {}
def get(key):
return aliases.setdefault(key, f"n{len(aliases)}")
while line := sys.stdin.readline():
if line.startswith("Archive member included to satisfy reference by file (symbol)"):
sys.stdin.readline() # Swallow newline
break
while line := sys.stdin.readline().strip("\r\n"):
if m := REQUIRES_RE.match(line):
requires = m["path"]
mappings.setdefault(requires, [])
elif m := PROVIDES_RE.match(line):
if requires:
mappings[requires].append((m["path"], m["symbol"]))
else:
print("Bad line", line)
break
print("digraph {")
print(" node [shape=box]")
for req, provs in mappings.items():
for prov, sym in provs:
print(f' {get(req)} -> {get(prov)} [label="{sym}"]')
for long, short in aliases.items():
print(f' {short} [label="{long}"]')
print("}")Here the root is the exit which we can override (since it is using archive
linking, and it is the only symbol in its file, if we define our own exit then
that one won’t get pulled in). Put this at the end of init.S, or you could
implement it in C even.
init.S.globl exit exit: b exit
The next errors are fortunately pretty simple.
.../bin/arm-none-eabi-ld.bfd: .../arm-none-eabi/lib/crt0.o: in function `_mainCRTStartup':
.../libgloss/arm/crt0.S:546:(.text+0x118): undefined reference to `main'
.../bin/arm-none-eabi-ld.bfd: .../libgloss/arm/crt0.S:548:(.text+0x130): undefined reference to `__bss_start__'
.../bin/arm-none-eabi-ld.bfd: .../libgloss/arm/crt0.S:548:(.text+0x134): undefined reference to `__bss_end__'The main is up to you to implement now. The bss is just missing from the link
script so let’s put that in.
link_script.lddiff --git a/src/bootloader/link_script.ld b/src/bootloader/link_script.ld index 0da6c49..f6bd085 100644 --- a/src/bootloader/link_script.ld +++ b/src/bootloader/link_script.ld @@ -23,4 +23,11 @@ SECTIONS __load_sram_begin__ = LOADADDR(.data); __dest_sram_begin__ = ADDR(.data); __dest_sram_end__ = ADDR(.data) + SIZEOF(.data); + + .bss : + { + __bss_start__ = .; + *(.bss) + __bss_end__ = .; + } >SRAM }
Now we have something which can do basic things – the next part will be about errors I’ve encountered, and how to debug those.