/* Mach-O object file format
- Copyright 2009, 2011, 2012 Free Software Foundation, Inc.
+ Copyright (C) 2009-2015 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
#include "mach-o/loader.h"
#include "obj-macho.h"
+#include <string.h>
+
/* Forward decls. */
static segT obj_mach_o_segT_from_bfd_name (const char *, int);
/* TODO: Implement the "-n" command line option to suppress the initial
switch to the text segment. */
+
static int obj_mach_o_start_with_text_section = 1;
/* Allow for special re-ordering on output. */
#define SECT_TYPE_SPECIFIED 0x0001
#define SECT_ATTR_SPECIFIED 0x0002
#define SECT_ALGN_SPECIFIED 0x0004
+#define SECT_STUB_SPECIFIED 0x0008
static segT
obj_mach_o_make_or_get_sect (char * segname, char * sectname,
if (oldflags == SEC_NO_FLAGS)
{
+ /* In the absence of canonical information, try to determine CODE and
+ DEBUG section flags from the mach-o section data. */
+ if (flags == SEC_NO_FLAGS
+ && (specified_mask & SECT_ATTR_SPECIFIED)
+ && (secattr & BFD_MACH_O_S_ATTR_PURE_INSTRUCTIONS))
+ flags |= SEC_CODE;
+
+ if (flags == SEC_NO_FLAGS
+ && (specified_mask & SECT_ATTR_SPECIFIED)
+ && (secattr & BFD_MACH_O_S_ATTR_DEBUG))
+ flags |= SEC_DEBUGGING;
+
/* New, so just use the defaults or what's specified. */
if (! bfd_set_section_flags (stdoutput, sec, flags))
as_warn (_("failed to set flags for \"%s\": %s"),
msect->align = secalign;
msect->flags = sectype | secattr;
- msect->reserved2 = stub_size;
if (sectype == BFD_MACH_O_S_ZEROFILL
|| sectype == BFD_MACH_O_S_GB_ZEROFILL)
as_warn (_("Ignoring changed section attributes for %s"), name);
}
+ if (specified_mask & SECT_STUB_SPECIFIED)
+ /* At present, the stub size is not supplied from the BFD tables. */
+ msect->reserved2 = stub_size;
+
return sec;
}
input_line_pointer++;
sizeof_stub = get_absolute_expression ();
+ specified_mask |= SECT_STUB_SPECIFIED;
}
else if ((specified_mask & SECT_ATTR_SPECIFIED)
&& sectype == BFD_MACH_O_S_SYMBOL_STUBS)
symbolS *sym = NULL;
unsigned int align = 0;
unsigned int specified_mask = 0;
- offsetT size;
+ offsetT size = 0;
#ifdef md_flush_pending_output
md_flush_pending_output ();
bfd_mach_o_asymbol *s = (bfd_mach_o_asymbol *) symbol_get_bfdsym (sym);
bfd_mach_o_section *sec;
int sectype = -1;
- int err = 0;
/* If the symbol is defined, then we can do more rigorous checking on
the validity of the qualifiers. Otherwise, we are stuck with waiting
as_bad (_("'%s' previously declared as '%s'."), s->symbol.name,
(s->n_type & BFD_MACH_O_N_PEXT) ? "private extern"
: "global" );
- err = 1;
+ s->symbol.udata.i = SYM_MACHO_FIELDS_UNSET;
+ return 1;
}
else
{
case OBJ_MACH_O_SYM_PRIV_EXT:
s->n_type |= BFD_MACH_O_N_PEXT ;
+ s->n_desc &= ~LAZY; /* The native tool switches this off too. */
/* We follow the system tools in marking PEXT as also global. */
/* Fall through. */
as_bad (_("'%s' can't be a weak_definition (currently only"
" supported in sections of type coalesced)"),
s->symbol.name);
- err = 1;
+ s->symbol.udata.i = SYM_MACHO_FIELDS_UNSET;
+ return 1;
}
else
s->n_desc |= BFD_MACH_O_N_WEAK_DEF;
/* We've seen some kind of qualifier - check validity if or when the entity
is defined. */
s->symbol.udata.i = SYM_MACHO_FIELDS_NOT_VALIDATED;
- return err;
+ return 0;
}
/* Respond to symbol qualifiers.
demand_empty_rest_of_line ();
}
-/* Dummy function to allow test-code to work while we are working
- on things. */
+typedef struct obj_mach_o_indirect_sym
+{
+ symbolS *sym;
+ segT sect;
+ struct obj_mach_o_indirect_sym *next;
+} obj_mach_o_indirect_sym;
+
+/* We store in order an maintain a pointer to the last one - to save reversing
+ later. */
+obj_mach_o_indirect_sym *indirect_syms;
+obj_mach_o_indirect_sym *indirect_syms_tail;
static void
-obj_mach_o_placeholder (int arg ATTRIBUTE_UNUSED)
+obj_mach_o_indirect_symbol (int arg ATTRIBUTE_UNUSED)
{
- ignore_rest_of_line ();
+ bfd_mach_o_section *sec = bfd_mach_o_get_mach_o_section (now_seg);
+
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
+
+ if (obj_mach_o_is_static)
+ as_bad (_("use of .indirect_symbols requires `-dynamic'"));
+
+ switch (sec->flags & BFD_MACH_O_SECTION_TYPE_MASK)
+ {
+ case BFD_MACH_O_S_SYMBOL_STUBS:
+ case BFD_MACH_O_S_LAZY_SYMBOL_POINTERS:
+ case BFD_MACH_O_S_NON_LAZY_SYMBOL_POINTERS:
+ {
+ obj_mach_o_indirect_sym *isym;
+ char *name = input_line_pointer;
+ char c = get_symbol_end ();
+ symbolS *sym = symbol_find_or_make (name);
+ unsigned int elsize =
+ bfd_mach_o_section_get_entry_size (stdoutput, sec);
+
+ if (elsize == 0)
+ {
+ as_bad (_("attempt to add an indirect_symbol to a stub or"
+ " reference section with a zero-sized element at %s"),
+ name);
+ *input_line_pointer = c;
+ ignore_rest_of_line ();
+ return;
+ }
+ *input_line_pointer = c;
+
+ /* The indirect symbols are validated after the symbol table is
+ frozen, we must make sure that if a local symbol is used as an
+ indirect, it is promoted to a 'real' one. Fetching the bfd sym
+ achieves this. */
+ symbol_get_bfdsym (sym);
+ isym = (obj_mach_o_indirect_sym *)
+ xmalloc (sizeof (obj_mach_o_indirect_sym));
+
+ /* Just record the data for now, we will validate it when we
+ compute the output in obj_mach_o_set_indirect_symbols. */
+ isym->sym = sym;
+ isym->sect = now_seg;
+ isym->next = NULL;
+ if (indirect_syms == NULL)
+ indirect_syms = isym;
+ else
+ indirect_syms_tail->next = isym;
+ indirect_syms_tail = isym;
+ }
+ break;
+
+ default:
+ as_bad (_("an .indirect_symbol must be in a symbol pointer"
+ " or stub section."));
+ ignore_rest_of_line ();
+ return;
+ }
+ demand_empty_rest_of_line ();
}
const pseudo_typeS mach_o_pseudo_table[] =
{"no_dead_strip", obj_mach_o_sym_qual, OBJ_MACH_O_SYM_NO_DEAD_STRIP},
{"weak", obj_mach_o_sym_qual, OBJ_MACH_O_SYM_WEAK}, /* ext */
- {"indirect_symbol", obj_mach_o_placeholder, 0},
+ { "indirect_symbol", obj_mach_o_indirect_symbol, 0},
/* File flags. */
{ "subsections_via_symbols", obj_mach_o_fileprop,
return BFD_MACH_O_N_SECT;
}
+void
+obj_mach_o_frob_colon (const char *name)
+{
+ if (!bfd_is_local_label_name (stdoutput, name))
+ {
+ /* A non-local label will create a new subsection, so start a new
+ frag. */
+ frag_wane (frag_now);
+ frag_new (0);
+ }
+}
+
/* We need to check the correspondence between some kinds of symbols and their
sections. Common and BSS vars will seen via the obj_macho_comm() function.
are possibly incompatible with the section etc. that the symbol is defined
in. */
-void obj_macho_frob_label (struct symbol *sp)
+void obj_mach_o_frob_label (struct symbol *sp)
{
- bfd_mach_o_asymbol *s = (bfd_mach_o_asymbol *) symbol_get_bfdsym (sp);
- /* This is the base symbol type, that we mask in. */
- unsigned base_type = obj_mach_o_type_for_symbol (s);
- bfd_mach_o_section *sec = bfd_mach_o_get_mach_o_section (s->symbol.section);
+ bfd_mach_o_asymbol *s;
+ unsigned base_type;
+ bfd_mach_o_section *sec;
int sectype = -1;
+ if (!bfd_is_local_label_name (stdoutput, S_GET_NAME (sp)))
+ {
+ /* If this is a non-local label, it should have started a new sub-
+ section. */
+ gas_assert (frag_now->obj_frag_data.subsection == NULL);
+ frag_now->obj_frag_data.subsection = sp;
+ }
+
+ /* Leave local symbols alone. */
+
+ if (S_IS_LOCAL (sp))
+ return;
+
+ s = (bfd_mach_o_asymbol *) symbol_get_bfdsym (sp);
+ /* Leave debug symbols alone. */
if ((s->n_type & BFD_MACH_O_N_STAB) != 0)
- return; /* Leave alone. */
-
+ return;
+
+ /* This is the base symbol type, that we mask in. */
+ base_type = obj_mach_o_type_for_symbol (s);
+
+ sec = bfd_mach_o_get_mach_o_section (s->symbol.section);
if (sec != NULL)
sectype = sec->flags & BFD_MACH_O_SECTION_TYPE_MASK;
{
if ((s->n_desc & BFD_MACH_O_N_WEAK_DEF)
&& sectype != BFD_MACH_O_S_COALESCED)
- as_bad (_("'%s' can't be a weak_definition (currently only supported"
- " in sections of type coalesced)"), s->symbol.name);
+ {
+ as_bad (_("'%s' can't be a weak_definition (currently only supported"
+ " in sections of type coalesced)"), s->symbol.name);
+ /* Don't cascade errors. */
+ s->symbol.udata.i = SYM_MACHO_FIELDS_UNSET;
+ }
/* Have we changed from an undefined to defined ref? */
s->n_desc &= ~(REFE | LAZY);
(e.g. global + weak_def). */
int
-obj_macho_frob_symbol (struct symbol *sp)
+obj_mach_o_frob_symbol (struct symbol *sp)
{
- bfd_mach_o_asymbol *s = (bfd_mach_o_asymbol *) symbol_get_bfdsym (sp);
- unsigned base_type = obj_mach_o_type_for_symbol (s);
- bfd_mach_o_section *sec = bfd_mach_o_get_mach_o_section (s->symbol.section);
+ bfd_mach_o_asymbol *s;
+ unsigned base_type;
+ bfd_mach_o_section *sec;
int sectype = -1;
-
+
+ /* Leave local symbols alone. */
+ if (S_IS_LOCAL (sp))
+ return 0;
+
+ s = (bfd_mach_o_asymbol *) symbol_get_bfdsym (sp);
+ /* Leave debug symbols alone. */
+ if ((s->n_type & BFD_MACH_O_N_STAB) != 0)
+ return 0;
+
+ base_type = obj_mach_o_type_for_symbol (s);
+ sec = bfd_mach_o_get_mach_o_section (s->symbol.section);
if (sec != NULL)
sectype = sec->flags & BFD_MACH_O_SECTION_TYPE_MASK;
- if ((s->n_type & BFD_MACH_O_N_STAB) != 0)
- return 0; /* Leave alone. */
- else if (s->symbol.section == bfd_und_section_ptr)
+ if (s->symbol.section == bfd_und_section_ptr)
{
/* ??? Do we really gain much from implementing this as well as the
mach-o specific ones? */
if (s->symbol.flags & BSF_WEAK)
s->n_desc |= BFD_MACH_O_N_WEAK_REF;
- /* Undefined references, become extern. */
- if (s->n_desc & REFE)
- {
- s->n_desc &= ~REFE;
- s->n_type |= BFD_MACH_O_N_EXT;
- }
-
- /* So do undefined 'no_dead_strip's. */
- if (s->n_desc & BFD_MACH_O_N_NO_DEAD_STRIP)
- s->n_type |= BFD_MACH_O_N_EXT;
-
+ /* Undefined syms, become extern. */
+ s->n_type |= BFD_MACH_O_N_EXT;
+ S_SET_EXTERNAL (sp);
+ }
+ else if (s->symbol.section == bfd_com_section_ptr)
+ {
+ /* ... so do comm. */
+ s->n_type |= BFD_MACH_O_N_EXT;
+ S_SET_EXTERNAL (sp);
}
else
{
/* It's a debug symbol. */
s->symbol.flags |= BSF_DEBUGGING;
+
+ /* We've set it - so check it, if you can, but don't try to create the
+ flags. */
+ s->symbol.udata.i = SYM_MACHO_FIELDS_NOT_VALIDATED;
+}
+
+/* This is a place to check for any errors that we can't detect until we know
+ what remains undefined at the end of assembly. */
+
+static void
+obj_mach_o_check_before_writing (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ void *unused ATTRIBUTE_UNUSED)
+{
+ fixS *fixP;
+ struct frchain *frchp;
+ segment_info_type *seginfo = seg_info (sec);
+
+ if (seginfo == NULL)
+ return;
+
+ /* We are not allowed subtractions where either of the operands is
+ undefined. So look through the frags for any fixes to check. */
+ for (frchp = seginfo->frchainP; frchp != NULL; frchp = frchp->frch_next)
+ for (fixP = frchp->fix_root; fixP != NULL; fixP = fixP->fx_next)
+ {
+ if (fixP->fx_addsy != NULL
+ && fixP->fx_subsy != NULL
+ && (! S_IS_DEFINED (fixP->fx_addsy)
+ || ! S_IS_DEFINED (fixP->fx_subsy)))
+ {
+ segT add_symbol_segment = S_GET_SEGMENT (fixP->fx_addsy);
+ segT sub_symbol_segment = S_GET_SEGMENT (fixP->fx_subsy);
+
+ if (! S_IS_DEFINED (fixP->fx_addsy)
+ && S_IS_DEFINED (fixP->fx_subsy))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("`%s' can't be undefined in `%s' - `%s' {%s section}"),
+ S_GET_NAME (fixP->fx_addsy), S_GET_NAME (fixP->fx_addsy),
+ S_GET_NAME (fixP->fx_subsy), segment_name (sub_symbol_segment));
+ }
+ else if (! S_IS_DEFINED (fixP->fx_subsy)
+ && S_IS_DEFINED (fixP->fx_addsy))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("`%s' can't be undefined in `%s' {%s section} - `%s'"),
+ S_GET_NAME (fixP->fx_subsy), S_GET_NAME (fixP->fx_addsy),
+ segment_name (add_symbol_segment), S_GET_NAME (fixP->fx_subsy));
+ }
+ else
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("`%s' and `%s' can't be undefined in `%s' - `%s'"),
+ S_GET_NAME (fixP->fx_addsy), S_GET_NAME (fixP->fx_subsy),
+ S_GET_NAME (fixP->fx_addsy), S_GET_NAME (fixP->fx_subsy));
+ }
+ }
+ }
+}
+
+/* Do any checks that we can't complete without knowing what's undefined. */
+void
+obj_mach_o_pre_output_hook (void)
+{
+ bfd_map_over_sections (stdoutput, obj_mach_o_check_before_writing, (char *) 0);
+}
+
+/* Here we count up frags in each subsection (where a sub-section is defined
+ as starting with a non-local symbol).
+ Note that, if there are no non-local symbols in a section, all the frags will
+ be attached as one anonymous subsection. */
+
+static void
+obj_mach_o_set_subsections (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ void *unused ATTRIBUTE_UNUSED)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ symbolS *cur_subsection = NULL;
+ struct obj_mach_o_symbol_data *cur_subsection_data = NULL;
+ fragS *frag;
+ frchainS *chain;
+
+ /* Protect against sections not created by gas. */
+ if (seginfo == NULL)
+ return;
+
+ /* Attach every frag to a subsection. */
+ for (chain = seginfo->frchainP; chain != NULL; chain = chain->frch_next)
+ for (frag = chain->frch_root; frag != NULL; frag = frag->fr_next)
+ {
+ if (frag->obj_frag_data.subsection == NULL)
+ frag->obj_frag_data.subsection = cur_subsection;
+ else
+ {
+ cur_subsection = frag->obj_frag_data.subsection;
+ cur_subsection_data = symbol_get_obj (cur_subsection);
+ cur_subsection_data->subsection_size = 0;
+ }
+ if (cur_subsection_data != NULL)
+ {
+ /* Update subsection size. */
+ cur_subsection_data->subsection_size += frag->fr_fix;
+ }
+ }
+}
+
+/* Handle mach-o subsections-via-symbols counting up frags belonging to each
+ sub-section. */
+
+void
+obj_mach_o_pre_relax_hook (void)
+{
+ bfd_map_over_sections (stdoutput, obj_mach_o_set_subsections, (char *) 0);
+}
+
+/* Zerofill and GB Zerofill sections must be sorted to follow all other
+ sections in their segments.
+
+ The native 'as' leaves the sections physically in the order they appear in
+ the source, and adjusts the section VMAs to meet the constraint.
+
+ We follow this for now - if nothing else, it makes comparison easier.
+
+ An alternative implementation would be to sort the sections as ld requires.
+ It might be advantageous to implement such a scheme in the future (or even
+ to make the style of section ordering user-selectable). */
+
+typedef struct obj_mach_o_set_vma_data
+{
+ bfd_vma vma;
+ unsigned vma_pass;
+ unsigned zerofill_seen;
+ unsigned gb_zerofill_seen;
+} obj_mach_o_set_vma_data;
+
+/* We do (possibly) three passes through to set the vma, so that:
+
+ zerofill sections get VMAs after all others in their segment
+ GB zerofill get VMAs last.
+
+ As we go, we notice if we see any Zerofill or GB Zerofill sections, so that
+ we can skip the additional passes if there's nothing to do. */
+
+static void
+obj_mach_o_set_section_vma (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *v_p)
+{
+ bfd_mach_o_section *ms = bfd_mach_o_get_mach_o_section (sec);
+ unsigned bfd_align = bfd_get_section_alignment (abfd, sec);
+ obj_mach_o_set_vma_data *p = (struct obj_mach_o_set_vma_data *)v_p;
+ unsigned sectype = (ms->flags & BFD_MACH_O_SECTION_TYPE_MASK);
+ unsigned zf;
+
+ zf = 0;
+ if (sectype == BFD_MACH_O_S_ZEROFILL)
+ {
+ zf = 1;
+ p->zerofill_seen = zf;
+ }
+ else if (sectype == BFD_MACH_O_S_GB_ZEROFILL)
+ {
+ zf = 2;
+ p->gb_zerofill_seen = zf;
+ }
+
+ if (p->vma_pass != zf)
+ return;
+
+ /* We know the section size now - so make a vma for the section just
+ based on order. */
+ ms->size = bfd_get_section_size (sec);
+
+ /* Make sure that the align agrees, and set to the largest value chosen. */
+ ms->align = ms->align > bfd_align ? ms->align : bfd_align;
+ bfd_set_section_alignment (abfd, sec, ms->align);
+
+ p->vma += (1 << ms->align) - 1;
+ p->vma &= ~((1 << ms->align) - 1);
+ ms->addr = p->vma;
+ bfd_set_section_vma (abfd, sec, p->vma);
+ p->vma += ms->size;
+}
+
+/* (potentially) three passes over the sections, setting VMA. We skip the
+ {gb}zerofill passes if we didn't see any of the relevant sections. */
+
+void obj_mach_o_post_relax_hook (void)
+{
+ obj_mach_o_set_vma_data d;
+
+ memset (&d, 0, sizeof (d));
+
+ bfd_map_over_sections (stdoutput, obj_mach_o_set_section_vma, (char *) &d);
+ if ((d.vma_pass = d.zerofill_seen) != 0)
+ bfd_map_over_sections (stdoutput, obj_mach_o_set_section_vma, (char *) &d);
+ if ((d.vma_pass = d.gb_zerofill_seen) != 0)
+ bfd_map_over_sections (stdoutput, obj_mach_o_set_section_vma, (char *) &d);
+}
+
+static void
+obj_mach_o_set_indirect_symbols (bfd *abfd, asection *sec,
+ void *xxx ATTRIBUTE_UNUSED)
+{
+ bfd_vma sect_size = bfd_section_size (abfd, sec);
+ bfd_mach_o_section *ms = bfd_mach_o_get_mach_o_section (sec);
+ unsigned lazy = 0;
+
+ /* See if we have any indirect syms to consider. */
+ if (indirect_syms == NULL)
+ return;
+
+ /* Process indirect symbols.
+ Check for errors, if OK attach them as a flat array to the section
+ for which they are defined. */
+
+ switch (ms->flags & BFD_MACH_O_SECTION_TYPE_MASK)
+ {
+ case BFD_MACH_O_S_SYMBOL_STUBS:
+ case BFD_MACH_O_S_LAZY_SYMBOL_POINTERS:
+ lazy = LAZY;
+ /* Fall through. */
+ case BFD_MACH_O_S_NON_LAZY_SYMBOL_POINTERS:
+ {
+ unsigned int nactual = 0;
+ unsigned int ncalc;
+ obj_mach_o_indirect_sym *isym;
+ obj_mach_o_indirect_sym *list = NULL;
+ obj_mach_o_indirect_sym *list_tail = NULL;
+ unsigned long eltsiz =
+ bfd_mach_o_section_get_entry_size (abfd, ms);
+
+ for (isym = indirect_syms; isym != NULL; isym = isym->next)
+ {
+ if (isym->sect == sec)
+ {
+ nactual++;
+ if (list == NULL)
+ list = isym;
+ else
+ list_tail->next = isym;
+ list_tail = isym;
+ }
+ }
+
+ /* If none are in this section, stop here. */
+ if (nactual == 0)
+ break;
+
+ /* If we somehow added indirect symbols to a section with a zero
+ entry size, we're dead ... */
+ gas_assert (eltsiz != 0);
+
+ ncalc = (unsigned int) (sect_size / eltsiz);
+ if (nactual != ncalc)
+ as_bad (_("the number of .indirect_symbols defined in section %s"
+ " does not match the number expected (%d defined, %d"
+ " expected)"), sec->name, nactual, ncalc);
+ else
+ {
+ unsigned n;
+ bfd_mach_o_asymbol *sym;
+ ms->indirect_syms =
+ bfd_zalloc (abfd,
+ nactual * sizeof (bfd_mach_o_asymbol *));
+
+ if (ms->indirect_syms == NULL)
+ {
+ as_fatal (_("internal error: failed to allocate %d indirect"
+ "symbol pointers"), nactual);
+ }
+
+ for (isym = list, n = 0; isym != NULL; isym = isym->next, n++)
+ {
+ sym = (bfd_mach_o_asymbol *)symbol_get_bfdsym (isym->sym);
+ /* Array is init to NULL & NULL signals a local symbol
+ If the section is lazy-bound, we need to keep the
+ reference to the symbol, since dyld can override.
+
+ Absolute symbols are handled specially. */
+ if (sym->symbol.section == bfd_abs_section_ptr)
+ ms->indirect_syms[n] = sym;
+ else if (S_IS_LOCAL (isym->sym) && ! lazy)
+ ;
+ else
+ {
+ if (sym == NULL)
+ ;
+ /* If the symbols is external ... */
+ else if (S_IS_EXTERNAL (isym->sym)
+ || (sym->n_type & BFD_MACH_O_N_EXT)
+ || ! S_IS_DEFINED (isym->sym)
+ || lazy)
+ {
+ sym->n_desc &= ~LAZY;
+ /* ... it can be lazy, if not defined or hidden. */
+ if ((sym->n_type & BFD_MACH_O_N_TYPE)
+ == BFD_MACH_O_N_UNDF
+ && ! (sym->n_type & BFD_MACH_O_N_PEXT)
+ && (sym->n_type & BFD_MACH_O_N_EXT))
+ sym->n_desc |= lazy;
+ ms->indirect_syms[n] = sym;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* The process of relocation could alter what's externally visible, thus we
+ leave setting the indirect symbols until last. */
+
+void
+obj_mach_o_frob_file_after_relocs (void)
+{
+ bfd_map_over_sections (stdoutput, obj_mach_o_set_indirect_symbols, (char *) 0);
+}
+
+/* Reverse relocations order to make ld happy. */
+
+void
+obj_mach_o_reorder_section_relocs (asection *sec, arelent **rels, unsigned int n)
+{
+ unsigned int i;
+ unsigned int max = n / 2;
+
+ for (i = 0; i < max; i++)
+ {
+ arelent *r = rels[i];
+ rels[i] = rels[n - i - 1];
+ rels[n - i - 1] = r;
+ }
+ bfd_set_reloc (stdoutput, sec, rels, n);
+}
+
+/* Relocation rules are different in frame sections. */
+
+static int
+obj_mach_o_is_frame_section (segT sec)
+{
+ int l;
+ l = strlen (segment_name (sec));
+ if ((l == 9 && strncmp (".eh_frame", segment_name (sec), 9) == 0)
+ || (l == 12 && strncmp (".debug_frame", segment_name (sec), 12) == 0))
+ return 1;
+ return 0;
+}
+
+/* Unless we're in a frame section, we need to force relocs to be generated for
+ local subtractions. We might eliminate them later (if they are within the
+ same sub-section) but we don't know that at the point that this decision is
+ being made. */
+
+int
+obj_mach_o_allow_local_subtract (expressionS * left ATTRIBUTE_UNUSED,
+ expressionS * right ATTRIBUTE_UNUSED,
+ segT seg)
+{
+ /* Don't interfere if it's one of the GAS internal sections. */
+ if (! SEG_NORMAL (seg))
+ return 1;
+
+ /* Allow in frame sections, otherwise emit a reloc. */
+ return obj_mach_o_is_frame_section (seg);
+}
+
+int
+obj_mach_o_in_different_subsection (symbolS *a, symbolS *b)
+{
+ fragS *fa;
+ fragS *fb;
+
+ if (S_GET_SEGMENT (a) != S_GET_SEGMENT (b)
+ || !S_IS_DEFINED (a)
+ || !S_IS_DEFINED (b))
+ {
+ /* Not in the same segment, or undefined symbol. */
+ return 1;
+ }
+
+ fa = symbol_get_frag (a);
+ fb = symbol_get_frag (b);
+ if (fa == NULL || fb == NULL)
+ {
+ /* One of the symbols is not in a subsection. */
+ return 1;
+ }
+
+ return fa->obj_frag_data.subsection != fb->obj_frag_data.subsection;
+}
+
+int
+obj_mach_o_force_reloc_sub_same (fixS *fix, segT seg)
+{
+ if (! SEG_NORMAL (seg))
+ return 1;
+ return obj_mach_o_in_different_subsection (fix->fx_addsy, fix->fx_subsy);
+}
+
+int
+obj_mach_o_force_reloc_sub_local (fixS *fix, segT seg ATTRIBUTE_UNUSED)
+{
+ return obj_mach_o_in_different_subsection (fix->fx_addsy, fix->fx_subsy);
+}
+
+int
+obj_mach_o_force_reloc (fixS *fix)
+{
+ if (generic_force_reloc (fix))
+ return 1;
+
+ /* Force a reloc if the target is not in the same subsection.
+ FIXME: handle (a - b) where a and b belongs to the same subsection ? */
+ if (fix->fx_addsy != NULL)
+ {
+ symbolS *subsec = fix->fx_frag->obj_frag_data.subsection;
+ symbolS *targ = fix->fx_addsy;
+
+ /* There might be no subsections at all. */
+ if (subsec == NULL)
+ return 0;
+
+ if (S_GET_SEGMENT (targ) == absolute_section)
+ return 0;
+
+ return obj_mach_o_in_different_subsection (targ, subsec);
+ }
+ return 0;
}