# Binutils Patches for Retro68 This (AI-generated) document describes the changes applied to binutils (currently 2.46) to support the Retro68 toolchain targeting classic Mac OS (m68k and PowerPC/XCOFF). --- ## 1. Enable the PowerPC XCOFF target unconditionally ### What it does The upstream `bfd/targets.c` wraps the `powerpc_xcoff_vec` target vector in `#if 0` because it shares the same BFD magic number as RS/6000 and was therefore disabled by default to avoid conflicts. Retro68 needs this target always available for the `powerpc-apple-macos` cross-compiler. ### Files changed | File | Change | |------|--------| | `binutils/bfd/targets.c` | Changes `#if 0` to `#if 1` around the `powerpc_xcoff_vec` entry in `_bfd_target_vector[]`. | | `binutils/bfd/config.bfd` | In the `powerpc-*-macos*` case, adds `targ_cflags=-DAIX_WEAK_SUPPORT` and `targ_selvecs=powerpc_xcoff_vec` (in addition to the already-present `targ_defvec`). | ### Re-applying to a newer binutils Find the `powerpc_xcoff_vec` entry in `bfd/targets.c` and change its guard from `#if 0` to `#if 1`. In `bfd/config.bfd`, locate the `powerpc-*-macos*` entry and add the two lines. --- ## 2. GAS: m68k-macos assembler format and AIX weak support ### What it does - Registers ELF as the output format for `m68k-*-macos*` targets in the GAS target configuration (upstream only handled `m68k-*-elf*` and `m68k-*-sysv4*`). - Extends the `AIX_WEAK_SUPPORT` preprocessor define (needed for the `C_WEAKEXT` symbol class used in XCOFF) to also cover `ppc-*-macos*` targets, not just AIX 5+ targets. ### Files changed | File | Change | |------|--------| | `binutils/gas/configure.tgt` | Adds `m68k-*-macos*) fmt=elf ;;` entry. | | `binutils/gas/configure.ac` | Extends the `ppc-*-aix[5-9].*` pattern to also match `ppc-*-macos*` for `AC_DEFINE(AIX_WEAK_SUPPORT, 1, ...)`. | | `binutils/gas/configure` | Generated `configure` updated to match. | ### Re-applying to a newer binutils In `gas/configure.tgt`, add the `m68k-*-macos*` line adjacent to the other `m68k-*-elf*` entries. In `gas/configure.ac`, extend the `ppc-*-aix[5-9].*` pattern to include `| ppc-*-macos*` and regenerate `configure`. --- ## 3. LD: custom linker script for `ppcmacos` ### What it does The upstream `ld/emulparams/ppcmacos.sh` used the generic `aix` linker script (`SCRIPT_NAME=aix`). This is replaced with a new `ppcmacos` script that: - Defines sections in the correct order for a classic Mac OS XCOFF executable: `.pad`, `.text`, `.data` (including the TOC and constructor table), `.bss`, `.loader`, and `.debug`. - Aligns the text segment at `0x10000000 + SIZEOF_HEADERS`. - Aligns the data segment to page boundaries relative to text. - Includes `${CONSTRUCTING+CONSTRUCTORS}` in `.data` so that C++ global constructors and destructors are linked in. - Does **not** import the `gdbserver` tool from the build system (see configure change below). ### Files changed | File | Change | |------|--------| | `binutils/ld/emulparams/ppcmacos.sh` | Changes `SCRIPT_NAME=aix` to `SCRIPT_NAME=ppcmacos`. | | `binutils/ld/scripttempl/ppcmacos.sc` | New file. The full linker script template (75 lines). | | `binutils/configure` | Removes `gdbserver` from `host_tools` (prevents an unneeded build dependency). | ### Re-applying to a newer binutils Copy `ppcmacos.sc` into `ld/scripttempl/`. Change `SCRIPT_NAME` in `ld/emulparams/ppcmacos.sh`. The `configure` change (removing `gdbserver`) should be verified against the current `configure.ac` and regenerated if necessary. --- ## 4. LD/objdump: `AIX_WEAK_SUPPORT` for `powerpc-*-macos` ### What it does The `AIX_WEAK_SUPPORT` flag enables proper handling of the `C_WEAKEXT` symbol class in XCOFF object files. Upstream only enabled it for AIX 5+ targets; Retro68 also needs it for `powerpc-*-macos`. ### Files changed | File | Change | |------|--------| | `binutils/binutils/configure.ac` | Extends the `powerpc*-aix[5-9].*` pattern to include `| powerpc-*-macos` for the `OBJDUMP_DEFS="-DAIX_WEAK_SUPPORT"` assignment. | | `binutils/binutils/configure` | Generated `configure` updated to match. | ### Re-applying to a newer binutils In `binutils/configure.ac`, find the `OBJDUMP_DEFS` AIX pattern and extend it to include `powerpc-*-macos`, then regenerate `configure`. --- ## 5. BFD/XCOFF: weak symbol handling fixes ### What it does Several related fixes in the XCOFF linker to handle `bfd_link_hash_undefweak` and `C_AIX_WEAKEXT` symbols correctly when linking Mac OS PowerPC executables: ### 5a. Do not report unresolved weak symbols as errors `bfd/coff-rs6000.c` (`xcoff_ppc_relocate_section`): adds a check `h->root.type != bfd_link_hash_undefweak` to the condition that reports an undefined-symbol error. Weak undefined symbols are allowed to remain unresolved; they resolve to zero. ### 5b. Handle `undefweak` as zero in relocation Same function: adds an `else if (h->root.type == bfd_link_hash_undefweak)` branch that sets `sec = 0` and `val = 0`, matching the ELF convention that a weak undefined symbol has value zero. ### 5c. Do not zero the size of `C_AIX_WEAKEXT` common sections `bfd/xcofflink.c` (`xcoff_link_add_symbols`): when adding a common symbol to the hash table, the original code unconditionally set `csect->size = 0`. For `C_AIX_WEAKEXT` symbols this must be preserved (see `AutomatedTests/LocalStatic.cc`). Two guarded sites: once for the initial common-section creation, and once in the duplicate-symbol resolution path where a `defweak` symbol should not trigger the size zeroing. ### 5d. Do not propagate `XCOFF_WAS_UNDEFINED` through weak descriptor `bfd/xcofflink.c` (`xcoff_mark_symbol`): when a descriptor's source symbol is marked `XCOFF_WAS_UNDEFINED`, the flag was unconditionally propagated to the descriptor. Adds a guard so the flag is only propagated when the descriptor is not `bfd_link_hash_undefweak`. ### 5e. Mark entry-point symbol before the static-section pass `bfd/xcofflink.c` (`bfd_xcoff_size_dynamic_sections`): moves the `xcoff_mark_symbol_by_name(entry, XCOFF_ENTRY)` call to *before* the `else` branch that processes non-relocatable links, ensuring the entry-point symbol is always marked with `XCOFF_ENTRY` regardless of link mode. ### Files changed All changes are in `binutils/bfd/coff-rs6000.c` and `binutils/bfd/xcofflink.c`. ### Re-applying to a newer binutils Each of these is a small, targeted change. Locate the functions by name (`xcoff_ppc_relocate_section`, `xcoff_link_add_symbols`, `xcoff_mark_symbol`, `bfd_xcoff_size_dynamic_sections`) and apply the individual hunks. The `xcoff_link_add_symbols` changes reference `C_AIX_WEAKEXT`; verify the constant name has not changed. --- ## 6. BFD/XCOFF: keep constructor and exception-table sections ### What it does `bfd/xcofflink.c` (`xcoff_link_add_symbols`): when a symbol whose name starts with `_GLOBAL__` (the GCC convention for constructor/destructor registration functions and exception-frame entries) is encountered, its csect is marked `SEC_KEEP` so that the XCOFF garbage-collector does not discard it. A second loop in `bfd_xcoff_size_dynamic_sections` then explicitly calls `xcoff_mark` on all sections with `SEC_KEEP` set, ensuring they are included in the final output even when `--gc-sections` is in effect. ### Files changed | File | Change | |------|--------| | `binutils/bfd/xcofflink.c` | Adds the `_GLOBAL__` name prefix check setting `SEC_KEEP`, and the `SEC_KEEP` mark loop. | ### Re-applying to a newer binutils Find `xcoff_link_add_symbols` and `bfd_xcoff_size_dynamic_sections`; apply the two hunks in sequence. --- ## 7. ELF GC: keep `.macsbug` sections adjacent to their function ### What it does During ELF section garbage collection (`bfd/elflink.c`, `_bfd_elf_gc_mark`), after marking a section as live, checks whether the *next* section in the section list has a name equal to the current section's name plus `.macsbug`. If so, that section is also marked live. This ensures that per-function MacsBug symbol records (which GCC places in `.text.funcname.macsbug` sections when `-ffunction-sections` is active) are not stripped by the linker's `--gc-sections` pass. ### Files changed | File | Change | |------|--------| | `binutils/bfd/elflink.c` | Adds a name-comparison block in `_bfd_elf_gc_mark` after the `group_sec` check. | ### Re-applying to a newer binutils Locate `_bfd_elf_gc_mark` in `bfd/elflink.c`. After the group-section recursion block and before "Look through the section relocs", insert the new block. The logic is: compare section names character by character, and if the current name is a prefix of the next section's name and the suffix is exactly `.macsbug`, mark the next section. --- ## 8. BFD/XCOFF: long filename in auxiliary records ### What it does Fixes a buffer-overrun / incorrect read in `bfd/coff-rs6000.c` (`_bfd_xcoff_swap_aux_in`) when a `C_FILE` auxiliary entry has more than one auxiliary record (i.e. the filename is longer than `FILNMLEN` bytes and spans multiple aux entries). The fix: when `numaux > 1` and the current aux-entry index (`indx`) is 0, copy `numaux * sizeof(AUXENT)` bytes instead of just `FILNMLEN` bytes. The original unconditional `memcpy(…, FILNMLEN)` is retained for the normal single-aux-entry case. ### Files changed | File | Change | |------|--------| | `binutils/bfd/coff-rs6000.c` | Replaces the `memcpy` in the `else` branch of the `C_FILE`/`x_n.x_n.x_zeroes` check with a conditional copy. | ### Re-applying to a newer binutils Locate `_bfd_xcoff_swap_aux_in` and the `else` branch of the filename read; apply the length-conditional logic. --- ## 9. objdump: pretty-print `C_FILE` auxiliary entries ### What it does Adds a `C_FILE` branch to `coff_print_aux` in `bfd/coffcode.h` that prints the filename stored in the auxiliary entry, resolving the string-table offset if necessary (the `x_zeroes` field indicates whether the name is inline or referenced by offset into the COFF string table). Without this, objdump prints raw hex bytes for `C_FILE` auxiliaries, making XCOFF object dumps very hard to read. ### Files changed | File | Change | |------|--------| | `binutils/bfd/coffcode.h` | Adds a `C_FILE` case at the top of `coff_print_aux` for RS6000COFF targets. | ### Re-applying to a newer binutils In `bfd/coffcode.h`, find `coff_print_aux` and the existing `#ifdef RS6000COFF_C` guard; insert the new `C_FILE` block immediately after the guard. --- ## 10. `zlib/zutil.h`: remove dead Metrowerks `fdopen` workaround ### What it does Removes a block that defined `fdopen(fd, mode)` as `NULL` for old Metrowerks CodeWarrior compilers on Mac OS. This block is now dead code and confuses modern compilers when `MACOS` or `TARGET_OS_MAC` is defined by the build environment. ### Files changed | File | Change | |------|--------| | `binutils/zlib/zutil.h` | Removes the `#ifndef Z_SOLO` / `fdopen` workaround block inside the `MACOS || TARGET_OS_MAC` guard. | ### Re-applying to a newer binutils Locate the `#if defined(MACOS) || defined(TARGET_OS_MAC)` block in `zlib/zutil.h` and remove the inner `fdopen` workaround (leave the `OS_CODE 7` line). --- ## 11. BFD/XCOFF: restore `xcoff_obj_data` after `bfd_free_cached_info` ### What it does In binutils 2.46, `xcoff_write_armap_big` calls `archive_iterator_begin` which calls `member_layout_init`. `member_layout_init` reads `xcoff_data(member)->text_align_power` for dynamic XCOFF members to compute page-alignment padding. However, by the time `xcoff_write_armap_big` is called, `_bfd_compute_and_write_armap` has already called `bfd_free_cached_info` on every archive member to free symbol tables. `_bfd_free_cached_info` sets `abfd->tdata.any = NULL` (via `_bfd_generic_bfd_free_cached_info`), which zeroes `xcoff_obj_data` because `tdata` is a union. This causes a NULL-pointer dereference in `member_layout_init` → segfault. The fix adds `xcoff_bfd_free_cached_info` in `bfd/coff-rs6000.c` that saves `xcoff_data(abfd)` before calling `_bfd_coff_free_cached_info` and restores the pointer afterwards. The underlying `xcoff_tdata` memory lives in the BFD's `objalloc` and is only released when the BFD is closed, so restoring the pointer is safe. This bug did not exist in binutils 2.39 because `xcoff_write_armap_big` did not call `archive_iterator_begin` in that version. ### Files changed | File | Change | |------|--------| | `binutils/bfd/coff-rs6000.c` | Adds `xcoff_bfd_free_cached_info`; changes `#define _bfd_xcoff_bfd_free_cached_info` to use it instead of `coff_bfd_free_cached_info`. | ### Re-applying to a newer binutils In `bfd/coff-rs6000.c`, find the `#define _bfd_xcoff_bfd_free_cached_info` line and the `_bfd_xcoff_mkobject` function. Insert `xcoff_bfd_free_cached_info` immediately before `_bfd_xcoff_mkobject` and update the `#define` to reference it. --- ## 12. BFD/ELF: don't corrupt relocations for `.eh_frame` inputs in custom output sections ### What it does When `--gc-sections` is used, `bfd_elf_gc_sections` parses each input `.eh_frame` section with `_bfd_elf_parse_eh_frame`, which sets `SEC_INFO_TYPE_EH_FRAME`. `_bfd_elf_discard_section_eh_frame` (which normally removes dead FDEs and initialises `sec->rawsize`) only runs for input sections mapped to an output section named `.eh_frame` — the Retro68 linker script instead places `KEEP(*(.eh_frame))` inside each `.codeNNNNN` output section, so the discard pass is skipped. With `sec->rawsize == 0`, `_bfd_elf_eh_frame_section_offset` took its `offset >= sec->rawsize` branch and returned `offset - 0 + sec->size`, i.e. it added the whole section size to every relocation offset. Because each `.eh_frame` input was shifted by its own size, relocations from different inputs collided at the same output offset. With `--emit-relocs` this produced duplicate `.rela.codeNNNNN` entries with conflicting addends; Retro68's `Elf2Mac` then produced a corrupt binary (crash at startup). The fix treats `rawsize == 0` as "this `.eh_frame` section has not been edited": `_bfd_elf_eh_frame_section_offset` returns the offset unchanged. This is correct because the section contents and entry offsets are untouched when the discard pass does not run. Links where the discard pass does run have `rawsize != 0`, so their behaviour is unchanged. Reproducer: an Executor/gtest app linked with `-Wl,-gc-sections` produced 50 duplicate offsets in `.rela.code00003`; with the fix it produces 0 and the app starts up instead of trapping at `pc 0x0`. ### Files changed | File | Change | |------|--------| | `binutils/bfd/elf-eh-frame.c` | In `_bfd_elf_eh_frame_section_offset`, return `offset` early when `sec->rawsize == 0`. | ### Re-applying to a newer binutils In `_bfd_elf_eh_frame_section_offset`, immediately after `sec_info = sec->sec_info;` and before the `if (offset >= sec->rawsize)` test, add: ```c if (sec->rawsize == 0) return offset; ``` --- ## 13. BFD/ELF: garbage-collect `.eh_frame` FDEs in custom output sections ### What it does Companion to §12. With `--gc-sections`, FDEs for discarded functions are meant to be removed by `_bfd_elf_discard_section_eh_frame`, but `bfd_elf_discard_info` only invoked it for the inputs of an output section named `.eh_frame`. Retro68 maps `KEEP(*(.eh_frame))` into each `.codeNNNNN` output section, so dead FDEs (and their relocations) were kept. `bfd_elf_discard_info` now also runs the discard pass, one output section at a time, for `.eh_frame` inputs that were already parsed by `bfd_elf_gc_sections` (`sec_info_type == SEC_INFO_TYPE_EH_FRAME`) but are not mapped to the `.eh_frame` output section. It is gated on `--gc-sections`, so non-GC links are unchanged. The CIE merge table (`eh_info.u.dwarf.cies`) is cleared before each output section, so CIEs are only merged within one output section — in Retro68 each `.codeNNNNN` is a separately loadable segment and an FDE must not reference a CIE in another segment. The section contents and relocations are then edited by the existing `_bfd_elf_write_section_eh_frame` / `_bfd_elf_eh_frame_section_offset` machinery, which already dispatch on the input section's `sec_info_type` and need no changes. Reproducer: the Executor gtest app linked with `-Wl,-gc-sections` shrinks `.code00003` from 0x8da54 to 0x815d8 (1480 FDEs and 128 CIEs removed), keeps zero duplicate relocations, and still starts up. ### Files changed | File | Change | |------|--------| | `binutils/bfd/elflink.c` | New `discard_eh_frame_inputs`; `bfd_elf_discard_info` processes parsed `.eh_frame` inputs of non-`.eh_frame` output sections when `info->gc_sections`. | ### Re-applying to a newer binutils In `bfd_elf_discard_info`, after the block that handles the output section named `.eh_frame` and before the `.sframe` block, add a loop over `output_bfd->sections` that, for each output section other than the `.eh_frame` one, runs `_bfd_elf_discard_section_eh_frame` (with a reloc cookie from `init_reloc_cookie_for_section`) on its `SEC_INFO_TYPE_EH_FRAME` inputs. Clear `elf_hash_table (info)->eh_info.u.dwarf.cies` before each such section. A reference implementation is `discard_eh_frame_inputs` in this tree. --- ## Summary of patches to re-apply when upgrading binutils | Priority | Area | Files | Complexity | |----------|------|-------|------------| | Essential | Enable XCOFF target | `bfd/targets.c`, `bfd/config.bfd` | Trivial | | Essential | GAS m68k-macos format + AIX weak support | `gas/configure.tgt`, `gas/configure.ac` | Low | | Essential | Custom `ppcmacos` linker script | `ld/emulparams/ppcmacos.sh`, `ld/scripttempl/ppcmacos.sc` | Low (new file) | | Essential | XCOFF weak symbol fixes | `bfd/coff-rs6000.c`, `bfd/xcofflink.c` | Medium | | Essential | Keep constructor/EH sections | `bfd/xcofflink.c` | Low | | Essential | Restore xcoff_obj_data after free_cached_info | `bfd/coff-rs6000.c` | Low | | Important | ELF GC: keep `.macsbug` sections | `bfd/elflink.c` | Low | | Important | ELF GC: `.eh_frame` relocs in custom output sections | `bfd/elf-eh-frame.c` | Trivial | | Important | ELF GC: `.eh_frame` FDEs in custom output sections | `bfd/elflink.c` | Low | | Important | AIX_WEAK_SUPPORT for objdump | `binutils/configure.ac` | Trivial | | Quality | Long filename in XCOFF aux records | `bfd/coff-rs6000.c` | Low | | Quality | objdump C_FILE pretty-print | `bfd/coffcode.h` | Low | | Quality | Remove dead `fdopen` workaround | `zlib/zutil.h` | Trivial |