🥦

U-BootからHelloWorld(その2)

2023/08/04に公開

前回の記事の続き。
ビルドしたhello_worldのロードアドレスと実行開始アドレスを調べる方法です。

実行ファイルから情報を得る

$ cd examples/standalone/
$ file hello_world*
hello_world:      ELF 64-bit LSB executable, UCB RISC-V, version 1 (SYSV), statically linked, with debug_info, not stripped
hello_world.bin:  data
hello_world.c:    C source, ASCII text
hello_world.o:    ELF 64-bit LSB relocatable, UCB RISC-V, version 1 (SYSV), with debug_info, not stripped
hello_world.srec: Motorola S-Record; binary data in text format
hello_world.su:   ASCII text

ELFのファイルのセクションヘッダの情報を見ます。

$ riscv64-unknown-elf-objdump -h hello_world

hello_world:     file format elf64-littleriscv

Sections:
Idx Name          Size      VMA               LMA               File off  Algn
  0 .text         00000218  0000000041000000  0000000041000000  00001000  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .rodata       000000b1  0000000041000218  0000000041000218  00001218  2**3
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .got          00000028  00000000410012d0  00000000410012d0  000012d0  2**3
                  CONTENTS, ALLOC, LOAD, DATA
  3 .comment      0000000e  0000000000000000  0000000000000000  000012f8  2**0
                  CONTENTS, READONLY
  4 .riscv.attributes 00000035  0000000000000000  0000000000000000  00001306  2**0
                  CONTENTS, READONLY
  5 .debug_aranges 00000070  0000000000000000  0000000000000000  0000133b  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
  6 .debug_info   000008af  0000000000000000  0000000000000000  000013ab  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
  7 .debug_abbrev 00000249  0000000000000000  0000000000000000  00001c5a  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
  8 .debug_line   000004a3  0000000000000000  0000000000000000  00001ea3  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
  9 .debug_frame  00000090  0000000000000000  0000000000000000  00002348  2**3
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 10 .debug_str    00000563  0000000000000000  0000000000000000  000023d8  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 11 .debug_loc    000001a0  0000000000000000  0000000000000000  0000293b  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS
 12 .debug_ranges 00000050  0000000000000000  0000000000000000  00002adb  2**0
                  CONTENTS, READONLY, DEBUGGING, OCTETS

LOADの属性のあるセクションが3つあって、それらの開始アドレスの一番若いものが0x41000000 です。
これがロードすべきアドレスです。

実行開始アドレスはELFのヘッダの中のEntry point addressの項目を見ます。

$ riscv64-unknown-elf-readelf -h hello_world |grep Entry
  Entry point address:               0x41000000

ロードアドレスも実行開始アドレスも0x41000000でした。

$ riscv64-unknown-elf-nm hello_world| sort | head
0000000041000000 T hello_world
00000000410000b8 T dummy
00000000410000b8 T get_version
00000000410000c2 T getc
00000000410000cc T tstc
00000000410000d6 T putc
00000000410000e0 T puts
00000000410000ea T printf
00000000410000f4 T install_hdlr
00000000410000fe T free_hdlr

0x41000000にはhello_worldの関数がありました。

ビルド方法の調査

ロードアドレスと実行開始アドレスはどこで指定されているのでしょうか。ビルドの詳細を調べてみます。
指定している箇所がわかれば変更することもできるはずです。

$ make CROSS_COMPILE=riscv64-unknown-elf- examples/standalone/
  UPD     include/generated/timestamp_autogenerated.h
  CC      examples/standalone/hello_world.o
  CC      examples/standalone/stubs.o
  LD      examples/standalone/libstubs.o
  LD      examples/standalone/hello_world
  OBJCOPY examples/standalone/hello_world.srec
  OBJCOPY examples/standalone/hello_world.bin

ビルド時のコマンドラインを表示するには V=1をつけます。

$ make V=1 CROSS_COMPILE=riscv64-unknown-elf- examples/standalone/

...

make KBUILD_MODULES= \
-f ./scripts/Makefile.build obj=examples/standalone
  riscv64-unknown-elf-gcc -Wp,-MD,examples/standalone/.hello_world.o.d  -nostdinc -isystem /usr/lib/gcc/riscv64-unknown-elf/9.3.0/include -Iinclude   -I./arch/riscv/include -include ./include/linux/kconfig.h -D__KERNEL__ -D__UBOOT__ -Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -std=gnu11 -fshort-wchar -fno-strict-aliasing -fno-PIE -Os -fno-stack-protector -fno-delete-null-pointer-checks -Wno-pointer-sign -Wno-stringop-truncation -Wno-array-bounds -Wno-stringop-overflow -Wno-maybe-uninitialized -fmacro-prefix-map=./= -g -fstack-usage -Wno-format-nonliteral -Wno-address-of-packed-member -Wno-unused-but-set-variable -Werror=date-time -Wno-packed-not-aligned -fno-toplevel-reorder -ffixed-gp -fpic -fno-common -gdwarf-2 -ffunction-sections -fdata-sections -pipe -march=rv64imafdc -mabi=lp64d -mcmodel=medany    -DKBUILD_BASENAME='"hello_world"'  -DKBUILD_MODNAME='"hello_world"' -c -o examples/standalone/hello_world.o examples/standalone/hello_world.c
  riscv64-unknown-elf-gcc -Wp,-MD,examples/standalone/.stubs.o.d  -nostdinc -isystem /usr/lib/gcc/riscv64-unknown-elf/9.3.0/include -Iinclude   -I./arch/riscv/include -include ./include/linux/kconfig.h -D__KERNEL__ -D__UBOOT__ -Wall -Wstrict-prototypes -Wno-format-security -fno-builtin -ffreestanding -std=gnu11 -fshort-wchar -fno-strict-aliasing -fno-PIE -Os -fno-stack-protector -fno-delete-null-pointer-checks -Wno-pointer-sign -Wno-stringop-truncation -Wno-array-bounds -Wno-stringop-overflow -Wno-maybe-uninitialized -fmacro-prefix-map=./= -g -fstack-usage -Wno-format-nonliteral -Wno-address-of-packed-member -Wno-unused-but-set-variable -Werror=date-time -Wno-packed-not-aligned -fno-toplevel-reorder -ffixed-gp -fpic -fno-common -gdwarf-2 -ffunction-sections -fdata-sections -pipe -march=rv64imafdc -mabi=lp64d -mcmodel=medany    -DKBUILD_BASENAME='"stubs"'  -DKBUILD_MODNAME='"stubs"' -c -o examples/standalone/stubs.o examples/standalone/stubs.c
  riscv64-unknown-elf-ld.bfd  -m elf64lriscv    -r -o examples/standalone/libstubs.o examples/standalone/stubs.o
  riscv64-unknown-elf-ld.bfd  -m elf64lriscv  -Ttext 0x41000000 -g -o examples/standalone/hello_world -e hello_world examples/standalone/hello_world.o examples/standalone/libstubs.o -L /usr/lib/gcc/riscv64-unknown-elf/9.3.0/rv64imafdc/lp64d -lgcc
  riscv64-unknown-elf-objcopy -O srec  examples/standalone/hello_world examples/standalone/hello_world.srec
  riscv64-unknown-elf-objcopy -O binary  examples/standalone/hello_world examples/standalone/hello_world.bin

リンクしているところは以下のようにしていました。

riscv64-unknown-elf-ld.bfd  -m elf64lriscv  -Ttext 0x41000000 \
-g -o examples/standalone/hello_world -e hello_world \
examples/standalone/hello_world.o examples/standalone/libstubs.o \
-L /usr/lib/gcc/riscv64-unknown-elf/9.3.0/rv64imafdc/lp64d -lgcc

-Ttext 0x41000000 でテキストセクションの開始アドレスを指定しています。
-e hello_world でエントリポイントのシンボルを指定しています。
hello_worldの関数が一番最初になったのは、hello_world.oを一番最初に指定しているためです。

普通はこれらの指定にはリンカスクリプトを使用することが多いのですが、今回のシンプルな例ではこれだけの指定で済むのですね。

余談

今回使用したhello_worldを逆アセンブルして調べてみると、データの配置は全てPC相対か、GPレジスタ相対になっていて絶対アドレスが含まれていませんでした。
なので、想定している0x41000000 以外のアドレスにロードしても無事に動きます。

StarFive # fatload mmc 1:3 0x40200000 hello_world.bin   
4856 bytes read in 7 ms (676.8 KiB/s)
StarFive # go 0x40200000
## Starting application at 0x40200000 ...
Example expects ABI version 9
Actual U-Boot ABI version 9
Hello World
argc = 1
argv[0] = "0x40200000"
argv[1] = "<NULL>"
Hit any key to exit ... 

## Application terminated, rc = 0x0
StarFive # 

もう一回別のアドレスでやってみます。

StarFive # fatload mmc 1:3 0x50000000 hello_world.bin    
4856 bytes read in 7 ms (676.8 KiB/s)
StarFive # go 0x50000000
## Starting application at 0x50000000 ...
Example expects ABI version 9
Actual U-Boot ABI version 9
Hello World
argc = 1
argv[0] = "0x50000000"
argv[1] = "<NULL>"
Hit any key to exit ... 

## Application terminated, rc = 0x0
StarFive # 

関連

https://zenn.dev/tetsu_koba/articles/554cad6a46149e
https://zenn.dev/tetsu_koba/articles/0f65daab36d40a
https://zenn.dev/tetsu_koba/articles/b7b31b372f0f40

Discussion