Files
Retro68/BINUTILS-PATCHES.md

18 KiB

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 `
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

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:

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