use xstrdup and friends more
[deliverable/binutils-gdb.git] / gas / config / obj-macho.c
index b5237678ab3a8d617b94caf416c02e756a0588f6..f823b5ce431ccc3bd4dac5c7961c8b47ec47be51 100644 (file)
@@ -1,5 +1,5 @@
 /* Mach-O object file format
-   Copyright 2009, 2011, 2012 Free Software Foundation, Inc.
+   Copyright (C) 2009-2016 Free Software Foundation, Inc.
 
    This file is part of GAS, the GNU Assembler.
 
    decorations.  */
 
 /* Mach-O supports multiple, named segments each of which may contain
-   multiple named sections.  Thus the concept of subsectioning is 
+   multiple named sections.  Thus the concept of subsectioning is
    handled by (say) having a __TEXT segment with appropriate flags from
-   which subsections are generated like __text, __const etc.  
-   
+   which subsections are generated like __text, __const etc.
+
    The well-known as short-hand section switch directives like .text, .data
    etc. are mapped onto predefined segment/section pairs using facilites
    supplied by the mach-o port of bfd.
-   
+
    A number of additional mach-o short-hand section switch directives are
    also defined.  */
 
@@ -45,6 +45,8 @@
 #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);
 
@@ -54,6 +56,7 @@ static int obj_mach_o_is_static;
 
 /* 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.  */
@@ -73,7 +76,7 @@ mach_o_begin (void)
       subseg_set (text_section, 0);
       if (obj_mach_o_is_static)
        {
-         bfd_mach_o_section *mo_sec 
+         bfd_mach_o_section *mo_sec
                        = bfd_mach_o_get_mach_o_section (text_section);
          mo_sec->flags &= ~BFD_MACH_O_S_ATTR_PURE_INSTRUCTIONS;
        }
@@ -82,40 +85,15 @@ mach_o_begin (void)
 
 /* Remember the subsections_by_symbols state in case we need to reset
    the file flags.  */
-static int obj_mach_o_subsections_by_symbols;
-
-static void
-obj_mach_o_weak (int ignore ATTRIBUTE_UNUSED)
-{
-  char *name;
-  int c;
-  symbolS *symbolP;
-
-  do
-    {
-      /* Get symbol name.  */
-      name = input_line_pointer;
-      c = get_symbol_end ();
-      symbolP = symbol_find_or_make (name);
-      S_SET_WEAK (symbolP);
-      *input_line_pointer = c;
-      SKIP_WHITESPACE ();
 
-      if (c != ',')
-        break;
-      input_line_pointer++;
-      SKIP_WHITESPACE ();
-    }
-  while (*input_line_pointer != '\n');
-  demand_empty_rest_of_line ();
-}
+static int obj_mach_o_subsections_by_symbols;
 
 /* This will put at most 16 characters (terminated by a ',' or newline) from
    the input stream into dest.  If there are more than 16 chars before the
    delimiter, a warning is given and the string is truncated.  On completion of
-   this function, input_line_pointer will point to the char after the ',' or 
-   to the newline.  
-   
+   this function, input_line_pointer will point to the char after the ',' or
+   to the newline.
+
    It trims leading and trailing space.  */
 
 static int
@@ -126,14 +104,14 @@ collect_16char_name (char *dest, const char *msg, int require_comma)
   SKIP_WHITESPACE ();
   namstart = input_line_pointer;
 
-  while ( (c = *input_line_pointer) != ',' 
+  while ( (c = *input_line_pointer) != ','
         && !is_end_of_line[(unsigned char) c])
     input_line_pointer++;
 
   {
       int len = input_line_pointer - namstart; /* could be zero.  */
-      /* lose any trailing space.  */  
-      while (len > 0 && namstart[len-1] == ' ') 
+      /* lose any trailing space.  */
+      while (len > 0 && namstart[len-1] == ' ')
         len--;
       if (len > 16)
         {
@@ -178,16 +156,17 @@ obj_mach_o_get_section_names (char *seg, char *sec,
 
 /* Build (or get) a section from the mach-o description - which includes
    optional definitions for type, attributes, alignment and stub size.
-   
+
    BFD supplies default values for sections which have a canonical name.  */
 
 #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,
-                            unsigned int specified_mask, 
+                            unsigned int specified_mask,
                             unsigned int usectype, unsigned int usecattr,
                             unsigned int ualign, offsetT stub_size)
 {
@@ -262,19 +241,30 @@ 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"),
                 bfd_section_name (stdoutput, sec),
                 bfd_errmsg (bfd_get_error ()));
+
       strncpy (msect->segname, segname, sizeof (msect->segname));
       strncpy (msect->sectname, sectname, sizeof (msect->sectname));
 
       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)
         seg_info (sec)->bss = 1;
@@ -286,6 +276,10 @@ obj_mach_o_make_or_get_sect (char * segname, char * sectname,
        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;
 }
 
@@ -297,7 +291,7 @@ obj_mach_o_make_or_get_sect (char * segname, char * sectname,
    White space is allowed everywhere between elements.
 
    <segment> and <section> may be from 0 to 16 chars in length - they may
-   contain spaces but leading and trailing space will be trimmed.  It is 
+   contain spaces but leading and trailing space will be trimmed.  It is
    mandatory that they be present (or that zero-length names are indicated
    by ",,").
 
@@ -407,7 +401,7 @@ obj_mach_o_section (int ignore ATTRIBUTE_UNUSED)
           while (*input_line_pointer == '+');
 
           /* Parse sizeof_stub.  */
-          if ((specified_mask & SECT_ATTR_SPECIFIED) 
+          if ((specified_mask & SECT_ATTR_SPECIFIED)
              && *input_line_pointer == ',')
             {
               if (sectype != BFD_MACH_O_S_SYMBOL_STUBS)
@@ -419,8 +413,9 @@ obj_mach_o_section (int ignore ATTRIBUTE_UNUSED)
 
              input_line_pointer++;
               sizeof_stub = get_absolute_expression ();
+              specified_mask |= SECT_STUB_SPECIFIED;
             }
-          else if ((specified_mask & SECT_ATTR_SPECIFIED) 
+          else if ((specified_mask & SECT_ATTR_SPECIFIED)
                   && sectype == BFD_MACH_O_S_SYMBOL_STUBS)
             {
               as_bad (_("missing sizeof_stub expression"));
@@ -430,7 +425,7 @@ obj_mach_o_section (int ignore ATTRIBUTE_UNUSED)
         }
     }
 
-  new_seg = obj_mach_o_make_or_get_sect (segname, sectname, specified_mask, 
+  new_seg = obj_mach_o_make_or_get_sect (segname, sectname, specified_mask,
                                         sectype, secattr, 0 /*align */,
                                         sizeof_stub);
   if (new_seg != NULL)
@@ -457,7 +452,7 @@ obj_mach_o_zerofill (int ignore ATTRIBUTE_UNUSED)
   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 ();
@@ -470,18 +465,17 @@ obj_mach_o_zerofill (int ignore ATTRIBUTE_UNUSED)
   /* Parse variable definition, if present.  */
   if (*input_line_pointer == ',')
     {
-      /* Parse symbol, size [.align] 
+      /* Parse symbol, size [.align]
          We follow the method of s_common_internal, with the difference
          that the symbol cannot be a duplicate-common.  */
       char *name;
       char c;
       char *p;
       expressionS exp;
-  
+
       input_line_pointer++; /* Skip ',' */
       SKIP_WHITESPACE ();
-      name = input_line_pointer;
-      c = get_symbol_end ();
+      c = get_symbol_name (&name);
       /* Just after name is now '\0'.  */
       p = input_line_pointer;
       *p = c;
@@ -493,7 +487,7 @@ obj_mach_o_zerofill (int ignore ATTRIBUTE_UNUSED)
          goto done;
        }
 
-      SKIP_WHITESPACE ();  
+      SKIP_WHITESPACE_AFTER_NAME ();
       if (*input_line_pointer == ',')
        input_line_pointer++;
 
@@ -540,8 +534,8 @@ obj_mach_o_zerofill (int ignore ATTRIBUTE_UNUSED)
                   name, (long) size, (long) exp.X_add_number);
 
       *p = c;  /* Restore the termination char.  */
-      
-      SKIP_WHITESPACE ();  
+
+      SKIP_WHITESPACE ();
       if (*input_line_pointer == ',')
        {
          align = (unsigned int) parse_align (0);
@@ -562,7 +556,7 @@ obj_mach_o_zerofill (int ignore ATTRIBUTE_UNUSED)
  /* else just a section definition.  */
 
   specified_mask |= SECT_TYPE_SPECIFIED;
-  new_seg = obj_mach_o_make_or_get_sect (segname, sectname, specified_mask, 
+  new_seg = obj_mach_o_make_or_get_sect (segname, sectname, specified_mask,
                                         BFD_MACH_O_S_ZEROFILL,
                                         BFD_MACH_O_S_ATTR_NONE,
                                         align, (offsetT) 0 /*stub size*/);
@@ -605,7 +599,7 @@ done:
   subseg_set (old_seg, 0);
 }
 
-static segT 
+static segT
 obj_mach_o_segT_from_bfd_name (const char *nam, int must_succeed)
 {
   const mach_o_section_name_xlat *xlat;
@@ -635,7 +629,7 @@ obj_mach_o_segT_from_bfd_name (const char *nam, int must_succeed)
       msect->flags = xlat->macho_sectype | xlat->macho_secattr;
       msect->align = xlat->sectalign;
 
-      if ((msect->flags & BFD_MACH_O_SECTION_TYPE_MASK) 
+      if ((msect->flags & BFD_MACH_O_SECTION_TYPE_MASK)
          == BFD_MACH_O_S_ZEROFILL)
        seg_info (sec)->bss = 1;
     }
@@ -719,7 +713,7 @@ static void
 obj_mach_o_objc_section (int sect_index)
 {
   segT section;
-  
+
 #ifdef md_flush_pending_output
   md_flush_pending_output ();
 #endif
@@ -779,7 +773,7 @@ obj_mach_o_debug_section (int sect_index)
 /* This could be moved to the tc-xx files, but there is so little dependency
    there, that the code might as well be shared.  */
 
-struct opt_tgt_sect 
+struct opt_tgt_sect
 {
  const char *name;
  unsigned x86_val;
@@ -901,8 +895,9 @@ obj_mach_o_common_parse (int is_local, symbolS *symbolP,
                         addressT size)
 {
   addressT align = 0;
+  bfd_mach_o_asymbol *s;
 
-  SKIP_WHITESPACE ();  
+  SKIP_WHITESPACE ();
 
   /* Both comm and lcomm take an optional alignment, as a power
      of two between 1 and 15.  */
@@ -920,15 +915,17 @@ obj_mach_o_common_parse (int is_local, symbolS *symbolP,
        }
     }
 
+  s = (bfd_mach_o_asymbol *) symbol_get_bfdsym (symbolP);
   if (is_local)
     {
       /* Create the BSS section on demand.  */
       if (bss_section == NULL)
        {
          bss_section = obj_mach_o_segT_from_bfd_name (BSS_SECTION_NAME, 1);
-         seg_info (bss_section)->bss = 1;        
+         seg_info (bss_section)->bss = 1;
        }
       bss_alloc (symbolP, size, align);
+      s->n_type = BFD_MACH_O_N_SECT;
       S_CLEAR_EXTERNAL (symbolP);
     }
   else
@@ -937,9 +934,14 @@ obj_mach_o_common_parse (int is_local, symbolS *symbolP,
       S_SET_ALIGN (symbolP, align);
       S_SET_EXTERNAL (symbolP);
       S_SET_SEGMENT (symbolP, bfd_com_section_ptr);
+      s->n_type = BFD_MACH_O_N_UNDF | BFD_MACH_O_N_EXT;
     }
 
-  symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT;
+  /* This is a data object (whatever we choose that to mean).  */
+  s->symbol.flags |= BSF_OBJECT;
+
+  /* We've set symbol qualifiers, so validate if you can.  */
+  s->symbol.udata.i = SYM_MACHO_FIELDS_NOT_VALIDATED;
 
   return symbolP;
 }
@@ -959,17 +961,17 @@ typedef enum obj_mach_o_file_properties {
   OBJ_MACH_O_FILE_PROP_MAX
 } obj_mach_o_file_properties;
 
-static void 
+static void
 obj_mach_o_fileprop (int prop)
 {
   if (prop < 0 || prop >= OBJ_MACH_O_FILE_PROP_MAX)
     as_fatal (_("internal error: bad file property ID %d"), prop);
-    
+
   switch ((obj_mach_o_file_properties) prop)
     {
       case OBJ_MACH_O_FILE_PROP_SUBSECTS_VIA_SYMS:
         obj_mach_o_subsections_by_symbols = 1;
-       if (!bfd_set_private_flags (stdoutput, 
+       if (!bfd_set_private_flags (stdoutput,
                                    BFD_MACH_O_MH_SUBSECTIONS_VIA_SYMBOLS))
          as_bad (_("failed to set subsections by symbols"));
        demand_empty_rest_of_line ();
@@ -979,13 +981,252 @@ obj_mach_o_fileprop (int prop)
     }
 }
 
-/* Dummy function to allow test-code to work while we are working
-   on things.  */
+/* Temporary markers for symbol reference data.
+   Lazy will remain in place.  */
+#define LAZY 0x01
+#define REFE 0x02
+
+/* We have a bunch of qualifiers that may be applied to symbols.
+   .globl is handled here so that we might make sure that conflicting qualifiers
+   are caught where possible.  */
+
+typedef enum obj_mach_o_symbol_type {
+  OBJ_MACH_O_SYM_UNK = 0,
+  OBJ_MACH_O_SYM_LOCAL = 1,
+  OBJ_MACH_O_SYM_GLOBL = 2,
+  OBJ_MACH_O_SYM_REFERENCE = 3,
+  OBJ_MACH_O_SYM_WEAK_REF = 4,
+  OBJ_MACH_O_SYM_LAZY_REF = 5,
+  OBJ_MACH_O_SYM_WEAK_DEF = 6,
+  OBJ_MACH_O_SYM_PRIV_EXT = 7,
+  OBJ_MACH_O_SYM_NO_DEAD_STRIP = 8,
+  OBJ_MACH_O_SYM_WEAK = 9
+} obj_mach_o_symbol_type;
+
+/* Set Mach-O-specific symbol qualifiers. */
+
+static int
+obj_mach_o_set_symbol_qualifier (symbolS *sym, int type)
+{
+  int is_defined;
+  bfd_mach_o_asymbol *s = (bfd_mach_o_asymbol *) symbol_get_bfdsym (sym);
+  bfd_mach_o_section *sec;
+  int sectype = -1;
+
+  /* If the symbol is defined, then we can do more rigorous checking on
+     the validity of the qualifiers.  Otherwise, we are stuck with waiting
+     until it's defined - or until write the file.
+
+     In certain cases (e.g. when a symbol qualifier is intended to introduce
+     an undefined symbol in a stubs section) we should check that the current
+     section is appropriate to the qualifier.  */
+
+  is_defined = s->symbol.section != bfd_und_section_ptr;
+  if (is_defined)
+    sec = bfd_mach_o_get_mach_o_section (s->symbol.section) ;
+  else
+    sec = bfd_mach_o_get_mach_o_section (now_seg) ;
+
+  if (sec != NULL)
+    sectype = sec->flags & BFD_MACH_O_SECTION_TYPE_MASK;
+
+  switch ((obj_mach_o_symbol_type) type)
+    {
+      case OBJ_MACH_O_SYM_LOCAL:
+       /* This is an extension over the system tools.  */
+        if (s->n_type & (BFD_MACH_O_N_PEXT | BFD_MACH_O_N_EXT))
+         {
+           as_bad (_("'%s' previously declared as '%s'."), s->symbol.name,
+                     (s->n_type & BFD_MACH_O_N_PEXT) ? "private extern"
+                                                     : "global" );
+           s->symbol.udata.i = SYM_MACHO_FIELDS_UNSET;
+           return 1;
+         }
+       else
+         {
+           s->n_type &= ~BFD_MACH_O_N_EXT;
+           S_CLEAR_EXTERNAL (sym);
+         }
+       break;
+
+      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.  */
+
+      case OBJ_MACH_O_SYM_GLOBL:
+       /* It's not an error to define a symbol and then make it global.  */
+       s->n_type |= BFD_MACH_O_N_EXT;
+       S_SET_EXTERNAL (sym);
+       break;
+
+      case OBJ_MACH_O_SYM_REFERENCE:
+        if (is_defined)
+          s->n_desc |= BFD_MACH_O_N_NO_DEAD_STRIP;
+        else
+          s->n_desc |= (REFE | BFD_MACH_O_N_NO_DEAD_STRIP);
+       break;
+
+      case OBJ_MACH_O_SYM_LAZY_REF:
+        if (is_defined)
+          s->n_desc |= BFD_MACH_O_N_NO_DEAD_STRIP;
+        else
+          s->n_desc |= (REFE | LAZY | BFD_MACH_O_N_NO_DEAD_STRIP);
+       break;
+
+      /* Force ld to retain the symbol - even if it appears unused.  */
+      case OBJ_MACH_O_SYM_NO_DEAD_STRIP:
+       s->n_desc |= BFD_MACH_O_N_NO_DEAD_STRIP ;
+       break;
+
+      /* Mach-O's idea of weak ...  */
+      case OBJ_MACH_O_SYM_WEAK_REF:
+       s->n_desc |= BFD_MACH_O_N_WEAK_REF ;
+       break;
+
+      case OBJ_MACH_O_SYM_WEAK_DEF:
+       if (is_defined && 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);
+           s->symbol.udata.i = SYM_MACHO_FIELDS_UNSET;
+           return 1;
+         }
+       else
+         s->n_desc |= BFD_MACH_O_N_WEAK_DEF;
+       break;
+
+      case OBJ_MACH_O_SYM_WEAK:
+        /* A generic 'weak' - we try to figure out what it means at
+          symbol frob time.  */
+       S_SET_WEAK (sym);
+       break;
+
+      default:
+       break;
+    }
+
+    /* 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 0;
+}
+
+/* Respond to symbol qualifiers.
+   All of the form:
+   .<qualifier> symbol [, symbol]*
+   a list of symbols is an extension over the Darwin system as.  */
 
 static void
-obj_mach_o_placeholder (int arg ATTRIBUTE_UNUSED)
+obj_mach_o_sym_qual (int ntype)
 {
-  ignore_rest_of_line ();
+  char *name;
+  char c;
+  symbolS *symbolP;
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+  do
+    {
+      c = get_symbol_name (&name);
+      symbolP = symbol_find_or_make (name);
+      obj_mach_o_set_symbol_qualifier (symbolP, ntype);
+      *input_line_pointer = c;
+      SKIP_WHITESPACE_AFTER_NAME ();
+      c = *input_line_pointer;
+      if (c == ',')
+       {
+         input_line_pointer++;
+         SKIP_WHITESPACE ();
+         if (is_end_of_line[(unsigned char) *input_line_pointer])
+           c = '\n';
+       }
+    }
+  while (c == ',');
+
+  demand_empty_rest_of_line ();
+}
+
+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_indirect_symbol (int arg ATTRIBUTE_UNUSED)
+{
+  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;
+         char c = get_symbol_name (&name);
+         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);
+             (void) restore_line_pointer (c);
+             ignore_rest_of_line ();
+             return;
+           }
+         (void) restore_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[] =
@@ -1052,7 +1293,7 @@ const pseudo_typeS mach_o_pseudo_table[] =
   { "debug_str", obj_mach_o_debug_section, 10}, /* extension.  */
   { "debug_ranges", obj_mach_o_debug_section, 11}, /* extension.  */
   { "debug_macro", obj_mach_o_debug_section, 12}, /* extension.  */
-  
+
   { "lazy_symbol_pointer", obj_mach_o_opt_tgt_section, 1},
   { "lazy_symbol_pointer2", obj_mach_o_opt_tgt_section, 2}, /* extension.  */
   { "lazy_symbol_pointer3", obj_mach_o_opt_tgt_section, 3}, /* extension.  */
@@ -1068,19 +1309,215 @@ const pseudo_typeS mach_o_pseudo_table[] =
   { "section", obj_mach_o_section, 0},
   { "zerofill", obj_mach_o_zerofill, 0},
 
-  /* Symbol-related.  */
-  { "indirect_symbol", obj_mach_o_placeholder, 0},
-  { "weak_definition", obj_mach_o_placeholder, 0},
-  { "private_extern", obj_mach_o_placeholder, 0},
-  { "weak", obj_mach_o_weak, 0},   /* extension */
+  /* Symbol qualifiers.  */
+  {"local",            obj_mach_o_sym_qual, OBJ_MACH_O_SYM_LOCAL},
+  {"globl",            obj_mach_o_sym_qual, OBJ_MACH_O_SYM_GLOBL},
+  {"reference",                obj_mach_o_sym_qual, OBJ_MACH_O_SYM_REFERENCE},
+  {"weak_reference",   obj_mach_o_sym_qual, OBJ_MACH_O_SYM_WEAK_REF},
+  {"lazy_reference",   obj_mach_o_sym_qual, OBJ_MACH_O_SYM_LAZY_REF},
+  {"weak_definition",  obj_mach_o_sym_qual, OBJ_MACH_O_SYM_WEAK_DEF},
+  {"private_extern",   obj_mach_o_sym_qual, OBJ_MACH_O_SYM_PRIV_EXT},
+  {"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_indirect_symbol, 0},
 
   /* File flags.  */
-  { "subsections_via_symbols", obj_mach_o_fileprop, 
+  { "subsections_via_symbols", obj_mach_o_fileprop,
                               OBJ_MACH_O_FILE_PROP_SUBSECTS_VIA_SYMS},
 
   {NULL, NULL, 0}
 };
 
+/* Determine the default n_type value for a symbol from its section.  */
+
+static unsigned
+obj_mach_o_type_for_symbol (bfd_mach_o_asymbol *s)
+{
+  if (s->symbol.section == bfd_abs_section_ptr)
+    return BFD_MACH_O_N_ABS;
+  else if (s->symbol.section == bfd_com_section_ptr
+          || s->symbol.section == bfd_und_section_ptr)
+    return BFD_MACH_O_N_UNDF;
+  else
+    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.
+
+   The earlier we can pick up a problem, the better the diagnostics will be.
+
+   However, when symbol type information is attached, the symbol section will
+   quite possibly be unknown.  So we are stuck with checking (most of the)
+   validity at the time the file is written (unfortunately, then one doesn't
+   get line number information in the diagnostic).  */
+
+/* Here we pick up the case where symbol qualifiers have been applied that
+   are possibly incompatible with the section etc. that the symbol is defined
+   in.  */
+
+void obj_mach_o_frob_label (struct symbol *sp)
+{
+  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;
+
+  /* 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 there is a pre-existing qualifier, we can make some checks about
+     validity now.  */
+
+  if(s->symbol.udata.i == SYM_MACHO_FIELDS_NOT_VALIDATED)
+    {
+      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);
+         /* 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);
+    }
+
+  s->n_type &= ~BFD_MACH_O_N_TYPE;
+  s->n_type |= base_type;
+}
+
+/* This is the fall-back, we come here when we get to the end of the file and
+   the symbol is not defined - or there are combinations of qualifiers required
+   (e.g. global + weak_def).  */
+
+int
+obj_mach_o_frob_symbol (struct symbol *sp)
+{
+  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->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 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
+    {
+      if ((s->symbol.flags & BSF_WEAK)
+          && (sectype == BFD_MACH_O_S_COALESCED)
+          && (s->n_type & (BFD_MACH_O_N_PEXT | BFD_MACH_O_N_EXT)))
+       s->n_desc |= BFD_MACH_O_N_WEAK_DEF;
+/* ??? we should do this - but then that reveals that the semantics of weak
+       are different from what's supported in mach-o object files.
+      else
+       as_bad (_("'%s' can't be a weak_definition."),
+               s->symbol.name); */
+    }
+
+  if (s->symbol.udata.i == SYM_MACHO_FIELDS_UNSET)
+    {
+      /* Anything here that should be added that is non-standard.  */
+      s->n_desc &= ~BFD_MACH_O_REFERENCE_MASK;
+    }
+  else if (s->symbol.udata.i == SYM_MACHO_FIELDS_NOT_VALIDATED)
+    {
+      /* Try to validate any combinations.  */
+      if (s->n_desc & BFD_MACH_O_N_WEAK_DEF)
+       {
+         if (s->symbol.section == bfd_und_section_ptr)
+           as_bad (_("'%s' can't be a weak_definition (since it is"
+                     " undefined)"), s->symbol.name);
+         else if (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);
+         else if (! (s->n_type & (BFD_MACH_O_N_PEXT | BFD_MACH_O_N_EXT)))
+           as_bad (_("Non-global symbol: '%s' can't be a weak_definition."),
+                   s->symbol.name);
+       }
+
+    }
+  else
+    as_bad (_("internal error: [%s] unexpected code [%lx] in frob symbol"),
+           s->symbol.name, (unsigned long)s->symbol.udata.i);
+
+  s->n_type &= ~BFD_MACH_O_N_TYPE;
+  s->n_type |= base_type;
+
+  if (s->symbol.flags & BSF_GLOBAL)
+    s->n_type |= BFD_MACH_O_N_EXT;
+
+  /* This cuts both ways - we promote some things to external above.  */
+  if (s->n_type & (BFD_MACH_O_N_PEXT | BFD_MACH_O_N_EXT))
+    S_SET_EXTERNAL (sp);
+
+  return 0;
+}
+
 /* Support stabs for mach-o.  */
 
 void
@@ -1119,4 +1556,437 @@ obj_mach_o_process_stab (int what, const char *string,
 
   /* 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;
 }
This page took 0.052458 seconds and 4 git commands to generate.