boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

commit c651b11ca099c4b878a023fbd61bde9f8e108eee
parent e301f7533bda6a2b139ad1771894919c12b87765
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue,  5 May 2026 17:19:04 -0700

hex2pp: single-pass + fixup list; cut wasted work; wire P1 lint

C reference (hex2pp.c) and the self-hosted P1 port (hex2pp.P1) both
move from a two-pass scanner to a single scan that emits zero
placeholders for forward refs and records fixups; an after-scan walk
resolves and patches them. Drops the duplicate-label dup-loop, replaces
the per-byte emit loops in .fill / .align with bulk fills, and avoids
copying name tokens by using pointer spans into the input buffer.

Discovered while porting: the P1 macro table only defines st/ld with
register a1 for offsets up to 64, and M0 silently passes unrecognized
tokens through as text. Two store/load sites in the new fixup record
needed offsets 72 and 80 and were producing misaligned binaries that
crashed at startup. scripts/lint.sh already catches this exact case
but was wired only as a Make dependency, never invoked. Run it from
the M1pp / hex2pp recipes on the host (the busybox build container has
no python3) before handing off to boot-build-p1.sh.

Diffstat:
MMakefile | 2++
Mhex2pp/hex2pp.P1 | 945++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Mhex2pp/hex2pp.c | 240++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
3 files changed, 803 insertions(+), 384 deletions(-)

diff --git a/Makefile b/Makefile @@ -256,9 +256,11 @@ ALPINE_GCC = podman run --rm --pull=never --platform $(PLATFORM_$(1)) \ -v $(CURDIR):/work -w /work boot2-alpine-gcc:$(1) $(M1PP_BINS): build/%/M1pp/M1pp: M1pp/M1pp.P1 $(P1_BUILD_DEPS) + ARCH=$* sh scripts/lint.sh M1pp/M1pp.P1 $(call PODMAN,$*) sh scripts/boot-build-p1.sh M1pp/M1pp.P1 $@ $(HEX2PP_BINS): build/%/hex2pp/hex2pp: hex2pp/hex2pp.P1 $(P1_BUILD_DEPS) + ARCH=$* sh scripts/lint.sh hex2pp/hex2pp.P1 $(call PODMAN,$*) sh scripts/boot-build-p1.sh hex2pp/hex2pp.P1 $@ $(SCHEME1_BINS): build/%/scheme1/scheme1: $(SCHEME1_SRC) $(P1PP_BUILD_DEPS) diff --git a/hex2pp/hex2pp.P1 b/hex2pp/hex2pp.P1 @@ -2,9 +2,10 @@ ## ## Mirrors hex2pp/hex2pp.c. See docs/HEX2pp.md for the full spec; brief: ## -## Single input file, scanned in two passes. Pass 1 records label -## definitions while advancing a position counter (ip). Pass 2 emits -## bytes, resolving label references against the table built in pass 1. +## Input is scanned once. Label definitions are recorded into a table +## on the fly; label references emit zero placeholders and append a +## fixup record. After the scan, fixups are resolved against the +## completed label table and patched into the output buffer. ## ## Active syntax: ## digits in current byte mode -> raw bytes (HEX or BINARY) @@ -38,6 +39,8 @@ DEFINE H2_TEXT_CAP 0000800000000000 DEFINE H2_LABEL_CAP 0000100000000000 DEFINE H2_TOKEN_CAP 0010000000000000 DEFINE H2_SCOPE_CAP 2000000000000000 +DEFINE H2_FIXUP_CAP 0000100000000000 +DEFINE H2_SCOPE_HISTORY_CAP 0000400000000000 ## openat / mode constants (Linux generic). DEFINE H2_O_WRONLY_CREAT_TRUNC 4102000000000000 @@ -57,9 +60,9 @@ DEFINE ZERO4 '00000000' ## Region sizes (matching the C caps): ## scope_stack 32 * 8 = 256 B ## line_scratch = 64 B -## name_buf = 4096 B -## label_buf = 4096 B -## other_buf = 4096 B +## name_buf = 4096 B (used by read_directive_name) +## label_buf = 4096 B (unused since single-pass refactor) +## other_buf = 4096 B (unused since single-pass refactor) ## pat_buf = 4096 B ## ev_bytes = 8 B ## df_byte = 8 B @@ -67,6 +70,8 @@ DEFINE ZERO4 '00000000' ## output_buf = 128 MiB ## text_buf = 8 MiB ## labels = 32 MiB (1<<20 * 32 B) +## fixups = 96 MiB (1<<20 * 96 B) +## scope_history = 32 MiB (1<<22 * 8 B) ## ## Compact cumulative offsets in 8-byte little-endian hex. DEFINE H2_OFF_scope_stack 0000000000000000 @@ -77,14 +82,18 @@ DEFINE H2_OFF_other_buf 4021000000000000 DEFINE H2_OFF_pat_buf 4031000000000000 DEFINE H2_OFF_ev_bytes 4041000000000000 DEFINE H2_OFF_df_byte 5041000000000000 -## input_buf: 0x004200 + 16 MiB -## output_buf: 0x1004200 + 128 MiB -## text_buf: 0x9004200 + 8 MiB -## labels: 0x9804200 + 32 MiB (end at ~184 MiB; ELF p_memsz is 512 MiB). +## input_buf: 0x00004200 + 16 MiB +## output_buf: 0x01004200 + 128 MiB +## text_buf: 0x09004200 + 8 MiB +## labels: 0x09804200 + 32 MiB +## fixups: 0x0B804200 + 96 MiB +## scope_history: 0x11804200 + 32 MiB (end at ~313 MiB; ELF p_memsz is 512 MiB). DEFINE H2_OFF_input_buf 0042000000000000 DEFINE H2_OFF_output_buf 0042000100000000 DEFINE H2_OFF_text_buf 0042000900000000 DEFINE H2_OFF_labels 0042800900000000 +DEFINE H2_OFF_fixups 0042800B00000000 +DEFINE H2_OFF_scope_history 0042801100000000 ## --- p1_main: argv parse -> load input -> two passes -> write -> exit ------ @@ -295,30 +304,21 @@ DEFINE H2_OFF_labels 0042800900000000 la_br &load_input call - # ---- Pass 1 ---------------------------------------------------------- - li_t0 %1 %0 - la_a0 &pass + # ---- Single scan: builds labels + fixup list ------------------------- + li_t0 %0 %0 + la_a0 &ip + st_t0,a0,0 + la_a0 &output_used st_t0,a0,0 - la_br &reset_pass_state - call - la_br &process_input - call la_a0 &scope_depth - ld_t0,a0,0 - la_br &err_scope_unclosed - bnez_t0 - - # Clear cur_path so any post-pass error reports without file:line. - li_t0 %0 %0 - la_a0 &cur_path st_t0,a0,0 - - # ---- Pass 2 ---------------------------------------------------------- - li_t0 %2 %0 - la_a0 &pass + la_a0 &scope_seq + st_t0,a0,0 + la_a0 &ptrsize_used + st_t0,a0,0 + li_t0 %4 %0 + la_a0 &ptrsize st_t0,a0,0 - la_br &reset_pass_state - call la_br &process_input call la_a0 &scope_depth @@ -326,6 +326,11 @@ DEFINE H2_OFF_labels 0042800900000000 la_br &err_scope_unclosed bnez_t0 + # ---- Resolve fixups -------------------------------------------------- + la_br &patch_fixups + call + + # Clear cur_path so any post-fixup error reports without file:line. li_t0 %0 %0 la_a0 &cur_path st_t0,a0,0 @@ -336,26 +341,6 @@ DEFINE H2_OFF_labels 0042800900000000 li_a0 %0 %0 eret -## reset_pass_state(): ip=0, output_used=0, scope_depth=0, scope_seq=0, -## ptrsize=4, ptrsize_used=0. -:reset_pass_state - enter_0 - li_t0 %0 %0 - la_a0 &ip - st_t0,a0,0 - la_a0 &output_used - st_t0,a0,0 - la_a0 &scope_depth - st_t0,a0,0 - la_a0 &scope_seq - st_t0,a0,0 - la_a0 &ptrsize_used - st_t0,a0,0 - li_t0 %4 %0 - la_a0 &ptrsize - st_t0,a0,0 - eret - ## --- Input loader ---------------------------------------------------------- ## load_input(): openat(input_path), read into input_buf until EOF. :load_input @@ -479,16 +464,13 @@ DEFINE H2_OFF_labels 0042800900000000 ld_t0,a0,0 addi_t0,t0,1 st_t0,a0,0 - # name = read_name(name_buf, MAX_TOKEN) - la_a0 &name_buf_ptr - ld_a0,a0,0 - li_a1 H2_TOKEN_CAP - la_br &read_name + # scan_name -> length in a0, pointer in sn_start (no copy). + la_br &scan_name call la_a1 &name_len st_a0,a1,0 # dotted = (name[0] == '.') AND scope_depth > 0 - la_a0 &name_buf_ptr + la_a0 &sn_start ld_a0,a0,0 lb_t0,a0,0 li_t1 %46 %0 @@ -514,13 +496,7 @@ DEFINE H2_OFF_labels 0042800900000000 la_a2 &name_scope st_t0,a2,0 :handle_label_dispatch - # Pass 1 only: define_label(name, len, scope) - la_a0 &pass - ld_t0,a0,0 - li_t1 %1 %0 - la_br &scan_loop - bne_t0,t1 - la_a0 &name_buf_ptr + la_a0 &sn_start ld_a0,a0,0 la_a1 &name_len ld_a1,a1,0 @@ -937,8 +913,47 @@ DEFINE H2_OFF_labels 0042800900000000 li_a0 %8 %0 ret +## scan_name() -> a0=length, sn_start=pointer into input buffer at name start. +## Advances scan_pos past the name. Fatal on empty. +:scan_name + enter_0 + la_a0 &scan_pos + ld_t0,a0,0 + la_a1 &sn_start + st_t0,a1,0 +:sn_loop + la_a0 &scan_pos + ld_t0,a0,0 + la_a1 &scan_end + ld_t1,a1,0 + la_br &sn_done + beq_t0,t1 + la_br &sn_done + blt_t1,t0 + lb_a0,t0,0 + la_br &is_name_terminator + call + la_br &sn_done + bnez_a0 + la_a0 &scan_pos + ld_t0,a0,0 + addi_t0,t0,1 + st_t0,a0,0 + la_br &sn_loop + b +:sn_done + la_a0 &scan_pos + ld_t0,a0,0 + la_a1 &sn_start + ld_t1,a1,0 + sub_a0,t0,t1 + la_br &err_empty_name + beqz_a0 + eret + ## read_name(a0=out_buf, a1=max) -> a0=length. Reads scan_pos into out_buf ## until is_name_terminator or scan_end. Fatal on overflow / empty. +## (Kept for reference; superseded by scan_name in single-pass refactor.) :read_name enter_0 la_a2 &rn_out @@ -1453,7 +1468,7 @@ DEFINE H2_OFF_labels 0042800900000000 li_a0 %0 %0 eret -## define_label(a0=src, a1=len, a2=scope_id). +## define_label(a0=src, a1=len, a2=scope_id). First definition wins. :define_label enter_0 la_a3 &dl_src @@ -1462,48 +1477,6 @@ DEFINE H2_OFF_labels 0042800900000000 st_a1,a3,0 la_a3 &dl_scope st_a2,a3,0 - li_t0 %0 %0 - la_a0 &dl_i - st_t0,a0,0 -:dl_dup_loop - la_a0 &dl_i - ld_t0,a0,0 - la_a1 &label_count - ld_t1,a1,0 - la_br &dl_dup_done - beq_t0,t1 - mov_a0,t0 - la_br &label_addr - call - la_a3 &dl_label - st_a0,a3,0 - # scope match? - ld_t0,a0,24 - la_a1 &dl_scope - ld_t1,a1,0 - la_br &dl_dup_next - bne_t0,t1 - # name match? - la_a0 &dl_label - ld_a0,a0,0 - la_a1 &dl_src - ld_a1,a1,0 - la_a2 &dl_len - ld_a2,a2,0 - la_br &name_eq - call - la_br &dl_dup_next - beqz_a0 - la_br &err_duplicate_label - b -:dl_dup_next - la_a0 &dl_i - ld_t0,a0,0 - addi_t0,t0,1 - st_t0,a0,0 - la_br &dl_dup_loop - b -:dl_dup_done la_a0 &label_count ld_t0,a0,0 li_t1 H2_LABEL_CAP @@ -1550,23 +1523,25 @@ DEFINE H2_OFF_labels 0042800900000000 st_t0,a0,0 eret -## lookup_label(a0=src, a1=len) -> a0=target_ip. -:lookup_label +## lookup_label_in(a0=src, a1=len, a2=stack_ptr, a3=depth) -> a0=target_ip. +:lookup_label_in enter_0 - la_a2 &ll_src - st_a0,a2,0 - la_a2 &ll_len - st_a1,a2,0 - # dotted? (first byte == '.' AND scope_depth > 0) + la_t1 &ll_src + st_a0,t1,0 + la_t1 &ll_len + st_a1,t1,0 + la_t1 &ll_stack + st_a2,t1,0 + la_t1 &ll_depth + st_a3,t1,0 + # dotted? (first byte == '.' AND depth > 0) lb_t0,a0,0 li_t1 %46 %0 la_br &ll_undotted bne_t0,t1 - la_a3 &scope_depth - ld_t0,a3,0 la_br &ll_undotted - beqz_t0 - addi_t0,t0,neg1 + beqz_a3 + addi_t0,a3,neg1 la_a1 &ll_d st_t0,a1,0 :ll_dot_outer @@ -1574,7 +1549,7 @@ DEFINE H2_OFF_labels 0042800900000000 ld_t0,a0,0 la_br &err_undefined_local bltz_t0 - la_a1 &scope_stack_ptr + la_a1 &ll_stack ld_a1,a1,0 shli_t2,t0,3 add_a1,a1,t2 @@ -1673,7 +1648,8 @@ DEFINE H2_OFF_labels 0042800900000000 ## --- Reference processor --------------------------------------------------- ## process_reference(): cur_sigil already set; scan_pos already past sigil. -## Reads label and (optional) -other, advances ip on pass 1, emits on pass 2. +## Captures label name span (and optional -OTHER), reserves placeholder bytes, +## and appends a Fixup record. Resolution happens later in patch_fixups. :process_reference enter_0 la_br &set_sigil_info @@ -1692,36 +1668,40 @@ DEFINE H2_OFF_labels 0042800900000000 call la_br &err_sigil_no_label bnez_a0 - # llen = read_name(label_buf, MAX_TOKEN) - la_a0 &label_buf_ptr - ld_a0,a0,0 - li_a1 H2_TOKEN_CAP - la_br &read_name + # scan_name -> length in a0, pointer in sn_start (no copy). + la_br &scan_name call - la_a1 &pr_llen + la_a1 &pref_name_len st_a0,a1,0 + la_a0 &sn_start + ld_t0,a0,0 + la_a1 &pref_name + st_t0,a1,0 + # default: other = NULL, other_len = 0 li_t0 %0 %0 - la_a0 &pr_has_other - st_t0,a0,0 - # optional '-' or '>' OTHER + la_a1 &pref_other + st_t0,a1,0 + la_a1 &pref_other_len + st_t0,a1,0 + # optional '-' or '>' separator (tight, no whitespace) la_a0 &scan_pos ld_t0,a0,0 la_a1 &scan_end ld_t1,a1,0 - la_br &pr_after_other + la_br &pref_after_other beq_t0,t1 - la_br &pr_after_other + la_br &pref_after_other blt_t1,t0 lb_a0,t0,0 li_t1 %45 %0 - la_br &pr_consume_sep + la_br &pref_consume_sep beq_a0,t1 li_t1 %62 %0 - la_br &pr_consume_sep + la_br &pref_consume_sep beq_a0,t1 - la_br &pr_after_other + la_br &pref_after_other b -:pr_consume_sep +:pref_consume_sep la_a0 &scan_pos ld_t0,a0,0 addi_t0,t0,1 @@ -1737,96 +1717,155 @@ DEFINE H2_OFF_labels 0042800900000000 call la_br &err_minus_no_label bnez_a0 - la_a0 &other_buf_ptr - ld_a0,a0,0 - li_a1 H2_TOKEN_CAP - la_br &read_name + la_br &scan_name call - la_a1 &pr_olen + la_a1 &pref_other_len st_a0,a1,0 - li_t0 %1 %0 - la_a0 &pr_has_other - st_t0,a0,0 -:pr_after_other - la_a0 &pass + la_a0 &sn_start ld_t0,a0,0 - li_t1 %1 %0 - la_br &pr_pass2 - bne_t0,t1 - # ip += pr_width - la_a0 &ip - ld_t1,a0,0 - la_a1 &pr_width - ld_t2,a1,0 - add_t1,t1,t2 - st_t1,a0,0 - eret -:pr_pass2 - la_a0 &label_buf_ptr + la_a1 &pref_other + st_t0,a1,0 +:pref_after_other + # Reserve placeholder bytes; emit_zeros returns starting offset. + la_a0 &pr_width ld_a0,a0,0 - la_a1 &pr_llen - ld_a1,a1,0 - la_br &lookup_label + la_br &emit_zeros call - la_a1 &pr_t_label + la_a1 &rf_out_off st_a0,a1,0 - la_a0 &pr_has_other + # ip_after = ip (already advanced by emit_zeros). + la_a0 &ip ld_t0,a0,0 - la_br &pr_no_other - beqz_t0 - la_a0 &other_buf_ptr - ld_a0,a0,0 - la_a1 &pr_olen - ld_a1,a1,0 - la_br &lookup_label + la_a1 &rf_ip_after + st_t0,a1,0 + la_br &record_fixup call - la_a1 &pr_t_label - ld_a1,a1,0 - sub_a0,a1,a0 - la_a1 &pr_value - st_a0,a1,0 - la_br &pr_emit - b -:pr_no_other - la_a0 &pr_is_rel + eret + +## record_fixup(): pref_name/pref_name_len/pref_other/pref_other_len already set +## by caller. rf_out_off and rf_ip_after also set. Snapshots cur_sigil/cur_path/ +## cur_line and the current scope stack (if depth > 0). +:record_fixup + enter_0 + la_a0 &fixup_count ld_t0,a0,0 - la_br &pr_abs - beqz_t0 - # rel: value = t_label - (ip + width) - la_a0 &ip - ld_a1,a0,0 - la_a0 &pr_width + li_t1 H2_FIXUP_CAP + la_br &err_too_many_fixups + beq_t0,t1 + la_br &err_too_many_fixups + blt_t1,t0 + # fp = fixups_ptr + count * 96 + li_t1 %96 %0 + mul_a1,t0,t1 + la_a2 &fixups_ptr + ld_a2,a2,0 + add_a1,a1,a2 + la_a3 &rf_fp + st_a1,a3,0 + # offset 0: out_off + la_a0 &rf_out_off ld_t0,a0,0 - add_a1,a1,t0 - la_a0 &pr_t_label - ld_a0,a0,0 - sub_a0,a0,a1 - la_a1 &pr_value - st_a0,a1,0 - la_br &pr_emit - b -:pr_abs - # value = t_label + base_address - la_a0 &pr_t_label - ld_a1,a0,0 - la_a0 &base_address + st_t0,a1,0 + # offset 8: ip_at_ref + la_a0 &rf_ip_after + ld_t0,a0,0 + st_t0,a1,8 + # offset 16: name + la_a0 &pref_name + ld_t0,a0,0 + st_t0,a1,16 + # offset 24: other (0 if none) + la_a0 &pref_other + ld_t0,a0,0 + st_t0,a1,24 + # offset 32: src_path = cur_path + la_a0 &cur_path + ld_t0,a0,0 + st_t0,a1,32 + # offset 40: name_len + la_a0 &pref_name_len + ld_t0,a0,0 + st_t0,a1,40 + # offset 48: other_len + la_a0 &pref_other_len + ld_t0,a0,0 + st_t0,a1,48 + # offset 56: scope_hist_off (filled below if depth > 0) + li_t0 %0 %0 + st_t0,a1,56 + # offset 64: scope_depth (snapshot) + la_a0 &scope_depth + ld_t0,a0,0 + st_t0,a1,64 + # offset 72: src_line = cur_line (offset > 64: indirect via a3) + la_a0 &cur_line + ld_t0,a0,0 + li_t1 %72 %0 + add_a3,a1,t1 + st_t0,a3,0 + # offset 80: sigil + la_a0 &cur_sigil + ld_t0,a0,0 + li_t1 %80 %0 + add_a3,a1,t1 + st_t0,a3,0 + # If depth > 0, snapshot scope_stack into scope_history. + la_a0 &scope_depth + ld_t0,a0,0 + la_br &rf_no_scope + beqz_t0 + la_a1 &scope_history_used + ld_t1,a1,0 + add_t2,t1,t0 + li_a3 H2_SCOPE_HISTORY_CAP + la_br &err_scope_history_overflow + blt_a3,t2 + # Set scope_hist_off (offset 56) on the fixup. + la_a0 &rf_fp ld_a0,a0,0 - add_a1,a1,a0 - la_a3 &pr_value - st_a1,a3,0 -:pr_emit - la_a1 &pr_width + st_t1,a0,56 + li_t2 %0 %0 + la_a0 &rf_i + st_t2,a0,0 +:rf_copy_loop + la_a0 &rf_i + ld_t0,a0,0 + la_a1 &scope_depth + ld_t1,a1,0 + la_br &rf_copy_done + beq_t0,t1 + # src = scope_stack_ptr + i*8 + la_a1 &scope_stack_ptr ld_a1,a1,0 - la_a2 &pr_lo + shli_t2,t0,3 + add_a1,a1,t2 + ld_a3,a1,0 + # dst = scope_history_ptr + (scope_history_used + i)*8 + la_a2 &scope_history_used ld_a2,a2,0 - la_a3 &pr_hi - ld_a3,a3,0 - la_a0 &pr_range_check + add_a2,a2,t0 + shli_a2,a2,3 + la_a1 &scope_history_ptr + ld_a1,a1,0 + add_a1,a1,a2 + st_a3,a1,0 + addi_t0,t0,1 + la_a0 &rf_i + st_t0,a0,0 + la_br &rf_copy_loop + b +:rf_copy_done + la_a1 &scope_history_used + ld_t0,a1,0 + la_a2 &scope_depth + ld_t1,a2,0 + add_t0,t0,t1 + st_t0,a1,0 +:rf_no_scope + la_a0 &fixup_count ld_t0,a0,0 - la_a0 &pr_value - ld_a0,a0,0 - la_br &emit_value - call + addi_t0,t0,1 + st_t0,a0,0 eret ## set_sigil_info(): reads cur_sigil; populates pr_width / pr_is_rel / @@ -2067,44 +2106,47 @@ DEFINE H2_OFF_labels 0042800900000000 la_a1 &da_pad st_a3,a1,0 :da_emit + la_a0 &da_pad + ld_t0,a0,0 + la_br &da_emit_done + beqz_t0 + # No-pattern fast path: emit_zeros(pad). + la_a1 &da_has_pat + ld_t1,a1,0 + la_br &da_emit_pattern + bnez_t1 + la_a0 &da_pad + ld_a0,a0,0 + la_br &emit_zeros + call + la_br &da_emit_done + b +:da_emit_pattern li_t0 %0 %0 la_a0 &da_i st_t0,a0,0 -:da_emit_loop +:da_pattern_loop la_a0 &da_i ld_t0,a0,0 la_a1 &da_pad ld_t1,a1,0 la_br &da_emit_done beq_t0,t1 - la_a0 &da_has_pat - ld_t1,a0,0 - la_br &da_emit_zero - beqz_t1 # b = pat[i % patlen] - la_a0 &da_i - ld_a0,a0,0 la_a1 &da_patlen ld_a1,a1,0 - rem_a2,a0,a1 + rem_a2,t0,a1 la_a0 &pat_buf_ptr ld_a0,a0,0 add_a0,a0,a2 lb_a0,a0,0 la_br &emit_byte call - la_br &da_emit_inc - b -:da_emit_zero - li_a0 %0 %0 - la_br &emit_byte - call -:da_emit_inc la_a0 &da_i ld_t0,a0,0 addi_t0,t0,1 st_t0,a0,0 - la_br &da_emit_loop + la_br &da_pattern_loop b :da_emit_done eret @@ -2126,27 +2168,15 @@ DEFINE H2_OFF_labels 0042800900000000 ld_a0,a0,0 la_br &parse_one_byte call - li_t0 %0 %0 - la_a0 &df_i - st_t0,a0,0 -:df_loop - la_a0 &df_i - ld_t0,a0,0 - la_a1 &df_n - ld_t1,a1,0 - la_br &df_done - beq_t0,t1 - la_a0 &df_byte_ptr + la_a0 &df_n ld_a0,a0,0 - lb_a0,a0,0 - la_br &emit_byte + la_br &df_done + beqz_a0 + la_a1 &df_byte_ptr + ld_a1,a1,0 + lb_a1,a1,0 + la_br &emit_fill call - la_a0 &df_i - ld_t0,a0,0 - addi_t0,t0,1 - st_t0,a0,0 - la_br &df_loop - b :df_done eret @@ -2223,13 +2253,8 @@ DEFINE H2_OFF_labels 0042800900000000 ## --- Emit ------------------------------------------------------------------ -## emit_byte(a0=byte): pass 1 only bumps ip; pass 2 also writes to output_buf. +## emit_byte(a0=byte): write one byte to output_buf and bump ip. :emit_byte - la_a1 &pass - ld_t0,a1,0 - li_t1 %2 %0 - la_br &eb_after_write - bne_t0,t1 la_a1 &output_used ld_t0,a1,0 li_t1 H2_OUTPUT_CAP @@ -2243,31 +2268,109 @@ DEFINE H2_OFF_labels 0042800900000000 sb_a0,a2,0 addi_t0,t0,1 st_t0,a1,0 -:eb_after_write la_a0 &ip ld_t0,a0,0 addi_t0,t0,1 st_t0,a0,0 ret -## emit_value(a0=value, a1=width, a2=lo, a3=hi, t0=range_check). Range-checks, -## packs little-endian into ev_bytes[0..width-1], emits in LE/BE order. -:emit_value +## emit_zeros(a0=n) -> a0 = starting offset (output_used before the call). +## Writes n zero bytes to output_buf and advances output_used + ip by n. +:emit_zeros enter_0 - la_t1 &ev_value + la_a3 &emz_n + st_a0,a3,0 + # Capacity check: output_used + n must fit. + la_a1 &output_used + ld_t0,a1,0 + add_t1,t0,a0 + li_t2 H2_OUTPUT_CAP + la_br &err_output_overflow + blt_t2,t1 + la_a2 &emz_off + st_t0,a2,0 + la_a2 &output_buf_ptr + ld_a2,a2,0 + add_a2,a2,t0 + li_t1 %0 %0 +:emz_loop + la_a3 &emz_n + ld_t2,a3,0 + la_br &emz_done + beq_t1,t2 + add_a3,a2,t1 + li_t0 %0 %0 + sb_t0,a3,0 + addi_t1,t1,1 + la_br &emz_loop + b +:emz_done + la_a3 &emz_n + ld_t0,a3,0 + la_a1 &output_used + ld_t1,a1,0 + add_t1,t1,t0 + st_t1,a1,0 + la_a1 &ip + ld_t1,a1,0 + add_t1,t1,t0 + st_t1,a1,0 + la_a0 &emz_off + ld_a0,a0,0 + eret + +## emit_fill(a0=n, a1=byte): write n copies of byte; advances output_used + ip. +:emit_fill + enter_0 + la_a3 &emf_n + st_a0,a3,0 + la_a3 &emf_b + st_a1,a3,0 + la_a2 &output_used + ld_t0,a2,0 + add_t1,t0,a0 + li_t2 H2_OUTPUT_CAP + la_br &err_output_overflow + blt_t2,t1 + la_a2 &output_buf_ptr + ld_a2,a2,0 + add_a2,a2,t0 + li_t1 %0 %0 +:emf_loop + la_a3 &emf_n + ld_t2,a3,0 + la_br &emf_done + beq_t1,t2 + la_a3 &emf_b + ld_t0,a3,0 + add_a3,a2,t1 + sb_t0,a3,0 + addi_t1,t1,1 + la_br &emf_loop + b +:emf_done + la_a3 &emf_n + ld_t0,a3,0 + la_a1 &output_used + ld_t1,a1,0 + add_t1,t1,t0 + st_t1,a1,0 + la_a1 &ip + ld_t1,a1,0 + add_t1,t1,t0 + st_t1,a1,0 + eret + +## write_value(a0=out_off): patch a value into output_buf at out_off. +## Caller pre-populates ev_value/ev_width/ev_lo/ev_hi/ev_range_check. +:write_value + enter_0 + la_t1 &ev_out_off st_a0,t1,0 - la_t1 &ev_width - st_a1,t1,0 - la_t1 &ev_lo - st_a2,t1,0 - la_t1 &ev_hi - st_a3,t1,0 - la_t1 &ev_range_check - st_t0,t1,0 la_a0 &ev_range_check ld_t0,a0,0 - la_br &ev_no_range + la_br &wv_no_range beqz_t0 la_a0 &ev_value ld_a0,a0,0 @@ -2281,7 +2384,7 @@ DEFINE H2_OFF_labels 0042800900000000 ld_a1,a1,0 la_br &err_ref_out_of_range blt_a1,a0 -:ev_no_range +:wv_no_range la_a0 &ev_value ld_a0,a0,0 la_a1 &ev_pack_v @@ -2289,12 +2392,12 @@ DEFINE H2_OFF_labels 0042800900000000 li_t0 %0 %0 la_a0 &ev_i st_t0,a0,0 -:ev_pack_loop +:wv_pack_loop la_a0 &ev_i ld_t0,a0,0 la_a1 &ev_width ld_t1,a1,0 - la_br &ev_emit_dispatch + la_br &wv_emit_dispatch beq_t0,t1 la_a1 &ev_pack_v ld_a3,a1,0 @@ -2310,59 +2413,227 @@ DEFINE H2_OFF_labels 0042800900000000 addi_t0,t0,1 la_a0 &ev_i st_t0,a0,0 - la_br &ev_pack_loop + la_br &wv_pack_loop b -:ev_emit_dispatch +:wv_emit_dispatch la_a0 &big_endian ld_t0,a0,0 - la_br &ev_emit_be + la_br &wv_emit_be bnez_t0 li_t0 %0 %0 la_a0 &ev_i st_t0,a0,0 -:ev_emit_le_loop +:wv_emit_le_loop la_a0 &ev_i ld_t0,a0,0 la_a1 &ev_width ld_t1,a1,0 - la_br &ev_done + la_br &wv_done beq_t0,t1 la_a2 &ev_bytes_ptr ld_a2,a2,0 add_a2,a2,t0 lb_a0,a2,0 - la_br &emit_byte - call - la_a0 &ev_i - ld_t0,a0,0 + la_a3 &output_buf_ptr + ld_a3,a3,0 + la_a2 &ev_out_off + ld_a2,a2,0 + add_a3,a3,a2 + add_a3,a3,t0 + sb_a0,a3,0 addi_t0,t0,1 + la_a0 &ev_i st_t0,a0,0 - la_br &ev_emit_le_loop + la_br &wv_emit_le_loop b -:ev_emit_be - la_a0 &ev_width - ld_t0,a0,0 - addi_t0,t0,neg1 - la_a1 &ev_i - st_t0,a1,0 -:ev_emit_be_loop +:wv_emit_be + li_t0 %0 %0 + la_a0 &ev_i + st_t0,a0,0 +:wv_emit_be_loop la_a0 &ev_i ld_t0,a0,0 - la_br &ev_done - bltz_t0 + la_a1 &ev_width + ld_t1,a1,0 + la_br &wv_done + beq_t0,t1 + # src_idx = (width - 1) - i + addi_t1,t1,neg1 + sub_t2,t1,t0 la_a2 &ev_bytes_ptr ld_a2,a2,0 - add_a2,a2,t0 + add_a2,a2,t2 lb_a0,a2,0 - la_br &emit_byte - call + la_a3 &output_buf_ptr + ld_a3,a3,0 + la_a2 &ev_out_off + ld_a2,a2,0 + add_a3,a3,a2 + add_a3,a3,t0 + sb_a0,a3,0 + addi_t0,t0,1 la_a0 &ev_i + st_t0,a0,0 + la_br &wv_emit_be_loop + b +:wv_done + eret + +## --- Fixup resolution ------------------------------------------------------ +## patch_fixups(): walk fixups[0..fixup_count), resolve labels, write values +## into output_buf. Per-fixup error reports use the snapshotted src_path / +## src_line via the cur_path / cur_line globals. +:patch_fixups + enter_0 + li_t0 %0 %0 + la_a0 &pf_i + st_t0,a0,0 +:pf_loop + la_a0 &pf_i ld_t0,a0,0 - addi_t0,t0,neg1 + la_a1 &fixup_count + ld_t1,a1,0 + la_br &pf_done + beq_t0,t1 + # fp = fixups + i*96 + li_t1 %96 %0 + mul_a1,t0,t1 + la_a2 &fixups_ptr + ld_a2,a2,0 + add_a1,a1,a2 + la_a3 &pf_fp + st_a1,a3,0 + # cur_path = fp[32]; cur_line = fp[72]; cur_sigil = fp[80] + ld_t0,a1,32 + la_a2 &cur_path + st_t0,a2,0 + li_t1 %72 %0 + add_a3,a1,t1 + ld_t0,a3,0 + la_a2 &cur_line + st_t0,a2,0 + li_t1 %80 %0 + add_a3,a1,t1 + ld_t0,a3,0 + la_a2 &cur_sigil + st_t0,a2,0 + # pr_width / pr_is_rel / pr_lo / pr_hi / pr_range_check + la_br &set_sigil_info + call + # Determine stack pointer for the lookup (NULL if depth == 0). + la_a0 &pf_fp + ld_a1,a0,0 + ld_t0,a1,64 + la_a2 &pf_depth + st_t0,a2,0 + li_t1 %0 %0 + la_a2 &pf_stack + st_t1,a2,0 + la_br &pf_lookup_label + beqz_t0 + # stack = scope_history_ptr + scope_hist_off * 8 + ld_t1,a1,56 + shli_t1,t1,3 + la_a2 &scope_history_ptr + ld_a2,a2,0 + add_a2,a2,t1 + la_a3 &pf_stack + st_a2,a3,0 +:pf_lookup_label + la_a0 &pf_fp + ld_a1,a0,0 + ld_a0,a1,16 + ld_t0,a1,40 + mov_a1,t0 + la_a2 &pf_stack + ld_a2,a2,0 + la_a3 &pf_depth + ld_a3,a3,0 + la_br &lookup_label_in + call + la_a1 &pf_t_label + st_a0,a1,0 + # If other != 0: value = t_label - t_other; emit. + la_a0 &pf_fp + ld_a1,a0,0 + ld_t0,a1,24 + la_br &pf_no_other + beqz_t0 + mov_a0,t0 + ld_t0,a1,48 + mov_a1,t0 + la_a2 &pf_stack + ld_a2,a2,0 + la_a3 &pf_depth + ld_a3,a3,0 + la_br &lookup_label_in + call + la_a1 &pf_t_label + ld_a1,a1,0 + sub_a0,a1,a0 + la_a1 &pf_value + st_a0,a1,0 + la_br &pf_emit + b +:pf_no_other + la_a0 &pr_is_rel + ld_t0,a0,0 + la_br &pf_abs + beqz_t0 + # rel: value = t_label - ip_at_ref + la_a0 &pf_fp + ld_a1,a0,0 + ld_t1,a1,8 + la_a0 &pf_t_label + ld_a0,a0,0 + sub_a0,a0,t1 + la_a1 &pf_value + st_a0,a1,0 + la_br &pf_emit + b +:pf_abs + la_a0 &pf_t_label + ld_a1,a0,0 + la_a0 &base_address + ld_a0,a0,0 + add_a1,a1,a0 + la_a3 &pf_value + st_a1,a3,0 +:pf_emit + # Populate ev_* state, then write_value(out_off). + la_a0 &pf_value + ld_t0,a0,0 + la_a1 &ev_value + st_t0,a1,0 + la_a0 &pr_width + ld_t0,a0,0 + la_a1 &ev_width + st_t0,a1,0 + la_a0 &pr_lo + ld_t0,a0,0 + la_a1 &ev_lo + st_t0,a1,0 + la_a0 &pr_hi + ld_t0,a0,0 + la_a1 &ev_hi + st_t0,a1,0 + la_a0 &pr_range_check + ld_t0,a0,0 + la_a1 &ev_range_check + st_t0,a1,0 + la_a0 &pf_fp + ld_a1,a0,0 + ld_a0,a1,0 + la_br &write_value + call + # i++ + la_a0 &pf_i + ld_t0,a0,0 + addi_t0,t0,1 st_t0,a0,0 - la_br &ev_emit_be_loop + la_br &pf_loop b -:ev_done +:pf_done eret ## --- Output writer --------------------------------------------------------- @@ -2910,6 +3181,14 @@ DEFINE H2_OFF_labels 0042800900000000 la_a0 &msg_bad_long la_br &fatal b +:err_too_many_fixups + la_a0 &msg_too_many_fixups + la_br &fatal + b +:err_scope_history_overflow + la_a0 &msg_scope_history_overflow + la_br &fatal + b :err_ptrsize_bad la_a0 &msg_ptrsize_bad la_br &fatal @@ -2979,6 +3258,8 @@ DEFINE H2_OFF_labels 0042800900000000 :msg_bad_long "invalid integer argument" '00' :msg_ptrsize_bad ".ptrsize: N must be 4 or 8" '00' :msg_ptrsize_conflict ".ptrsize conflicts with already-used width" '00' +:msg_too_many_fixups "too many references" '00000000' +:msg_scope_history_overflow "scope history overflow" '00' ## BSS pointer-slot init table. :bss_init_tbl @@ -2994,6 +3275,8 @@ DEFINE H2_OFF_labels 0042800900000000 &output_buf_ptr ZERO4 H2_OFF_output_buf &text_buf_ptr ZERO4 H2_OFF_text_buf &labels_ptr ZERO4 H2_OFF_labels +&fixups_ptr ZERO4 H2_OFF_fixups +&scope_history_ptr ZERO4 H2_OFF_scope_history :bss_init_tbl_end ## --- BSS scalars ---------------------------------------------------------- @@ -3049,6 +3332,10 @@ ZERO8 ZERO8 :label_count ZERO8 +:fixup_count +ZERO8 +:scope_history_used +ZERO8 :scope_depth ZERO8 :scope_seq @@ -3068,6 +3355,54 @@ ZERO8 :rn_n ZERO8 +## scan_name (returns ptr+len; pointer goes here, length in a0). +:sn_start +ZERO8 + +## record_fixup scratch +:rf_name +ZERO8 +:rf_name_len +ZERO8 +:rf_other +ZERO8 +:rf_other_len +ZERO8 +:rf_out_off +ZERO8 +:rf_ip_after +ZERO8 +:rf_idx +ZERO8 +:rf_fp +ZERO8 +:rf_i +ZERO8 + +## patch_fixups scratch +:pf_i +ZERO8 +:pf_fp +ZERO8 +:pf_t_label +ZERO8 +:pf_value +ZERO8 +:pf_stack +ZERO8 +:pf_depth +ZERO8 + +## process_reference scratch (single-pass record path) +:pref_name +ZERO8 +:pref_name_len +ZERO8 +:pref_other +ZERO8 +:pref_other_len +ZERO8 + ## decimal read :rd_val ZERO8 @@ -3126,11 +3461,15 @@ ZERO8 :dl_name_off ZERO8 -## lookup_label +## lookup_label_in :ll_src ZERO8 :ll_len ZERO8 +:ll_stack +ZERO8 +:ll_depth +ZERO8 :ll_d ZERO8 :ll_sid @@ -3164,7 +3503,7 @@ ZERO8 :pr_value ZERO8 -## emit_value +## emit_value / write_value :ev_value ZERO8 :ev_width @@ -3179,6 +3518,18 @@ ZERO8 ZERO8 :ev_i ZERO8 +:ev_out_off +ZERO8 + +## emit_zeros / emit_fill scratch +:emz_n +ZERO8 +:emz_off +ZERO8 +:emf_n +ZERO8 +:emf_b +ZERO8 ## directive scratch :da_n @@ -3257,5 +3608,9 @@ ZERO8 ZERO8 :labels_ptr ZERO8 +:fixups_ptr +ZERO8 +:scope_history_ptr +ZERO8 :ELF_end diff --git a/hex2pp/hex2pp.c b/hex2pp/hex2pp.c @@ -3,9 +3,10 @@ * * See docs/HEX2pp.md for the spec. Brief summary: * - * Inputs are concatenated, scanned in two passes. Pass 1 records label - * definitions while advancing a position counter (ip). Pass 2 emits - * bytes, resolving label references against the table built in pass 1. + * Input is scanned once. Label definitions are recorded into a table + * on the fly; label references emit zero placeholders and append a + * fixup record. After the scan, fixups are resolved against the + * completed label table and patched into the output buffer. * * Active syntax: * digits in current byte mode -> raw bytes (HEX or BINARY) @@ -27,12 +28,14 @@ #include <string.h> #include <sys/stat.h> -#define MAX_INPUT_BYTES (16 * 1024 * 1024) -#define MAX_OUTPUT_BYTES (128 * 1024 * 1024) -#define MAX_LABELS (1 << 20) -#define MAX_TEXT (8 * 1024 * 1024) -#define MAX_TOKEN 4096 -#define MAX_SCOPE_DEPTH 32 +#define MAX_INPUT_BYTES (16 * 1024 * 1024) +#define MAX_OUTPUT_BYTES (128 * 1024 * 1024) +#define MAX_LABELS (1 << 20) +#define MAX_FIXUPS (1 << 20) +#define MAX_TEXT (8 * 1024 * 1024) +#define MAX_TOKEN 4096 +#define MAX_SCOPE_DEPTH 32 +#define MAX_SCOPE_HISTORY (1 << 22) enum { HEX_MODE, BINARY_MODE }; @@ -49,6 +52,20 @@ struct Label { int scope_id; /* 0 = global */ }; +struct Fixup { + long long out_off; /* offset in output_buf to patch */ + long long ip_at_ref; /* ip immediately after the reference's bytes */ + const char *name; /* points into input buffer */ + const char *other; /* NULL if no -OTHER */ + const char *src_path; + int name_len; + int other_len; + int scope_hist_off; /* index into scope_history (depth>0 only) */ + int scope_depth; + int src_line; + int sigil; +}; + static struct InFile input_file; static char text_buf[MAX_TEXT]; @@ -57,6 +74,12 @@ static int text_used; static struct Label labels[MAX_LABELS]; static int label_count; +static struct Fixup fixups[MAX_FIXUPS]; +static int fixup_count; + +static int scope_history[MAX_SCOPE_HISTORY]; +static int scope_history_used; + static unsigned char output_buf[MAX_OUTPUT_BYTES]; static long long output_used; @@ -73,7 +96,6 @@ static int scope_stack[MAX_SCOPE_DEPTH]; static int scope_depth; static int scope_seq; -static int pass; /* 1 or 2 */ static const char *cur_path; static int cur_line; @@ -116,13 +138,6 @@ static int name_eq(const struct Label *L, const char *s, int len) static void define_label(const char *s, int len, int scope_id) { - int i; - /* Duplicate-definition check (within the same scope namespace). */ - for (i = 0; i < label_count; i++) { - if (labels[i].scope_id == scope_id && name_eq(&labels[i], s, len)) { - die("duplicate label '%.*s'", len, s); - } - } if (label_count >= MAX_LABELS) { die("too many labels"); } @@ -133,17 +148,17 @@ static void define_label(const char *s, int len, int scope_id) label_count++; } -static long long lookup_label(const char *s, int len) +static long long lookup_label_in(const char *s, int len, const int *stack, int depth) { int i; int d; - int dotted = (len > 0 && s[0] == '.' && scope_depth > 0); + int dotted = (len > 0 && s[0] == '.' && depth > 0); if (dotted) { /* Inside a scope, walk the scope stack innermost-out. A dotted * name resolves to the nearest enclosing definition, so an inner * scope can shadow an outer one with the same local name. */ - for (d = scope_depth - 1; d >= 0; d--) { - int sid = scope_stack[d]; + for (d = depth - 1; d >= 0; d--) { + int sid = stack[d]; for (i = 0; i < label_count; i++) { if (labels[i].scope_id == sid && name_eq(&labels[i], s, len)) { return labels[i].target_ip; @@ -157,8 +172,6 @@ static long long lookup_label(const char *s, int len) return labels[i].target_ip; } } - /* Also accept dotted labels referenced without sigil context? - * No -- non-dotted lookup only sees global. */ die("undefined label '%.*s'", len, s); } return 0; /* unreachable */ @@ -168,10 +181,6 @@ static long long lookup_label(const char *s, int len) static void emit_byte(unsigned b) { - if (pass != 2) { - ip++; - return; - } if (output_used >= MAX_OUTPUT_BYTES) { die("output overflow"); } @@ -179,7 +188,29 @@ static void emit_byte(unsigned b) ip++; } -static void emit_value(long long v, int width, long long lo, long long hi, int range_check) +static long long emit_zeros(long long n) +{ + long long off = output_used; + if (output_used + n > MAX_OUTPUT_BYTES) { + die("output overflow"); + } + memset(output_buf + output_used, 0, (size_t)n); + output_used += n; + ip += n; + return off; +} + +static void emit_fill(long long n, unsigned char b) +{ + if (output_used + n > MAX_OUTPUT_BYTES) { + die("output overflow"); + } + memset(output_buf + output_used, b, (size_t)n); + output_used += n; + ip += n; +} + +static void write_value(long long out_off, long long v, int width, long long lo, long long hi, int range_check) { int i; unsigned char bytes[8]; @@ -191,18 +222,16 @@ static void emit_value(long long v, int width, long long lo, long long hi, int r die("internal: bad reference width %d", width); } - /* Pack as little-endian into bytes[0..width-1]. */ for (i = 0; i < width; i++) { bytes[i] = (unsigned char)((unsigned long long)v >> (8 * i)) & 0xff; } if (big_endian) { - /* Reverse for big-endian output. */ for (i = 0; i < width; i++) { - emit_byte(bytes[width - 1 - i]); + output_buf[out_off + i] = bytes[width - 1 - i]; } } else { for (i = 0; i < width; i++) { - emit_byte(bytes[i]); + output_buf[out_off + i] = bytes[i]; } } } @@ -347,18 +376,18 @@ static int is_name_terminator(int c) return 0; } -static int read_name(struct Scanner *s, char *out, int max) +/* Scan a label name; return pointer span into the input buffer (no copy). */ +static int scan_name(struct Scanner *s, const char **out_start) { - int n = 0; + int start = s->pos; while (s->pos < s->len) { int c = (unsigned char)s->buf[s->pos]; if (is_name_terminator(c)) break; - if (n >= max) die("name too long"); - out[n++] = (char)c; s->pos++; } - if (n == 0) die("expected label name"); - return n; + if (s->pos == start) die("expected label name"); + *out_start = s->buf + start; + return s->pos - start; } /* Decimal integer (for directive arity arguments). */ @@ -403,14 +432,44 @@ static struct SigilInfo sigil_info(int c) return si; } +static void record_fixup(const char *name, int name_len, + const char *other, int other_len, + int sigil, long long out_off, long long ip_after) +{ + struct Fixup *f; + if (fixup_count >= MAX_FIXUPS) die("too many references"); + f = &fixups[fixup_count++]; + f->out_off = out_off; + f->ip_at_ref = ip_after; + f->name = name; + f->name_len = name_len; + f->other = other; + f->other_len = other_len; + f->sigil = sigil; + f->src_path = cur_path; + f->src_line = cur_line; + f->scope_depth = scope_depth; + if (scope_depth > 0) { + if (scope_history_used + scope_depth > MAX_SCOPE_HISTORY) { + die("scope history overflow"); + } + f->scope_hist_off = scope_history_used; + memcpy(&scope_history[scope_history_used], scope_stack, + (size_t)scope_depth * sizeof(int)); + scope_history_used += scope_depth; + } else { + f->scope_hist_off = 0; + } +} + static void process_reference(struct Scanner *s, int sigil) { - char label[MAX_TOKEN]; - char other[MAX_TOKEN]; - int llen, olen = 0; - int has_other = 0; + const char *name_start; + const char *other_start = NULL; + int name_len; + int other_len = 0; struct SigilInfo si = sigil_info(sigil); - long long value = 0; + long long out_off; if (sigil == '&' || sigil == '%') ptrsize_used = 1; @@ -418,7 +477,7 @@ static void process_reference(struct Scanner *s, int sigil) if (s->pos >= s->len || is_name_terminator((unsigned char)s->buf[s->pos])) { die("sigil '%c' not followed by label name", sigil); } - llen = read_name(s, label, sizeof(label)); + name_len = scan_name(s, &name_start); /* Optional '-' OTHER or '>' OTHER (tight, no whitespace). * '>' is a synonym for '-', accepted for hex2 compatibility. */ @@ -427,30 +486,12 @@ static void process_reference(struct Scanner *s, int sigil) if (s->pos >= s->len || is_name_terminator((unsigned char)s->buf[s->pos])) { die("'-' must be followed by label name"); } - olen = read_name(s, other, sizeof(other)); - has_other = 1; - } - - if (pass == 1) { - ip += si.width; - return; + other_len = scan_name(s, &other_start); } - /* Pass 2: compute value. */ - { - long long t_label = lookup_label(label, llen); - if (has_other) { - long long t_other = lookup_label(other, olen); - value = t_label - t_other; - } else if (si.is_rel) { - /* base = ip immediately after the reference's bytes are accounted for */ - value = t_label - (ip + si.width); - } else { - /* abs: target's absolute address (which includes Base_Address) */ - value = t_label + base_address; - } - emit_value(value, si.width, si.lo, si.hi, si.range_check); - } + out_off = emit_zeros(si.width); + record_fixup(name_start, name_len, other_start, other_len, + sigil, out_off, ip); } /* --- directives ------------------------------------------------------- */ @@ -510,10 +551,6 @@ static void do_align(struct Scanner *s) if (s->pos < s->len) { c = (unsigned char)s->buf[s->pos]; if (is_byte_digit(c)) { - int dummy; - unsigned char dummyb; - (void)dummyb; - (void)dummy; parse_one_byte_literal(s, NULL, 1, patbuf, (int)sizeof(patbuf), &patlen); has_pattern = 1; } @@ -521,25 +558,30 @@ static void do_align(struct Scanner *s) target = (ip + N - 1) & ~(N - 1); pad = target - ip; + if (pad <= 0) return; + if (output_used + pad > MAX_OUTPUT_BYTES) die("output overflow"); if (!has_pattern) { - for (i = 0; i < pad; i++) emit_byte(0); + memset(output_buf + output_used, 0, (size_t)pad); } else { - for (i = 0; i < pad; i++) emit_byte(patbuf[i % patlen]); + for (i = 0; i < pad; i++) { + output_buf[output_used + i] = patbuf[i % patlen]; + } } + output_used += pad; + ip += pad; } static void do_fill(struct Scanner *s) { long long N; unsigned char b; - long long i; skip_inline_ws(s); N = read_decimal(s); if (N < 0) die(".fill: N must be non-negative (got %lld)", N); skip_inline_ws(s); parse_one_byte_literal(s, &b, 0, NULL, 0, NULL); - for (i = 0; i < N; i++) emit_byte(b); + if (N > 0) emit_fill(N, b); } static void do_ptrsize(struct Scanner *s) @@ -584,17 +626,17 @@ static void process_file(struct InFile *f) c = (unsigned char)s.buf[s.pos]; if (c == ':') { - char name[MAX_TOKEN]; + const char *name; int n; int dotted; int scope; s.pos++; - n = read_name(&s, name, sizeof(name)); + n = scan_name(&s, &name); /* A dot-prefixed name is scope-local only inside a .scope; * outside, it is an ordinary global name. */ dotted = (n > 0 && name[0] == '.' && scope_depth > 0); scope = dotted ? scope_stack[scope_depth - 1] : 0; - if (pass == 1) define_label(name, n, scope); + define_label(name, n, scope); continue; } @@ -627,6 +669,37 @@ static void process_file(struct InFile *f) } } +/* --- fixup resolution ------------------------------------------------- */ + +static void patch_fixups(void) +{ + int i; + for (i = 0; i < fixup_count; i++) { + struct Fixup *f = &fixups[i]; + struct SigilInfo si = sigil_info(f->sigil); + const int *stack = (f->scope_depth > 0) + ? &scope_history[f->scope_hist_off] + : NULL; + long long t_label; + long long value; + + cur_path = f->src_path; + cur_line = f->src_line; + + t_label = lookup_label_in(f->name, f->name_len, stack, f->scope_depth); + if (f->other != NULL) { + long long t_other = lookup_label_in(f->other, f->other_len, + stack, f->scope_depth); + value = t_label - t_other; + } else if (si.is_rel) { + value = t_label - f->ip_at_ref; + } else { + value = t_label + base_address; + } + write_value(f->out_off, value, si.width, si.lo, si.hi, si.range_check); + } +} + /* --- argument parsing & top-level ------------------------------------- */ static long long parse_long(const char *s, const char *what) @@ -717,18 +790,6 @@ int main(int argc, char **argv) } load_input(in_path); - /* Pass 1: collect labels. */ - pass = 1; - ip = 0; - scope_depth = 0; - scope_seq = 0; - ptrsize = 4; - ptrsize_used = 0; - process_file(&input_file); - if (scope_depth != 0) die(".scope not closed at end of input"); - - /* Pass 2: emit. */ - pass = 2; ip = 0; output_used = 0; scope_depth = 0; @@ -737,6 +798,7 @@ int main(int argc, char **argv) ptrsize_used = 0; process_file(&input_file); if (scope_depth != 0) die(".scope not closed at end of input"); + patch_fixups(); /* Write output. */ {