← Bootloader DUJINLABS.COM

Barebox · source analysis

Barebox 빌드 산출물과 PBL image

Kconfig와 linker 단계가 barebox ELF, PBL, DTB와 보드별 images를 만드는 구조를 정리합니다.

Series
01 / 19
Baseline
v2026.06.1
Commit
314c679eea2d
Source
Makefile:1074

Barebox 빌드 산출물과 PBL image 단계에서 실제로 바뀌는 상태는 무엇인가?

barebox 실행 파일과 images/ 아래 ROM 투입 파일을 구분한다. PBL 압축, 헤더, load address가 최종 파일에서 어디에 놓이는지 본다.

ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다. 이 문장을 기준으로 코드를 위에서 아래로 읽으면, 함수 이름을 외우는 대신 어느 시점에 어떤 상태를 신뢰할 수 있는지 판단할 수 있다.

객체와 주소가 놓이는 구조

그림 1. Barebox 빌드 산출물과 PBL image에서 입력, 내부 상태, 출력의 경계
입력과 전제내부 상태외부로 공개되는 결과
01main image
02PBL objects
03embedded DTB
04SoC boot image
INVARIANT

ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다.

barebox: $를 중심에 놓고 왼쪽의 입력이 어떤 검사를 거쳐 오른쪽 결과로 공개되는지 표시했다. 실제 디버깅에서는 각 블록의 주소와 크기를 로그에 대입한다.

실행 흐름

그림 2. 정상 경로의 주요 호출과 상태 전달
Kconfig
built-in objects
barebox ELF
PBL and DTB
images/

화살표는 단순 호출 순서만 뜻하지 않는다. 각 단계가 성공을 반환할 때 다음 단계가 읽을 수 있는 상태가 무엇인지 함께 확인한다. 오류 반환이 발생하면 바로 다음 화살표로 진행하지 않고 해당 단계의 정리 경로를 따라간다.

단계별 입력과 출력

호출 순서를 함수 이름으로만 외우지 않고, 각 단계가 무엇을 받아 무엇을 공개하는지 적은 표다. 실제 소스에서 생산 필드가 다르면 표를 고치는 방식으로 사용한다.

#단계진입 시 신뢰할 상태성공 뒤 남아야 할 상태다음 소비자
01Kconfig빌드 시점main imagebuilt-in objects
02built-in objectsKconfig 완료 상태PBL objectsbarebox ELF
03barebox ELFbuilt-in objects 완료 상태embedded DTBPBL and DTB
04PBL and DTBbarebox ELF 완료 상태SoC boot imageimages/
05images/PBL and DTB 완료 상태SoC boot image최종 barebox, barebox.bin, PBL, images/* 또는 다음 stage

공통 불변 조건: ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다. 한 단계의 출력이 다음 단계의 입력 조건을 만족하지 않으면 오류가 실제로 드러난 위치보다 앞의 생산 단계부터 조사한다.

원본 코드

아래 코드는 Barebox v2026.06.1의 Makefile에서 1074-1162줄을 그대로 가져온 것이다. 설명을 위해 실제 코드를 가짜 의사 코드로 바꾸지 않았다.

commit314c679eea2d0ca847c64d1b646a1cf91225c0ecsymbolbarebox: $(BAREBOX_LDS)
1074# This target has pytest select the YAML env matching CONFIG_NAME if available.
1075# Use pytest --lg-env $labgrid_env_yaml directly if you need more than that.
1076
1077labgrid-env := $(srctree)/test/$(SRCARCH)/$(CONFIG_NAME).yaml
1078
1079check:
1080ifeq ($(CONFIG_NAME),"")
1081	@echo "error: can't autoload labgrid env with CONFIG_NAME unset!" >&2
1082	@exit 1
1083endif
1084	@if [ ! -r "$(labgrid-env)" ]; then \
1085		echo "error: No Labgrid environment at $(labgrid-env)!" >&2; \
1086		echo "Choose a different config (or change CONFIG_NAME)" >&2; \
1087		echo "that can be automatically tested" >&2; \
1088		exit 1; \
1089	fi
1090	@echo
1091	@# This is intentionally not @suppressed, to make it easier to reproduce
1092	(cd $(srctree); KBUILD_OUTPUT=$(abs_objtree) $(PYTEST))
1093
1094PHONY += check
1095
1096# barebox image
1097# ---------------------------------------------------------------------------
1098barebox: $(BAREBOX_LDS) $(BAREBOX_OBJS) $(kallsyms.o) FORCE
1099	$(call if_changed_rule,barebox__)
1100ifeq ($(BAREBOX_PROPER),barebox)
1101	$(Q)rm -f .old_version
1102endif
1103
1104barebox.fit: images/barebox-$(CONFIG_ARCH_LINUX_NAME).fit
1105	$(Q)ln -fsn $< $@
1106
1107barebox.srec: barebox
1108	$(OBJCOPY) -O srec $< $@
1109
1110OBJCOPYFLAGS_vmbarebox = $(call objcopy-option,--strip-section-headers,--strip-all)  \
1111			 --remove-section=.comment \
1112			 --remove-section=.note* \
1113			 --remove-section=.gnu.hash
1114
1115vmbarebox: barebox FORCE
1116	$(call if_changed,objcopy)
1117
1118quiet_cmd_barebox_proper__ = CC      $@
1119      cmd_barebox_proper__ = $(CC) -r -o $@ -Wl,--whole-archive $(BAREBOX_OBJS)
1120
1121.tmp_barebox.o: $(BAREBOX_OBJS) $(kallsyms.o) FORCE
1122	$(if $(CONFIG_KALLSYMS),,+$(call cmd,barebox_version))
1123	$(call cmd,barebox_proper__)
1124	$(Q)echo 'savedcmd_$@ := $(cmd_barebox_proper__)' > $(@D)/.$(@F).cmd
1125	$(Q)rm -f .old_version
1126
1127barebox.o: .tmp_barebox.o FORCE
1128	$(call if_changed,objcopy)
1129
1130# The actual objects are generated when descending,
1131# make sure no implicit rule kicks in
1132$(sort $(BAREBOX_OBJS)) $(BAREBOX_LDS) $(BAREBOX_PBL_OBJS): $(barebox-dirs) ;
1133
1134# Handle descending into subdirectories listed in $(barebox-dirs)
1135# Preset locale variables to speed up the build process. Limit locale
1136# tweaks to this spot to avoid wrong language settings when running
1137# make menuconfig etc.
1138# Error messages still appears in the original language
1139
1140PHONY += $(barebox-dirs)
1141$(barebox-dirs): prepare scripts
1142	$(Q)$(MAKE) $(build)=$@
1143
1144# Store (new) KERNELRELASE string in include/config/kernel.release
1145localversion = $(shell $(srctree)/scripts/setlocalversion $(srctree))
1146include/config/kernel.release: FORCE
1147	$(Q)rm -f $@
1148	$(Q)echo $(KERNELVERSION)$(localversion) > $@
1149
1150# Additional helpers built in scripts/
1151# Carefully list dependencies so we do not try to build scripts twice
1152# in parallel
1153PHONY += scripts
1154scripts: scripts_basic scripts_dtc include/generated/utsrelease.h
1155	$(Q)$(MAKE) $(build)=$(@)
1156
1157# Things we need to do before we recursively start building the kernel
1158# or the modules are listed in "prepare".
1159# A multi level approach is used. prepareN is processed before prepareN-1.
1160# archprepare is used in arch Makefiles and when processed asm symlink,
1161# version.h and scripts_basic is processed / created.
1162

1074-1162줄 해설

원본에 보이는 모든 줄을 순서대로 설명한다. 빈 줄도 block 경계로 남겨, 코드와 설명의 위치가 어긋나지 않게 했다.

1074# This target has pytest select the YAML env matching CONFIG_NAME if available.

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. 관련 설정은 CONFIG_NAME이다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1075# Use pytest --lg-env $labgrid_env_yaml directly if you need more than that.

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1076(빈 줄)

# Use pytest --lg-env $labgrid_env_yaml directly if you need more than that.까지의 동작과 labgrid-env := $(srctree)/test/$(SRCARCH)/$(CONFIG_NAME).yaml에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 embedded DTB 상태가 아래 블록의 입력으로 사용되는 경계다.

1077labgrid-env := $(srctree)/test/$(SRCARCH)/$(CONFIG_NAME).yaml

원본 1077번 줄의 labgrid-env := $(srctree)/test/$(SRCARCH)/$(CONFIG_NAME).yaml는 앞의 이전 block 경계 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1078(빈 줄)

labgrid-env := $(srctree)/test/$(SRCARCH)/$(CONFIG_NAME).yaml까지의 동작과 check:에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1079check:

'check' label이다. 이곳을 참조하는 branch를 역검색하고 각 진입 경로의 register, stack, lock 조건이 같은지 확인한다.

1080ifeq ($(CONFIG_NAME),"")

ifeq($(CONFIG_NAME),"")를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 barebox: $의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 SoC boot image 항목과 대조한다.

1081@echo "error: can't autoload labgrid env with CONFIG_NAME unset!" >&2

원본 1081번 줄의 @echo "error: can't autoload labgrid env with CONFIG_NAME unset!" >&2는 앞의 ifeq ($(CONFIG_NAME),"") 결과를 받아 다음 @exit 1로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1082@exit 1

원본 1082번 줄의 @exit 1는 앞의 @echo "error: can't autoload labgrid env with CONFIG_NAME unset!" >&2 결과를 받아 다음 endif로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1083endif

원본 1083번 줄의 endif는 앞의 @exit 1 결과를 받아 다음 @if [ ! -r "$(labgrid-env)" ]; then \로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1084@if [ ! -r "$(labgrid-env)" ]; then \

원본 1084번 줄의 @if [ ! -r "$(labgrid-env)" ]; then \는 앞의 endif 결과를 받아 다음 echo "error: No Labgrid environment at $(labgrid-env)!" >&2; \로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1085echo "error: No Labgrid environment at $(labgrid-env)!" >&2; \

원본 1085번 줄의 echo "error: No Labgrid environment at $(labgrid-env)!" >&2; \는 앞의 @if [ ! -r "$(labgrid-env)" ]; then \ 결과를 받아 다음 echo "Choose a different config (or change CONFIG_NAME)" >&2; \로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1086echo "Choose a different config (or change CONFIG_NAME)" >&2; \

config(or change CONFIG_NAME)를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 barebox: $의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 SoC boot image 항목과 대조한다.

1087echo "that can be automatically tested" >&2; \

원본 1087번 줄의 echo "that can be automatically tested" >&2; \는 앞의 echo "Choose a different config (or change CONFIG_NAME)" >&2; \ 결과를 받아 다음 exit 1; \로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1088exit 1; \

원본 1088번 줄의 exit 1; \는 앞의 echo "that can be automatically tested" >&2; \ 결과를 받아 다음 fi로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1089fi

원본 1089번 줄의 fi는 앞의 exit 1; \ 결과를 받아 다음 @echo로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1090@echo

원본 1090번 줄의 @echo는 앞의 fi 결과를 받아 다음 @# This is intentionally not @suppressed, to make it easier to reproduce로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1091@# This is intentionally not @suppressed, to make it easier to reproduce

원본 1091번 줄의 @# This is intentionally not @suppressed, to make it easier to reproduce는 앞의 @echo 결과를 받아 다음 (cd $(srctree); KBUILD_OUTPUT=$(abs_objtree) $(PYTEST))로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1092(cd $(srctree); KBUILD_OUTPUT=$(abs_objtree) $(PYTEST))

원본 1092번 줄의 (cd $(srctree); KBUILD_OUTPUT=$(abs_objtree) $(PYTEST))는 앞의 @# This is intentionally not @suppressed, to make it easier to reproduce 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1093(빈 줄)

(cd $(srctree); KBUILD_OUTPUT=$(abs_objtree) $(PYTEST))까지의 동작과 PHONY += check에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1094PHONY += check

원본 1094번 줄의 PHONY += check는 앞의 이전 block 경계 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1095(빈 줄)

PHONY += check까지의 동작과 # barebox image에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1096# barebox image

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1097# ---------------------------------------------------------------------------

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1098barebox: $(BAREBOX_LDS) $(BAREBOX_OBJS) $(kallsyms.o) FORCE

원본 1098번 줄의 barebox: $(BAREBOX_LDS) $(BAREBOX_OBJS) $(kallsyms.o) FORCE는 앞의 # --------------------------------------------------------------------------- 결과를 받아 다음 $(call if_changed_rule,barebox__)로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1099$(call if_changed_rule,barebox__)

원본 1099번 줄의 $(call if_changed_rule,barebox__)는 앞의 barebox: $(BAREBOX_LDS) $(BAREBOX_OBJS) $(kallsyms.o) FORCE 결과를 받아 다음 ifeq ($(BAREBOX_PROPER),barebox)로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1100ifeq ($(BAREBOX_PROPER),barebox)

ifeq($(BAREBOX_PROPER),barebox)를 호출한다. 반환값을 직접 사용하지 않으므로 이 함수가 실패를 내부 처리하는지 확인해야 한다. pointer 인자는 barebox: $의 소유권을 넘기는지 호출 동안만 빌리는지 구분하고, 호출 뒤 공개되는 상태를 SoC boot image 항목과 대조한다.

1101$(Q)rm -f .old_version

원본 1101번 줄의 $(Q)rm -f .old_version는 앞의 ifeq ($(BAREBOX_PROPER),barebox) 결과를 받아 다음 endif로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1102endif

원본 1102번 줄의 endif는 앞의 $(Q)rm -f .old_version 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1103(빈 줄)

endif까지의 동작과 barebox.fit: images/barebox-$(CONFIG_ARCH_LINUX_NAME).fit에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1104barebox.fit: images/barebox-$(CONFIG_ARCH_LINUX_NAME).fit

원본 1104번 줄의 barebox.fit: images/barebox-$(CONFIG_ARCH_LINUX_NAME).fit는 앞의 이전 block 경계 결과를 받아 다음 $(Q)ln -fsn $< $@로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1105$(Q)ln -fsn $< $@

원본 1105번 줄의 $(Q)ln -fsn $< $@는 앞의 barebox.fit: images/barebox-$(CONFIG_ARCH_LINUX_NAME).fit 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1106(빈 줄)

$(Q)ln -fsn $< $@까지의 동작과 barebox.srec: barebox에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1107barebox.srec: barebox

원본 1107번 줄의 barebox.srec: barebox는 앞의 이전 block 경계 결과를 받아 다음 $(OBJCOPY) -O srec $< $@로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1108$(OBJCOPY) -O srec $< $@

원본 1108번 줄의 $(OBJCOPY) -O srec $< $@는 앞의 barebox.srec: barebox 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1109(빈 줄)

$(OBJCOPY) -O srec $< $@까지의 동작과 OBJCOPYFLAGS_vmbarebox = $(call objcopy-option,--strip-section-headers,--strip-all) \에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1110OBJCOPYFLAGS_vmbarebox = $(call objcopy-option,--strip-section-headers,--strip-all) \

원본 1110번 줄의 OBJCOPYFLAGS_vmbarebox = $(call objcopy-option,--strip-section-headers,--strip-all) \는 앞의 이전 block 경계 결과를 받아 다음 --remove-section=.comment \로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1111--remove-section=.comment \

원본 1111번 줄의 --remove-section=.comment \는 앞의 OBJCOPYFLAGS_vmbarebox = $(call objcopy-option,--strip-section-headers,--strip-all) \ 결과를 받아 다음 --remove-section=.note* \로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1112--remove-section=.note* \

원본 1112번 줄의 --remove-section=.note* \는 앞의 --remove-section=.comment \ 결과를 받아 다음 --remove-section=.gnu.hash로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1113--remove-section=.gnu.hash

원본 1113번 줄의 --remove-section=.gnu.hash는 앞의 --remove-section=.note* \ 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1114(빈 줄)

--remove-section=.gnu.hash까지의 동작과 vmbarebox: barebox FORCE에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1115vmbarebox: barebox FORCE

원본 1115번 줄의 vmbarebox: barebox FORCE는 앞의 이전 block 경계 결과를 받아 다음 $(call if_changed,objcopy)로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1116$(call if_changed,objcopy)

원본 1116번 줄의 $(call if_changed,objcopy)는 앞의 vmbarebox: barebox FORCE 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1117(빈 줄)

$(call if_changed,objcopy)까지의 동작과 quiet_cmd_barebox_proper__ = CC $@에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1118quiet_cmd_barebox_proper__ = CC $@

원본 1118번 줄의 quiet_cmd_barebox_proper__ = CC $@는 앞의 이전 block 경계 결과를 받아 다음 cmd_barebox_proper__ = $(CC) -r -o $@ -Wl,--whole-archive $(BAREBOX_OBJS)로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1119cmd_barebox_proper__ = $(CC) -r -o $@ -Wl,--whole-archive $(BAREBOX_OBJS)

원본 1119번 줄의 cmd_barebox_proper__ = $(CC) -r -o $@ -Wl,--whole-archive $(BAREBOX_OBJS)는 앞의 quiet_cmd_barebox_proper__ = CC $@ 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1120(빈 줄)

cmd_barebox_proper__ = $(CC) -r -o $@ -Wl,--whole-archive $(BAREBOX_OBJS)까지의 동작과 .tmp_barebox.o: $(BAREBOX_OBJS) $(kallsyms.o) FORCE에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1121.tmp_barebox.o: $(BAREBOX_OBJS) $(kallsyms.o) FORCE

원본 1121번 줄의 .tmp_barebox.o: $(BAREBOX_OBJS) $(kallsyms.o) FORCE는 앞의 이전 block 경계 결과를 받아 다음 $(if $(CONFIG_KALLSYMS),,+$(call cmd,barebox_version))로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1122$(if $(CONFIG_KALLSYMS),,+$(call cmd,barebox_version))

원본 1122번 줄의 $(if $(CONFIG_KALLSYMS),,+$(call cmd,barebox_version))는 앞의 .tmp_barebox.o: $(BAREBOX_OBJS) $(kallsyms.o) FORCE 결과를 받아 다음 $(call cmd,barebox_proper__)로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1123$(call cmd,barebox_proper__)

원본 1123번 줄의 $(call cmd,barebox_proper__)는 앞의 $(if $(CONFIG_KALLSYMS),,+$(call cmd,barebox_version)) 결과를 받아 다음 $(Q)echo 'savedcmd_$@ := $(cmd_barebox_proper__)' > $(@D)/.$(@F).cmd로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1124$(Q)echo 'savedcmd_$@ := $(cmd_barebox_proper__)' > $(@D)/.$(@F).cmd

원본 1124번 줄의 $(Q)echo 'savedcmd_$@ := $(cmd_barebox_proper__)' > $(@D)/.$(@F).cmd는 앞의 $(call cmd,barebox_proper__) 결과를 받아 다음 $(Q)rm -f .old_version로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1125$(Q)rm -f .old_version

원본 1125번 줄의 $(Q)rm -f .old_version는 앞의 $(Q)echo 'savedcmd_$@ := $(cmd_barebox_proper__)' > $(@D)/.$(@F).cmd 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1126(빈 줄)

$(Q)rm -f .old_version까지의 동작과 barebox.o: .tmp_barebox.o FORCE에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1127barebox.o: .tmp_barebox.o FORCE

원본 1127번 줄의 barebox.o: .tmp_barebox.o FORCE는 앞의 이전 block 경계 결과를 받아 다음 $(call if_changed,objcopy)로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1128$(call if_changed,objcopy)

원본 1128번 줄의 $(call if_changed,objcopy)는 앞의 barebox.o: .tmp_barebox.o FORCE 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1129(빈 줄)

$(call if_changed,objcopy)까지의 동작과 # The actual objects are generated when descending,에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1130# The actual objects are generated when descending,

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1131# make sure no implicit rule kicks in

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1132$(sort $(BAREBOX_OBJS)) $(BAREBOX_LDS) $(BAREBOX_PBL_OBJS): $(barebox-dirs) ;

원본 1132번 줄의 $(sort $(BAREBOX_OBJS)) $(BAREBOX_LDS) $(BAREBOX_PBL_OBJS): $(barebox-dirs) ;는 앞의 # make sure no implicit rule kicks in 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1133(빈 줄)

$(sort $(BAREBOX_OBJS)) $(BAREBOX_LDS) $(BAREBOX_PBL_OBJS): $(barebox-dirs) ;까지의 동작과 # Handle descending into subdirectories listed in $(barebox-dirs)에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1134# Handle descending into subdirectories listed in $(barebox-dirs)

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1135# Preset locale variables to speed up the build process. Limit locale

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1136# tweaks to this spot to avoid wrong language settings when running

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1137# make menuconfig etc.

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1138# Error messages still appears in the original language

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1139(빈 줄)

# Error messages still appears in the original language까지의 동작과 PHONY += $(barebox-dirs)에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1140PHONY += $(barebox-dirs)

원본 1140번 줄의 PHONY += $(barebox-dirs)는 앞의 이전 block 경계 결과를 받아 다음 $(barebox-dirs): prepare scripts로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1141$(barebox-dirs): prepare scripts

원본 1141번 줄의 $(barebox-dirs): prepare scripts는 앞의 PHONY += $(barebox-dirs) 결과를 받아 다음 $(Q)$(MAKE) $(build)=$@로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1142$(Q)$(MAKE) $(build)=$@

원본 1142번 줄의 $(Q)$(MAKE) $(build)=$@는 앞의 $(barebox-dirs): prepare scripts 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1143(빈 줄)

$(Q)$(MAKE) $(build)=$@까지의 동작과 # Store (new) KERNELRELASE string in include/config/kernel.release에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1144# Store (new) KERNELRELASE string in include/config/kernel.release

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1145localversion = $(shell $(srctree)/scripts/setlocalversion $(srctree))

원본 1145번 줄의 localversion = $(shell $(srctree)/scripts/setlocalversion $(srctree))는 앞의 # Store (new) KERNELRELASE string in include/config/kernel.release 결과를 받아 다음 include/config/kernel.release: FORCE로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1146include/config/kernel.release: FORCE

원본 1146번 줄의 include/config/kernel.release: FORCE는 앞의 localversion = $(shell $(srctree)/scripts/setlocalversion $(srctree)) 결과를 받아 다음 $(Q)rm -f $@로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1147$(Q)rm -f $@

원본 1147번 줄의 $(Q)rm -f $@는 앞의 include/config/kernel.release: FORCE 결과를 받아 다음 $(Q)echo $(KERNELVERSION)$(localversion) > $@로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1148$(Q)echo $(KERNELVERSION)$(localversion) > $@

원본 1148번 줄의 $(Q)echo $(KERNELVERSION)$(localversion) > $@는 앞의 $(Q)rm -f $@ 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1149(빈 줄)

$(Q)echo $(KERNELVERSION)$(localversion) > $@까지의 동작과 # Additional helpers built in scripts/에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1150# Additional helpers built in scripts/

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1151# Carefully list dependencies so we do not try to build scripts twice

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1152# in parallel

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1153PHONY += scripts

원본 1153번 줄의 PHONY += scripts는 앞의 # in parallel 결과를 받아 다음 scripts: scripts_basic scripts_dtc include/generated/utsrelease.h로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1154scripts: scripts_basic scripts_dtc include/generated/utsrelease.h

원본 1154번 줄의 scripts: scripts_basic scripts_dtc include/generated/utsrelease.h는 앞의 PHONY += scripts 결과를 받아 다음 $(Q)$(MAKE) $(build)=$(@)로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1155$(Q)$(MAKE) $(build)=$(@)

원본 1155번 줄의 $(Q)$(MAKE) $(build)=$(@)는 앞의 scripts: scripts_basic scripts_dtc include/generated/utsrelease.h 결과를 받아 다음 다음 block 경계로 넘기는 중간 연산이다. 이 줄이 바꾸는 register·field·list link를 찾고, 변경 뒤에도 'ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다' 조건이 유지되는지 확인한다.

1156(빈 줄)

$(Q)$(MAKE) $(build)=$(@)까지의 동작과 # Things we need to do before we recursively start building the kernel에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

1157# Things we need to do before we recursively start building the kernel

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1158# or the modules are listed in "prepare".

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1159# A multi level approach is used. prepareN is processed before prepareN-1.

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1160# archprepare is used in arch Makefiles and when processed asm symlink,

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1161# version.h and scripts_basic is processed / created.

# 전처리 지시문으로 이 줄 아래의 code가 binary에 존재할지를 결정한다. architecture 또는 build stage 조건을 여닫는다. 현재 .config와 compiler의 -dD -E 출력에서 실제로 남은 branch를 확인한 뒤 line-by-line 흐름에 포함한다.

1162(빈 줄)

# version.h and scripts_basic is processed / created.까지의 동작과 다음 block 경계에서 시작하는 동작을 나누는 빈 줄이다. 앞 블록이 만든 SoC boot image 상태가 아래 블록의 입력으로 사용되는 경계다.

내부 동작을 더 깊게 읽기

01

진입 조건을 먼저 고정한다

Kconfig에서 들어온 실행은 빌드 시점에 놓여 있다. 이때 interrupt, MMU/cache, stack, heap 중 무엇이 이미 준비되었는지 소스의 호출자까지 올라가 확인한다. 같은 함수라도 SPL, relocation 전후, app thread처럼 호출 문맥이 달라지면 허용되는 API와 지연 시간이 달라진다.

barebox 실행 파일과 images/ 아래 ROM 투입 파일을 구분한다. PBL 압축, 헤더, load address가 최종 파일에서 어디에 놓이는지 본다.

02

중심 객체의 생성과 공개를 나눈다

이 글의 중심 객체는 barebox: $다. 메모리를 확보한 시점, 필드를 채운 시점, 전역 list나 다른 subsystem에 공개한 시점을 구분한다. 공개 뒤 오류가 발생한다면 목록에서 제거하고 child, buffer, reference를 역순으로 정리하는지 확인한다.

빌드 산출물 관점에서는 barebox, barebox.bin, PBL, images/* 안에 해당 symbol과 section이 실제로 포함되었는지도 map과 objdump로 검증한다.

03

주소, 크기와 정렬을 계산한다

부트 코드의 오류는 논리보다 주소 계산에서 먼저 드러나는 경우가 많다. source range, destination range, header가 말하는 payload size, block 또는 page 단위를 표로 적고 각 구간의 끝 주소를 직접 계산한다. 끝 주소는 start + size - 1인지 exclusive end인지 API 계약을 확인한다.

ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다.

04

성공 flag와 실제 완료 시점을 맞춘다

built-in objects → barebox ELF → PBL and DTB 구간에서는 부분 초기화 상태가 생길 수 있다. flag, list insertion, callback 등록, storage write 완료 중 무엇이 성공의 기준인지 찾는다. hardware write나 DMA가 포함되면 함수 반환과 장치 완료가 같은 시점인지도 확인한다.

다른 CPU, interrupt handler, USB completion 또는 shell command가 상태를 관찰할 수 있다면 memory ordering과 lock 범위도 함께 읽는다.

05

마지막 handoff의 계약을 적는다

정상 경로는 images/에서 끝난다. 이 단계가 함수 반환인지, scheduler 전환인지, 다른 image로의 비복귀 분기인지 구분한다. 비복귀 handoff라면 cache clean/invalidate, interrupt disable, 장치 quiesce, argument register와 FDT 또는 image address가 최종 점검 항목이다.

반환하는 경로라면 caller가 오류와 부분 성공을 구분하고 다음 후보 또는 복구 경로를 선택하는지 확인한다.

구현을 읽을 때 놓치기 쉬운 부분

01

PBL과 full barebox는 서로 다른 실행 환경을 가진다

PBL은 Boot ROM이 제공한 SRAM과 제한된 stack에서 DDR 또는 다음 image를 준비한다. full barebox의 driver, malloc, shell API가 source tree에 있다고 PBL에서 호출할 수 있는 것은 아니며 __PBL__ 조건과 PBL object 목록을 확인해야 한다.

같은 함수 이름도 PBL용 구현과 runtime 구현이 따로 선택될 수 있다.

02

링크 주소와 image packing 단계를 분리한다

PBL ELF의 text 주소, raw payload offset, SoC header가 선언한 load/entry 주소가 모두 맞아야 ROM에서 첫 instruction에 도달한다. piggydata로 full image를 포함하면 압축 data의 source와 decompression destination도 memory map에 추가한다.

map, objdump, final image hexdump에서 각 boundary를 같은 표로 대조한다.

03

크기 제한은 section과 기능 단위로 관리한다

SRAM 제한을 넘으면 header 생성은 성공해도 runtime stack이나 decompressor 영역이 겹칠 수 있다. text/data/BSS, embedded DT와 stack의 최대치를 따로 계산한다.

feature toggle 전후 section delta와 PBL call graph를 기록해 full-only code가 끌려 들어온 원인을 찾는다.

객체와 수명

대상만들어지는 시점유효 범위확인할 조건
barebox: $built-in objectsimages/ 또는 오류 정리 완료까지ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다.
입력 buffer / metadataKconfigparse와 검증이 끝날 때까지길이, 정렬, 소유권, 변조 가능성
등록된 list / descriptorbarebox ELFsubsystem 종료 또는 image handoff까지이중 등록, dangling pointer, 오류 unwind
hardware 또는 persistent state실제 write/probe가 완료된 뒤reset 또는 명시적 해제·갱신까지flush, timeout, 전원 차단, rollback
barebox, barebox.bin, PBL, images/*link/image 생성 시점다음 stage가 새 image로 교체할 때까지load address, entry, section과 header 일치

실패 지점과 증상

#조건관찰되는 증상먼저 볼 단계
01잘못된 image targetROM header 없음built-in objects
02PBL/main address overlap압축 해제 손상barebox ELF
03DTB 미포함보드 식별 실패PBL and DTB

로그가 끊긴 마지막 함수만 고치지 않는다. 그 함수가 받은 주소, size, flag가 만들어진 앞 단계까지 올라가고, 오류 뒤 등록 객체와 hardware 상태가 남았는지도 확인한다.

소스 밖에서 확인할 증거

소스 해석은 실제 빌드 산출물과 target 로그로 닫아야 한다. 아래 명령의 보드 이름과 toolchain prefix는 사용 중인 빌드 환경에 맞게 바꾼다.

#목적명령 또는 계측판정 기준
01빌드 구성make <board>_defconfig && make V=1Kconfig, linker section과 최종 barebox image 생성 명령을 보관한다.
02symbol과 section${CROSS_COMPILE}nm -n barebox | grep 'barebox'initcall, command, driver descriptor가 image에 남았는지 확인한다.
03주소와 resourceiomemimage, malloc, stack, device resource와 reserved 영역의 겹침을 확인한다.
04device 상태devinfo; drvinfo; of_dumpDT node 생성, device 등록, driver probe를 서로 구분한다.
05boot 정책boot -d -v; state -dentry 탐색 결과와 persistent state가 실제 선택에 미친 영향을 확인한다.

직접 확인할 실험

  1. 01
    make V=1로 PBL과 main 링크 명령 분리

    built-in objects 진입 전후에 main image의 주소·크기·반환값과 timestamp를 함께 남긴다. 결과는 정상 부팅 여부로 끝내지 말고 ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다. 조건이 처음 깨지는 줄을 기록한다.

  2. 02
    readelf와 barebox.map 주소 대조

    barebox ELF 진입 전후에 PBL objects의 주소·크기·반환값과 timestamp를 함께 남긴다. 결과는 정상 부팅 여부로 끝내지 말고 ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다. 조건이 처음 깨지는 줄을 기록한다.

  3. 03
    images/*.imx 또는 *.img 헤더 도구로 검사

    PBL and DTB 진입 전후에 embedded DTB의 주소·크기·반환값과 timestamp를 함께 남긴다. 결과는 정상 부팅 여부로 끝내지 말고 ROM용 image의 header, load address, entry와 포함된 PBL/main payload의 위치가 보드 부트 규약과 일치해야 한다. 조건이 처음 깨지는 줄을 기록한다.

원문과 다음 글