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 handledm68k-*-elf*andm68k-*-sysv4*). - Extends the
AIX_WEAK_SUPPORTpreprocessor define (needed for theC_WEAKEXTsymbol class used in XCOFF) to also coverppc-*-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.dataso that C++ global constructors and destructors are linked in. - Does not import the
gdbservertool 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 |