boot2

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

commit d60e3e32916334532d527101871f8f429019786a
parent c612ee4b1ae6980f51ef14c0a4d990c6580c59aa
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed,  6 May 2026 12:55:41 -0700

AT.2: native PT_NOTE for PVH boot; retire elf-pvh-note shim

Three tcc patches replace the post-link elf-pvh-note.c host tool that
rewrote kernel.elf so QEMU's PVH `-kernel` path on amd64 could find
the Xen 18 entry note:

- note-section-sht-note: find_section() creates `.note*` sections as
  SHT_NOTE.
- pt-note-phdr: elf_output_file() bumps phnum and emits one PT_NOTE
  covering the SHT_NOTE+SHF_ALLOC range. Gated on actual presence,
  so aarch64/riscv64 (no .note sections) keep their existing phnum
  and produce byte-identical output.
- load-obj-accept-sht-note: tcc_load_object_file() accepts SHT_NOTE
  in its section-type filter. Strict pair with the find_section
  change — without it, kernel-asm.o's .note.Xen is silently dropped
  during link and the subsequent .rela.note.Xen merge segfaults the
  linker on a NULL sm_table[].s.

Validation: aarch64 boot6/Image is byte-identical to pre-patch
(sha256 matches). amd64 boot6/kernel.elf has .note.Xen as NOTE,
exactly one PT_NOTE phdr at the right offset/size, and the Xen 18
note pointing at the kernel base.

Diffstat:
Mdocs/PLAN.md | 46+++++++++++++++++++++++++++++-----------------
Mdocs/TCC.md | 41+++++++++++++++++++++++++++++++++++++++++
Mscripts/boot6-gen-runscm.sh | 23-----------------------
Mscripts/boot6.sh | 13-------------
Mscripts/prep-src.sh | 5+----
Ascripts/simple-patches/tcc-0.9.26/load-obj-accept-sht-note.after | 18++++++++++++++++++
Ascripts/simple-patches/tcc-0.9.26/load-obj-accept-sht-note.before | 13+++++++++++++
Ascripts/simple-patches/tcc-0.9.26/note-section-sht-note.after | 6++++++
Ascripts/simple-patches/tcc-0.9.26/note-section-sht-note.before | 2++
Ascripts/simple-patches/tcc-0.9.26/pt-note-phdr.after | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ascripts/simple-patches/tcc-0.9.26/pt-note-phdr.before | 31+++++++++++++++++++++++++++++++
Mscripts/stage1-flatten.sh | 15+++++++++++++++
Dseed-kernel/scripts/elf-pvh-note.c | 103-------------------------------------------------------------------------------
13 files changed, 250 insertions(+), 160 deletions(-)

diff --git a/docs/PLAN.md b/docs/PLAN.md @@ -247,20 +247,32 @@ land" wording); audit found that wrong (AT.4 is a pure refactor). `scripts/simple-patches/tcc-0.9.26/arm64-svcul-no-truncate*`; track separately. -- **AT.2. `.note.*` SHT_NOTE / PT_NOTE.** tcc emits `.note.*` sections - as `SHT_PROGBITS` and never writes a `PT_NOTE` phdr, forcing the - post-link `seed-kernel/scripts/elf-pvh-note.c` tool to rewrite the - ELF for amd64 PVH boot. Two patches: - - `note-section-sht-note.{before,after}` — in `tccelf.c:251` +- **AT.2. `.note.*` SHT_NOTE / PT_NOTE.** [done] tcc emitted + `.note.*` sections as `SHT_PROGBITS` and never wrote a `PT_NOTE` + phdr, forcing the post-link `seed-kernel/scripts/elf-pvh-note.c` + tool to rewrite the ELF for amd64 PVH boot. Three patches landed in + `scripts/simple-patches/tcc-0.9.26/`: + - `note-section-sht-note.{before,after}` — in `tccelf.c` `find_section()`, name-prefix-check `.note*` → create as `SHT_NOTE`. - - `pt-note-phdr.{before,after}` — in `tccelf.c:elf_output_file` - (~2202), gate `phnum++` on "any `SHT_NOTE+SHF_ALLOC` section - exists" (else aarch64/riscv64 phnum perturbs and breaks the A5 - reproducibility target); compute `min(sh_offset)` / - `max(sh_offset+sh_size)` over those sections; emit one `PT_NOTE`. - Then delete `seed-kernel/scripts/elf-pvh-note.c`, - `scripts/prep-src.sh:121–123`, `scripts/boot6.sh:80–89`, - `scripts/boot6-gen-runscm.sh:99–117`. `seed-kernel/arch/amd64/kernel.lds` + - `pt-note-phdr.{before,after}` — in `tccelf.c:elf_output_file`, + gate `phnum++` on "any `SHT_NOTE+SHF_ALLOC` section exists" (else + aarch64/riscv64 phnum perturbs and breaks the A5 reproducibility + target); compute `min(sh_offset)` / `max(sh_offset+sh_size)` + over those sections; fill the leftover `PT_NULL` slot with one + `PT_NOTE` after `fill_unloadable_phdr` runs. + - `load-obj-accept-sht-note.{before,after}` — in + `tccelf.c:tcc_load_object_file`, add `SHT_NOTE` to the + accepted-section-type list. Strict pair with the find_section + change: without it, .o files emitted by the patched compiler + drop their `.note*` sections during link, then the subsequent + `.rela.note.*` merge derefs `sm_table[].s == NULL` and segfaults + the linker. (Found during validation: aarch64 was byte-identical, + amd64 boot6 link crashed under QEMU until this third patch.) + Then deleted `seed-kernel/scripts/elf-pvh-note.c`, the + `cp seed-kernel/scripts/elf-pvh-note.c` block in + `scripts/prep-src.sh`, the `if [ "$ARCH" = amd64 ]` block in + `scripts/boot6.sh`, and the amd64 PVH-fixup block in + `scripts/boot6-gen-runscm.sh`. `seed-kernel/arch/amd64/kernel.lds` is unchanged (consumed by clang+ld.lld, not tcc). - **AT.3. riscv64 inline-asm — audit + memory update only, no patch.** @@ -308,10 +320,10 @@ land" wording); audit found that wrong (AT.4 is a pure refactor). - `vendor/mes-libc/mes/abtol.c` (AT.1 preferred fix) **or** new patch `scripts/simple-patches/tcc-0.9.26/tccasm-asm-expr-parse-number.*` (AT.1 alternative). -- `scripts/simple-patches/tcc-0.9.26/note-section-sht-note.*` and - `pt-note-phdr.*` (AT.2). The patch directory is - `scripts/simple-patches/tcc-0.9.26/` (not `seed-kernel/simple-patches/`, - which doesn't exist). +- `scripts/simple-patches/tcc-0.9.26/note-section-sht-note.*`, + `pt-note-phdr.*`, and `load-obj-accept-sht-note.*` (AT.2). The + patch directory is `scripts/simple-patches/tcc-0.9.26/` (not + `seed-kernel/simple-patches/`, which doesn't exist). - `scripts/stage1-flatten.sh`: list any newly-added patches in `apply_our_patch` (around line ~223); regenerate the flattened tree. - `seed-kernel/arch/amd64/kernel.S`: lines 406–413 (`.quad` GDT diff --git a/docs/TCC.md b/docs/TCC.md @@ -521,3 +521,44 @@ narrow. Until this is fixed, tcc1 is the "shake-out" stage and tcc2 is the canonical compiler. + +## AT-series patches (post-bootstrap uniformity) + +These patches go beyond the bootstrap-stub patches in +`scripts/simple-patches/tcc-0.9.26/` and exist to remove per-arch +workarounds in seed-kernel and the build pipeline. They live in the +same patch directory and are listed in `scripts/stage1-flatten.sh`'s +`apply_our_patch` block. + +### AT.2 — native PT_NOTE for PVH boot + +Stock tcc 0.9.26 tags every assembler-created section as +`SHT_PROGBITS` and emits only `PT_LOAD` phdrs for static EXEs. +QEMU's PVH `-kernel` path on amd64 scans `PT_NOTE` phdrs for the +Xen 18 note that names the 32-bit entry; without one it errors out +("Error loading uncompressed kernel without PVH ELF Note"). The +old workaround was a post-link host tool, `elf-pvh-note.c`, that +rewrote the ELF after tcc finished. AT.2 replaces it with two +patches in `tccelf.c`: + +- `note-section-sht-note` — `find_section()` creates `.note*` + sections as `SHT_NOTE` instead of `SHT_PROGBITS`. +- `pt-note-phdr` — `elf_output_file()` bumps `phnum` by one when at + least one `SHT_NOTE+SHF_ALLOC` section exists, then fills the + reserved phdr slot with a `PT_NOTE` covering + `[min(sh_offset), max(sh_offset+sh_size))` over those sections. + The bump is gated on actual presence so arches with no note + sections (aarch64, riscv64) keep the same phnum and produce + byte-identical output. +- `load-obj-accept-sht-note` — `tcc_load_object_file()`'s + accepted-section-type list adds `SHT_NOTE`. Without this, .o + files emitted by the patched `find_section` (which now produces + `SHT_NOTE` for `.note*`) get silently skipped during the link; + the subsequent `.rela.note.*` merge then derefs a NULL + `sm_table[].s` for the (skipped) note section. Strict pair with + `note-section-sht-note`. + +After AT.2 the post-link `elf-pvh-note.c` tool, the amd64-only +branch in `boot6.sh`, and the amd64 fixup block in +`boot6-gen-runscm.sh` are all gone. The amd64 kernel is emitted +ready-to-boot by tcc3. diff --git a/scripts/boot6-gen-runscm.sh b/scripts/boot6-gen-runscm.sh @@ -94,29 +94,6 @@ cat > "$OUT" <<EOF "-o" "out/$OUT_FILE" "out/kernel-asm.o" "out/kernel.o" "out/mmu.o" "out/mem.o") "link $OUT_FILE") -EOF - -if [ "$ARCH" = amd64 ]; then - cat >> "$OUT" <<'EOF' - -;; amd64: tcc3 doesn't emit PT_NOTE phdrs, so QEMU's PVH `-kernel` -;; path doesn't find the Xen 18 note we placed in `.note.Xen`. -;; Build the fixup tool with tcc3 + boot4's libc/crt1/libtcc1 and -;; rewrite the ELF in place. -(write-string stdout "boot6: tcc3 build elf-pvh-note\n") -(must (run "in/tcc3" "-nostdlib" - "in/crt1.o" "in/elf-pvh-note.c" - "in/libc.a" "in/libtcc1.a" "in/libc.a" - "-o" "out/elf-pvh-note") - "build elf-pvh-note") - -(write-string stdout "boot6: amd64 PVH note fixup\n") -(must (run "out/elf-pvh-note" "out/kernel.elf") - "elf-pvh-note kernel.elf") -EOF -fi - -cat >> "$OUT" <<EOF (write-string stdout "boot6: ALL-OK\n") (exit 0) diff --git a/scripts/boot6.sh b/scripts/boot6.sh @@ -15,8 +15,6 @@ ## — DTB parse, MMU bring-up, syscalls, ## virtio-blk, tmpfs, ELF loader ## build/$ARCH/src/src/tcc-cc/mem.c memcpy/memset/memmove/memcmp -## build/$ARCH/src/src/kernel/scripts/elf-pvh-note.c -## — amd64-only post-link PT_NOTE fixup ## ## ─── Tools ──────────────────────────────────────────────────────────── ## In container: scratch + busybox (boot2-empty:$ARCH). @@ -77,17 +75,6 @@ runscm_input_from_src src "kernel/arch/$ARCH/arch.h" runscm_input_from_src src "kernel/arch/$ARCH/mmu.c" runscm_input_from_src src tcc-cc/mem.c -# amd64 needs a post-link fixup — tcc3 doesn't emit PT_NOTE phdrs, so -# QEMU's PVH `-kernel` path can't find the Xen 18 note that names the -# 32-bit entry. The fixup is a hosted C tool we build inside the same -# run.scm with tcc3 + boot4's libc/crt1/libtcc1, then run on kernel.elf. -if [ "$ARCH" = amd64 ]; then - runscm_input_from_src src kernel/scripts/elf-pvh-note.c - runscm_input crt1.o "$BOOT4/crt1.o" - runscm_input libc.a "$BOOT4/libc.a" - runscm_input libtcc1.a "$BOOT4/libtcc1.a" -fi - runscm_export "$OUT_FILE" runscm_run "${BOOT6_TIMEOUT:-1200}" diff --git a/scripts/prep-src.sh b/scripts/prep-src.sh @@ -108,7 +108,7 @@ mkdir -p "$DST_SRC/test-fixtures" cp scripts/boot-hello.c "$DST_SRC/test-fixtures/boot-hello.c" # ── (3) seed-kernel sources for this arch ───────────────────────────── -mkdir -p "$DST_SRC/kernel/arch/$ARCH" "$DST_SRC/kernel/user" "$DST_SRC/kernel/scripts" +mkdir -p "$DST_SRC/kernel/arch/$ARCH" "$DST_SRC/kernel/user" cp seed-kernel/kernel.c "$DST_SRC/kernel/kernel.c" for f in seed-kernel/arch/$ARCH/*; do [ -f "$f" ] || continue @@ -118,9 +118,6 @@ for f in seed-kernel/user/*; do [ -f "$f" ] || continue cp "$f" "$DST_SRC/kernel/user/$(basename "$f")" done -# elf-pvh-note.c is consumed by boot6 on amd64; keep it in the canonical -# tree on every arch so the layout is uniform. -cp seed-kernel/scripts/elf-pvh-note.c "$DST_SRC/kernel/scripts/elf-pvh-note.c" # ── (4) tcc flatten ─────────────────────────────────────────────────── # stage1-flatten.sh writes to build/<arch>/vendor/tcc/. Run it (it's diff --git a/scripts/simple-patches/tcc-0.9.26/load-obj-accept-sht-note.after b/scripts/simple-patches/tcc-0.9.26/load-obj-accept-sht-note.after @@ -0,0 +1,18 @@ + /* ignore sections types we do not handle. AT.2: SHT_NOTE is + * accepted so .o files emitted by find_section()'s .note* + * branch are merged in. Without this, kernel.elf links skip + * .note.Xen and the subsequent .rela.note.Xen merge derefs + * a NULL sm_table[].s for the (skipped) note section. */ + if (sh->sh_type != SHT_PROGBITS && + sh->sh_type != SHT_RELX && +#ifdef TCC_ARM_EABI + sh->sh_type != SHT_ARM_EXIDX && +#endif + sh->sh_type != SHT_NOBITS && + sh->sh_type != SHT_NOTE && + sh->sh_type != SHT_PREINIT_ARRAY && + sh->sh_type != SHT_INIT_ARRAY && + sh->sh_type != SHT_FINI_ARRAY && + strcmp(sh_name, ".stabstr") + ) + continue; diff --git a/scripts/simple-patches/tcc-0.9.26/load-obj-accept-sht-note.before b/scripts/simple-patches/tcc-0.9.26/load-obj-accept-sht-note.before @@ -0,0 +1,13 @@ + /* ignore sections types we do not handle */ + if (sh->sh_type != SHT_PROGBITS && + sh->sh_type != SHT_RELX && +#ifdef TCC_ARM_EABI + sh->sh_type != SHT_ARM_EXIDX && +#endif + sh->sh_type != SHT_NOBITS && + sh->sh_type != SHT_PREINIT_ARRAY && + sh->sh_type != SHT_INIT_ARRAY && + sh->sh_type != SHT_FINI_ARRAY && + strcmp(sh_name, ".stabstr") + ) + continue; diff --git a/scripts/simple-patches/tcc-0.9.26/note-section-sht-note.after b/scripts/simple-patches/tcc-0.9.26/note-section-sht-note.after @@ -0,0 +1,6 @@ + /* AT.2: .note* sections must have SHT_NOTE so a PT_NOTE phdr can + * point at them; PVH boot scans .note.Xen for the 18 entry. All + * other implicitly-created sections stay PROGBITS. */ + if (!strncmp(name, ".note", 5)) + return new_section(s1, name, SHT_NOTE, SHF_ALLOC); + return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC); diff --git a/scripts/simple-patches/tcc-0.9.26/note-section-sht-note.before b/scripts/simple-patches/tcc-0.9.26/note-section-sht-note.before @@ -0,0 +1,2 @@ + /* sections are created as PROGBITS */ + return new_section(s1, name, SHT_PROGBITS, SHF_ALLOC); diff --git a/scripts/simple-patches/tcc-0.9.26/pt-note-phdr.after b/scripts/simple-patches/tcc-0.9.26/pt-note-phdr.after @@ -0,0 +1,94 @@ + /* compute number of program headers */ + switch(file_type) { + default: + case TCC_OUTPUT_OBJ: + phnum = 0; + break; + case TCC_OUTPUT_EXE: + if (!s1->static_link) + phnum = 4 + HAVE_PHDR; + else + phnum = 2; + break; + case TCC_OUTPUT_DLL: + phnum = 3; + break; + } + + /* AT.2: bump phnum by 1 when an SHT_NOTE+SHF_ALLOC section is + * present, so the layout has room for one PT_NOTE phdr covering + * the .note* group. Gating on actual presence keeps the phnum + * unchanged for builds with no note sections (aarch64/riscv64 + * stay byte-identical). */ + if (phnum > 0) { + for (i = 1; i < s1->nb_sections; i++) { + Section *ns = s1->sections[i]; + if (ns->sh_type == SHT_NOTE && (ns->sh_flags & SHF_ALLOC)) { + phnum++; + break; + } + } + } + + /* Allocate strings for section names */ + alloc_sec_names(s1, file_type, strsec); + + /* allocate program segment headers */ + phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr))); + + /* compute section to program header mapping */ + file_offset = layout_sections(s1, phdr, phnum, interp, strsec, &dyninf, + sec_order); + + /* Fill remaining program header and finalize relocation related to dynamic + linking. */ + if (phnum > 0) { + fill_unloadable_phdr(phdr, phnum, interp, dynamic); + /* AT.2: fill the PT_NOTE phdr in the leftover PT_NULL slot. + * layout_sections fills the PT_LOAD slots; fill_unloadable_phdr + * fills PT_PHDR/PT_INTERP/PT_DYNAMIC. The extra slot reserved by + * the bump above is left as PT_NULL by tcc_mallocz, so the first + * PT_NULL we find is ours. Range = [min sh_offset, max + * sh_offset+sh_size) over SHT_NOTE+SHF_ALLOC sections. */ + { + addr_t note_off = 0, note_end = 0, note_addr = 0; + int note_found = 0; + for (i = 1; i < s1->nb_sections; i++) { + Section *ns = s1->sections[i]; + if (ns->sh_type != SHT_NOTE || !(ns->sh_flags & SHF_ALLOC)) + continue; + if (!note_found) { + note_off = ns->sh_offset; + note_end = ns->sh_offset + ns->sh_size; + note_addr = ns->sh_addr; + note_found = 1; + } else { + if (ns->sh_offset < note_off) + note_off = ns->sh_offset; + if (ns->sh_offset + ns->sh_size > note_end) + note_end = ns->sh_offset + ns->sh_size; + if (ns->sh_addr < note_addr) + note_addr = ns->sh_addr; + } + } + if (note_found) { + ElfW(Phdr) *nph = NULL; + int j; + for (j = 0; j < phnum; j++) { + if (phdr[j].p_type == PT_NULL) { + nph = &phdr[j]; + break; + } + } + if (nph) { + nph->p_type = PT_NOTE; + nph->p_flags = PF_R; + nph->p_offset = note_off; + nph->p_vaddr = note_addr; + nph->p_paddr = note_addr; + nph->p_filesz = note_end - note_off; + nph->p_memsz = note_end - note_off; + nph->p_align = 4; + } + } + } diff --git a/scripts/simple-patches/tcc-0.9.26/pt-note-phdr.before b/scripts/simple-patches/tcc-0.9.26/pt-note-phdr.before @@ -0,0 +1,31 @@ + /* compute number of program headers */ + switch(file_type) { + default: + case TCC_OUTPUT_OBJ: + phnum = 0; + break; + case TCC_OUTPUT_EXE: + if (!s1->static_link) + phnum = 4 + HAVE_PHDR; + else + phnum = 2; + break; + case TCC_OUTPUT_DLL: + phnum = 3; + break; + } + + /* Allocate strings for section names */ + alloc_sec_names(s1, file_type, strsec); + + /* allocate program segment headers */ + phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr))); + + /* compute section to program header mapping */ + file_offset = layout_sections(s1, phdr, phnum, interp, strsec, &dyninf, + sec_order); + + /* Fill remaining program header and finalize relocation related to dynamic + linking. */ + if (phnum > 0) { + fill_unloadable_phdr(phdr, phnum, interp, dynamic); diff --git a/scripts/stage1-flatten.sh b/scripts/stage1-flatten.sh @@ -164,6 +164,21 @@ apply_our_patch bss-start-symbol "$SRC/tccelf.c" # other arches: the block is gated `#ifdef TCC_TARGET_X86_64`. apply_our_patch x86_64-static-plt32 "$SRC/tccelf.c" +# AT.2: native PT_NOTE for PVH boot. Stock tcc tags `.note.*` sections +# as SHT_PROGBITS and never emits a PT_NOTE phdr, so QEMU's PVH +# `-kernel` path on amd64 (which scans PT_NOTE for the Xen 18 entry) +# rejects the kernel. Three patches: (1) retype implicitly-created +# `.note*` sections to SHT_NOTE; (2) allocate a PT_NOTE phdr covering +# every SHT_NOTE+SHF_ALLOC section; (3) accept SHT_NOTE in the .o +# loader so kernel-asm.o's .note.Xen merges into the link output (else +# the subsequent .rela.note.Xen merge derefs sm_table[].s == NULL). +# The phnum bump in (2) is gated on actual presence so aarch64/riscv64 +# (no .note sections) keep their existing phdr count and stay +# byte-identical to pre-patch output. +apply_our_patch note-section-sht-note "$SRC/tccelf.c" +apply_our_patch pt-note-phdr "$SRC/tccelf.c" +apply_our_patch load-obj-accept-sht-note "$SRC/tccelf.c" + # x86_64 va_list runtime: tcc's lib/va_list.c declares `extern void # abort(void)` and calls it in an unreachable default branch of the # arg-type switch. Under -nostdlib that abort() symbol is unresolved diff --git a/seed-kernel/scripts/elf-pvh-note.c b/seed-kernel/scripts/elf-pvh-note.c @@ -1,103 +0,0 @@ -/* elf-pvh-note — append a PT_NOTE program header pointing at the - * existing `.note.Xen` section, and retype that section as SHT_NOTE. - * - * Why: tcc 0.9.26's linker emits only PT_LOAD program headers and - * marks every assembler-created section as SHT_PROGBITS, so QEMU's - * PVH `-kernel` path (which scans PT_NOTE phdrs for the Xen 18 note - * to find the 32-bit entry) refuses the kernel with "Error loading - * uncompressed kernel without PVH ELF Note". Patching the linker is - * a much bigger change than rewriting six fields after the fact; - * tcc reserves the post-Ehdr program-header area only large enough - * for its declared phnum, but the next section content lives at - * 0x200000 (s_align), so writing one extra Phdr at phoff+phnum*phentsize - * fits in the gap. - * - * Usage: elf-pvh-note <elf-path> - */ - -extern int open(const char *, int, ...); -extern long lseek(int, long, int); -extern long read(int, void *, unsigned long); -extern long write(int, const void *, unsigned long); -extern int close(int); -extern void *malloc(unsigned long); -extern int strcmp(const char *, const char *); -extern int printf(const char *, ...); - -#define O_RDWR 2 -#define SEEK_SET 0 -#define SEEK_END 2 - -#define PT_NOTE 4 -#define PF_R 4 -#define SHT_NOTE 7 - -typedef unsigned long u64; -typedef unsigned int u32; -typedef unsigned short u16; -typedef unsigned char u8; - -static u16 r16(u8 *p) { return (u16)p[0] | ((u16)p[1] << 8); } -static u32 r32(u8 *p) { - return (u32)p[0] | ((u32)p[1] << 8) | ((u32)p[2] << 16) | ((u32)p[3] << 24); -} -static u64 r64(u8 *p) { - return (u64)r32(p) | ((u64)r32(p + 4) << 32); -} -static void w16(u8 *p, u16 v) { p[0] = v; p[1] = v >> 8; } -static void w32(u8 *p, u32 v) { - p[0] = v; p[1] = v >> 8; p[2] = v >> 16; p[3] = v >> 24; -} -static void w64(u8 *p, u64 v) { w32(p, (u32)v); w32(p + 4, (u32)(v >> 32)); } - -int main(int argc, char **argv) { - if (argc != 2) { printf("usage: elf-pvh-note <elf>\n"); return 2; } - int fd = open(argv[1], O_RDWR); - if (fd < 0) { printf("elf-pvh-note: open failed\n"); return 1; } - long size = lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); - u8 *buf = (u8 *)malloc(size); - if (read(fd, buf, size) != size) { printf("elf-pvh-note: short read\n"); return 1; } - - u64 phoff = r64(buf + 0x20); - u16 phentsize = r16(buf + 0x36); - u16 phnum = r16(buf + 0x38); - u64 shoff = r64(buf + 0x28); - u16 shentsize = r16(buf + 0x3a); - u16 shnum = r16(buf + 0x3c); - u16 shstrndx = r16(buf + 0x3e); - - u64 shstrtab_off = r64(buf + shoff + shstrndx * shentsize + 0x18); - - u64 note_off = 0, note_size = 0, note_addr = 0; - int found = 0; - for (int i = 0; i < shnum; i++) { - u8 *sh = buf + shoff + i * shentsize; - u32 name_off = r32(sh); - if (strcmp((char *)(buf + shstrtab_off + name_off), ".note.Xen") == 0) { - note_addr = r64(sh + 0x10); - note_off = r64(sh + 0x18); - note_size = r64(sh + 0x20); - w32(sh + 4, SHT_NOTE); - found = 1; - break; - } - } - if (!found) { printf("elf-pvh-note: no .note.Xen section\n"); return 1; } - - u8 *ph = buf + phoff + phnum * phentsize; - w32(ph + 0, PT_NOTE); - w32(ph + 4, PF_R); - w64(ph + 8, note_off); - w64(ph + 16, note_addr); - w64(ph + 24, note_addr); - w64(ph + 32, note_size); - w64(ph + 40, note_size); - w64(ph + 48, 4); - w16(buf + 0x38, phnum + 1); - - lseek(fd, 0, SEEK_SET); - if (write(fd, buf, size) != size) { printf("elf-pvh-note: short write\n"); return 1; } - close(fd); - return 0; -}