#!/bin/sh set -eu : "${PACKAGE_VERSION:?}" : "${CPU_AS:?}" "${CPU_LD:?}" "${CPU_RUN:?}" "${CPU_READELF:?}" : "${CPU_AR:?}" "${CPU_NM:?}" "${CPU_OBJCOPY:?}" TMP=${TMPDIR:-/tmp}/libmcpu-objcopy-$$ mkdir -p "$TMP" trap 'rm -rf "$TMP"' EXIT HUP INT TERM cat > "$TMP/main.s" <<'EOS' .global _life .text _start_life: LDI r64@0x00000, 42 LDI r32@0x00030, 0 EXIT r32@0x00030 EOS cat > "$TMP/add-one.s" <<'EOS' .global add_one .text add_one: LDI r64@0x00008, 1 IADD r64@0x00000, r64@0x00000, r64@0x00008 RET EOS "$CPU_AS" -o "$TMP/main.o" "$TMP/main.s" "$CPU_AS" -o "$TMP/add-one.o" "$TMP/add-one.s" "$CPU_LD" --stack-size=4096 --no-heap -o "$TMP/main" "$TMP/main.o" "$CPU_AR" rcs "$TMP/libadd.a" "$TMP/add-one.o" # Plain MELF -> MELF copying is byte-for-byte when no edit is requested. "$CPU_OBJCOPY" "$TMP/main" "$TMP/main.copy" cmp "$TMP/main" "$TMP/main.copy" printf 'metadata-one\n' > "$TMP/note-one" printf 'metadata-two\n' > "$TMP/note-two" # Add non-ALLOC metadata without touching the executable image. "$CPU_OBJCOPY" --add-section=.note.test="$TMP/note-one" \ "$TMP/main" "$TMP/main.note" "$CPU_READELF" -S "$TMP/main.note" | grep -q '\.note\.test' test "$("$CPU_RUN" --int-reg=r64@0x00000 "$TMP/main.note")" = 42 # Dump is read-only and may inspect runtime sections as well as metadata. "$CPU_OBJCOPY" --dump-section=.note.test="$TMP/note.dump" \ "$TMP/main.note" "$TMP/main.dump-copy" cmp "$TMP/note-one" "$TMP/note.dump" "$CPU_OBJCOPY" --dump-section=.text="$TMP/text.before" \ "$TMP/main" "$TMP/main.text-copy" "$CPU_OBJCOPY" --dump-section=.text="$TMP/text.after" \ "$TMP/main.note" "$TMP/main.note-copy" cmp "$TMP/text.before" "$TMP/text.after" # Update and rename apply only to safe non-ALLOC PROGBITS metadata. "$CPU_OBJCOPY" --update-section=.note.test="$TMP/note-two" \ "$TMP/main.note" "$TMP/main.updated" "$CPU_OBJCOPY" --dump-section=.note.test="$TMP/updated.dump" \ "$TMP/main.updated" "$TMP/main.updated-copy" cmp "$TMP/note-two" "$TMP/updated.dump" "$CPU_OBJCOPY" --rename-section=.note.test=.note.renamed \ "$TMP/main.updated" "$TMP/main.renamed" "$CPU_READELF" -S "$TMP/main.renamed" | grep -q '\.note\.renamed' if "$CPU_READELF" -S "$TMP/main.renamed" | grep -q '\.note\.test'; then echo "old section name survived mcpu-objcopy rename" >&2 exit 1 fi # Safe metadata can be removed; runtime behavior stays unchanged. "$CPU_OBJCOPY" -R .note.renamed "$TMP/main.renamed" "$TMP/main.removed" if "$CPU_READELF" -S "$TMP/main.removed" | grep -q '\.note\.renamed'; then echo "removed metadata section survived mcpu-objcopy" >&2 exit 1 fi test "$("$CPU_RUN" --int-reg=r64@0x00000 "$TMP/main.removed")" = 42 # Runtime-visible and structural sections are protected. if "$CPU_OBJCOPY" -R .text "$TMP/main" "$TMP/bad-remove" >/dev/null 2>&1; then echo "mcpu-objcopy removed .text" >&2 exit 1 fi if "$CPU_OBJCOPY" --update-section=.text="$TMP/note-one" \ "$TMP/main" "$TMP/bad-update" >/dev/null 2>&1; then echo "mcpu-objcopy updated .text" >&2 exit 1 fi if "$CPU_OBJCOPY" --rename-section=.text=.foo \ "$TMP/main" "$TMP/bad-rename" >/dev/null 2>&1; then echo "mcpu-objcopy renamed .text" >&2 exit 1 fi if "$CPU_OBJCOPY" --add-section=.text="$TMP/note-one" \ "$TMP/main" "$TMP/bad-duplicate" >/dev/null 2>&1; then echo "mcpu-objcopy added duplicate .text" >&2 exit 1 fi # --only-section is strict: an executable cannot lose its runtime image. if "$CPU_OBJCOPY" -j .note.test "$TMP/main.note" "$TMP/bad-only" \ >/dev/null 2>&1; then echo "mcpu-objcopy created an incomplete ET_EXEC with -j" >&2 exit 1 fi # ET_REL link semantics are protected, but optional metadata may be filtered. "$CPU_OBJCOPY" --add-section=.note.rel="$TMP/note-one" \ "$TMP/main.o" "$TMP/main.note.o" "$CPU_OBJCOPY" \ -j .text -j .symtab -j .strtab -j .mcpu.target -j .shstrtab \ "$TMP/main.note.o" "$TMP/main.filtered.o" if "$CPU_READELF" -S "$TMP/main.filtered.o" | grep -q '\.note\.rel'; then echo "--only-section failed to remove optional ET_REL metadata" >&2 exit 1 fi "$CPU_LD" --stack-size=4096 --no-heap -o "$TMP/main.filtered" \ "$TMP/main.filtered.o" test "$("$CPU_RUN" --int-reg=r64@0x00000 "$TMP/main.filtered")" = 42 if "$CPU_OBJCOPY" -R .symtab "$TMP/main.o" "$TMP/bad-rel" \ >/dev/null 2>&1; then echo "mcpu-objcopy removed ET_REL .symtab" >&2 exit 1 fi # Archives are transformed member-by-member and retain their symbol index. "$CPU_OBJCOPY" --add-section=.note.arc="$TMP/note-one" \ "$TMP/libadd.a" "$TMP/libadd.note.a" "$CPU_NM" -A "$TMP/libadd.note.a" | grep -q 'add_one' mkdir "$TMP/extract" ( cd "$TMP/extract" "$CPU_AR" x ../libadd.note.a "$CPU_READELF" -S add-one.o | grep -q '\.note\.arc' ) "$CPU_OBJCOPY" -R .note.arc "$TMP/libadd.note.a" "$TMP/libadd.clean.a" "$CPU_NM" -A "$TMP/libadd.clean.a" | grep -q 'add_one' if "$CPU_OBJCOPY" --dump-section=.text="$TMP/archive.dump" \ "$TMP/libadd.a" "$TMP/libadd.dump.a" >/dev/null 2>&1; then echo "mcpu-objcopy accepted ambiguous archive --dump-section" >&2 exit 1 fi "$CPU_OBJCOPY" --help | grep -q -- '--add-section=NAME=FILE' "$CPU_OBJCOPY" --version | grep -Fq "$PACKAGE_VERSION" if "$CPU_OBJCOPY" "$TMP/main" >/dev/null 2>&1; then echo "mcpu-objcopy unexpectedly accepted a missing output file" >&2 exit 1 fi if "$CPU_OBJCOPY" "$TMP/main" "$TMP/main" >/dev/null 2>&1; then echo "mcpu-objcopy unexpectedly accepted in-place operation" >&2 exit 1 fi exit 0