]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gas/config/obj-macho.c
Update year range in copyright notice of binutils files
[thirdparty/binutils-gdb.git] / gas / config / obj-macho.c
index 4623384017659ff27bab7833589cac11793861ad..1380707e3c91e253594f334ca430b7fcdef639ac 100644 (file)
@@ -1,5 +1,5 @@
 /* Mach-O object file format
-   Copyright 2009, 2011, 2012 Free Software Foundation, Inc.
+   Copyright (C) 2009-2021 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
+   etc. are mapped onto predefined segment/section pairs using facilities
    supplied by the mach-o port of bfd.
-   
+
    A number of additional mach-o short-hand section switch directives are
    also defined.  */
 
@@ -56,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.  */
@@ -75,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;
        }
@@ -90,9 +91,9 @@ 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
@@ -103,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)
         {
@@ -155,7 +156,7 @@ 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
@@ -165,7 +166,7 @@ obj_mach_o_get_section_names (char *seg, char *sec,
 
 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)
 {
@@ -180,7 +181,7 @@ obj_mach_o_make_or_get_sect (char * segname, char * sectname,
      attributes along with the canonical name.  */
   xlat = bfd_mach_o_section_data_for_mach_sect (stdoutput, segname, sectname);
 
-  /* TODO: more checking of whether overides are acually allowed.  */
+  /* TODO: more checking of whether overrides are actually allowed.  */
 
   if (xlat != NULL)
     {
@@ -191,7 +192,7 @@ obj_mach_o_make_or_get_sect (char * segname, char * sectname,
          if ((sectype == BFD_MACH_O_S_ZEROFILL
               || sectype == BFD_MACH_O_S_GB_ZEROFILL)
              && sectype != usectype)
-           as_bad (_("cannot overide zerofill section type for `%s,%s'"),
+           as_bad (_("cannot override zerofill section type for `%s,%s'"),
                    segname, sectname);
          else
            sectype = usectype;
@@ -205,16 +206,7 @@ obj_mach_o_make_or_get_sect (char * segname, char * sectname,
       /* There is no normal BFD section name for this section.  Create one.
          The name created doesn't really matter as it will never be written
          on disk.  */
-      size_t seglen = strlen (segname);
-      size_t sectlen = strlen (sectname);
-      char *n;
-
-      n = xmalloc (seglen + 1 + sectlen + 1);
-      memcpy (n, segname, seglen);
-      n[seglen] = '.';
-      memcpy (n + seglen + 1, sectname, sectlen);
-      n[seglen + 1 + sectlen] = 0;
-      name = n;
+      name = concat (segname, ".", sectname, (char *) NULL);
       if (specified_mask & SECT_TYPE_SPECIFIED)
        sectype = usectype;
       else
@@ -235,7 +227,7 @@ obj_mach_o_make_or_get_sect (char * segname, char * sectname,
   /* Sub-segments don't exists as is on Mach-O.  */
   sec = subseg_new (name, 0);
 
-  oldflags = bfd_get_section_flags (stdoutput, sec);
+  oldflags = bfd_section_flags (sec);
   msect = bfd_mach_o_get_mach_o_section (sec);
 
   if (oldflags == SEC_NO_FLAGS)
@@ -246,24 +238,26 @@ obj_mach_o_make_or_get_sect (char * segname, char * sectname,
          && (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))
+      if (!bfd_set_section_flags (sec, flags))
        as_warn (_("failed to set flags for \"%s\": %s"),
-                bfd_section_name (stdoutput, sec),
+                bfd_section_name (sec),
                 bfd_errmsg (bfd_get_error ()));
-      strncpy (msect->segname, segname, sizeof (msect->segname));
-      strncpy (msect->sectname, sectname, sizeof (msect->sectname));
+
+      strncpy (msect->segname, segname, BFD_MACH_O_SEGNAME_SIZE);
+      msect->segname[BFD_MACH_O_SEGNAME_SIZE] = 0;
+      strncpy (msect->sectname, sectname, BFD_MACH_O_SECTNAME_SIZE);
+      msect->sectname[BFD_MACH_O_SECTNAME_SIZE] = 0;
 
       msect->align = secalign;
       msect->flags = sectype | secattr;
-      
+
       if (sectype == BFD_MACH_O_S_ZEROFILL
          || sectype == BFD_MACH_O_S_GB_ZEROFILL)
         seg_info (sec)->bss = 1;
@@ -290,7 +284,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 ",,").
 
@@ -316,7 +310,7 @@ obj_mach_o_section (int ignore ATTRIBUTE_UNUSED)
   md_flush_pending_output ();
 #endif
 
-  /* Get the User's segment annd section names.  */
+  /* Get the User's segment and section names.  */
   if (! obj_mach_o_get_section_names (segname, sectname, 17, 17))
     return;
 
@@ -400,7 +394,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)
@@ -414,7 +408,7 @@ obj_mach_o_section (int ignore ATTRIBUTE_UNUSED)
               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"));
@@ -424,7 +418,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,25 +451,24 @@ obj_mach_o_zerofill (int ignore ATTRIBUTE_UNUSED)
   md_flush_pending_output ();
 #endif
 
-  /* Get the User's segment annd section names.  */
+  /* Get the User's segment and section names.  */
   if (! obj_mach_o_get_section_names (segname, sectname, 17, 17))
     return;
 
   /* 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;
@@ -487,7 +480,7 @@ obj_mach_o_zerofill (int ignore ATTRIBUTE_UNUSED)
          goto done;
        }
 
-      SKIP_WHITESPACE ();  
+      SKIP_WHITESPACE_AFTER_NAME ();
       if (*input_line_pointer == ',')
        input_line_pointer++;
 
@@ -507,7 +500,7 @@ obj_mach_o_zerofill (int ignore ATTRIBUTE_UNUSED)
        }
 
       size = exp.X_add_number;
-      size &= ((offsetT) 2 << (stdoutput->arch_info->bits_per_address - 1)) - 1;
+      size &= ((valueT) 2 << (stdoutput->arch_info->bits_per_address - 1)) - 1;
       if (exp.X_add_number != size || !exp.X_unsigned)
        {
          as_warn (_("size (%ld) out of range, ignored"),
@@ -534,8 +527,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);
@@ -556,7 +549,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*/);
@@ -594,12 +587,12 @@ obj_mach_o_zerofill (int ignore ATTRIBUTE_UNUSED)
        S_CLEAR_EXTERNAL (sym);
     }
 
-done:
+ done:
   /* switch back to the section that was current before the .zerofill.  */
   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;
@@ -629,7 +622,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;
     }
@@ -713,7 +706,7 @@ static void
 obj_mach_o_objc_section (int sect_index)
 {
   segT section;
-  
+
 #ifdef md_flush_pending_output
   md_flush_pending_output ();
 #endif
@@ -773,7 +766,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;
@@ -897,7 +890,7 @@ obj_mach_o_common_parse (int is_local, symbolS *symbolP,
   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.  */
@@ -922,7 +915,7 @@ obj_mach_o_common_parse (int is_local, symbolS *symbolP,
       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;
@@ -961,17 +954,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 ();
@@ -981,7 +974,7 @@ obj_mach_o_fileprop (int prop)
     }
 }
 
-/* Temporary markers for symbol reference data.  
+/* Temporary markers for symbol reference data.
    Lazy will remain in place.  */
 #define LAZY 0x01
 #define REFE 0x02
@@ -1012,12 +1005,11 @@ obj_mach_o_set_symbol_qualifier (symbolS *sym, int type)
   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 
+     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.  */
@@ -1040,7 +1032,8 @@ obj_mach_o_set_symbol_qualifier (symbolS *sym, int type)
            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
          {
@@ -1091,7 +1084,8 @@ obj_mach_o_set_symbol_qualifier (symbolS *sym, int type)
            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;
@@ -1110,7 +1104,7 @@ obj_mach_o_set_symbol_qualifier (symbolS *sym, int type)
     /* 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.
@@ -1131,12 +1125,11 @@ obj_mach_o_sym_qual (int ntype)
 
   do
     {
-      name = input_line_pointer;
-      c = get_symbol_end ();
+      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 ();
+      SKIP_WHITESPACE_AFTER_NAME ();
       c = *input_line_pointer;
       if (c == ',')
        {
@@ -1182,8 +1175,8 @@ obj_mach_o_indirect_symbol (int arg ATTRIBUTE_UNUSED)
       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 ();
+         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);
@@ -1193,19 +1186,18 @@ obj_mach_o_indirect_symbol (int arg ATTRIBUTE_UNUSED)
              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;
+             (void) restore_line_pointer (c);
              ignore_rest_of_line ();
              return;
-         }
-         *input_line_pointer = c;
+           }
+         (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 
+         /* 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));
+         isym = XNEW (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.  */
@@ -1293,7 +1285,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.  */
@@ -1323,7 +1315,7 @@ const pseudo_typeS mach_o_pseudo_table[] =
   { "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}
@@ -1343,11 +1335,23 @@ obj_mach_o_type_for_symbol (bfd_mach_o_asymbol *s)
     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
@@ -1357,13 +1361,21 @@ obj_mach_o_type_for_symbol (bfd_mach_o_asymbol *s)
    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;
   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))
@@ -1377,7 +1389,7 @@ void obj_macho_frob_label (struct symbol *sp)
   /* 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);  
+  sec = bfd_mach_o_get_mach_o_section (s->symbol.section);
   if (sec != NULL)
     sectype = sec->flags & BFD_MACH_O_SECTION_TYPE_MASK;
 
@@ -1388,8 +1400,12 @@ void obj_macho_frob_label (struct symbol *sp)
     {
       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);
@@ -1404,7 +1420,7 @@ void obj_macho_frob_label (struct symbol *sp)
    (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;
   unsigned base_type;
@@ -1421,7 +1437,7 @@ obj_macho_frob_symbol (struct symbol *sp)
     return 0;
 
   base_type = obj_mach_o_type_for_symbol (s);
-  sec = bfd_mach_o_get_mach_o_section (s->symbol.section);  
+  sec = bfd_mach_o_get_mach_o_section (s->symbol.section);
   if (sec != NULL)
     sectype = sec->flags & BFD_MACH_O_SECTION_TYPE_MASK;
 
@@ -1459,8 +1475,7 @@ obj_macho_frob_symbol (struct symbol *sp)
     {
       /* Anything here that should be added that is non-standard.  */
       s->n_desc &= ~BFD_MACH_O_REFERENCE_MASK;
-      s->symbol.udata.i = SYM_MACHO_FIELDS_NOT_VALIDATED;
-    }    
+    }
   else if (s->symbol.udata.i == SYM_MACHO_FIELDS_NOT_VALIDATED)
     {
       /* Try to validate any combinations.  */
@@ -1495,17 +1510,159 @@ obj_macho_frob_symbol (struct symbol *sp)
   return 0;
 }
 
-/* Relocation rules are different in frame sections.  */
+/* Support stabs for mach-o.  */
 
-static int
-obj_mach_o_is_frame_section (segT sec)
+void
+obj_mach_o_process_stab (int what, const char *string,
+                        int type, int other, int desc)
 {
-  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;
+  symbolS *symbolP;
+  bfd_mach_o_asymbol *s;
+
+  switch (what)
+    {
+      case 'd':
+       symbolP = symbol_new ("", now_seg, frag_now, frag_now_fix ());
+       /* Special stabd NULL name indicator.  */
+       S_SET_NAME (symbolP, NULL);
+       break;
+
+      case 'n':
+      case 's':
+       symbolP = symbol_new (string, undefined_section,
+                             &zero_address_frag, 0);
+       pseudo_set (symbolP);
+       break;
+
+      default:
+       as_bad(_("unrecognized stab type '%c'"), (char)what);
+       abort ();
+       break;
+    }
+
+  s = (bfd_mach_o_asymbol *) symbol_get_bfdsym (symbolP);
+  s->n_type = type;
+  s->n_desc = desc;
+  /* For stabd, this will eventually get overwritten by the section number.  */
+  s->n_sect = other;
+
+  /* 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
@@ -1513,7 +1670,7 @@ obj_mach_o_is_frame_section (segT sec)
 
    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.
@@ -1532,7 +1689,7 @@ typedef struct obj_mach_o_set_vma_data
 
    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.  */
 
@@ -1540,7 +1697,7 @@ 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);
+  unsigned bfd_align = bfd_section_alignment (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;
@@ -1562,20 +1719,20 @@ obj_mach_o_set_section_vma (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *v_p
 
   /* We know the section size now - so make a vma for the section just
      based on order.  */
-  ms->size = bfd_get_section_size (sec);
-  
+  ms->size = bfd_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);
-  
+  bfd_set_section_alignment (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);
+  bfd_set_section_vma (sec, p->vma);
   p->vma += ms->size;
 }
 
-/* (potentially) three passes over the sections, setting VMA.  We skip the 
+/* (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)
@@ -1583,7 +1740,7 @@ 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);
@@ -1595,7 +1752,7 @@ 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_vma sect_size = bfd_section_size (sec);
   bfd_mach_o_section *ms = bfd_mach_o_get_mach_o_section (sec);
   unsigned lazy = 0;
 
@@ -1620,7 +1777,7 @@ obj_mach_o_set_indirect_symbols (bfd *abfd, asection *sec,
          obj_mach_o_indirect_sym *isym;
          obj_mach_o_indirect_sym *list = NULL;
          obj_mach_o_indirect_sym *list_tail = NULL;
-         unsigned long eltsiz = 
+         unsigned long eltsiz =
                        bfd_mach_o_section_get_entry_size (abfd, ms);
 
          for (isym = indirect_syms; isym != NULL; isym = isym->next)
@@ -1653,26 +1810,36 @@ obj_mach_o_set_indirect_symbols (bfd *abfd, asection *sec,
            {
              unsigned n;
              bfd_mach_o_asymbol *sym;
+
+             /* FIXME: It seems that there can be more indirect symbols
+                than is computed by the loop above.  So be paranoid and
+                allocate enough space for every symbol to be indirect.
+                See PR 21939 for an example of where this is needed.  */
+             if (nactual < bfd_get_symcount (abfd))
+               nactual = bfd_get_symcount (abfd);
+
              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);
-               }
-             
+               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;
+                   {
+                     if (n >= nactual)
+                       as_fatal (_("internal error: more indirect mach-o symbols than expected"));
+                     ms->indirect_syms[n] = sym;
+                   }
                  else if (S_IS_LOCAL (isym->sym) && ! lazy)
                    ;
                  else
@@ -1687,11 +1854,13 @@ obj_mach_o_set_indirect_symbols (bfd *abfd, asection *sec,
                        {
                          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 
+                         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;
+                         if (n >= nactual)
+                           as_fatal (_("internal error: more indirect mach-o symbols than expected"));
                          ms->indirect_syms[n] = sym;
                        }
                    }
@@ -1731,44 +1900,17 @@ obj_mach_o_reorder_section_relocs (asection *sec, arelent **rels, unsigned int n
   bfd_set_reloc (stdoutput, sec, rels, n);
 }
 
-/* Support stabs for mach-o.  */
+/* Relocation rules are different in frame sections.  */
 
-void
-obj_mach_o_process_stab (int what, const char *string,
-                        int type, int other, int desc)
+static int
+obj_mach_o_is_frame_section (segT sec)
 {
-  symbolS *symbolP;
-  bfd_mach_o_asymbol *s;
-
-  switch (what)
-    {
-      case 'd':
-       symbolP = symbol_new ("", now_seg, frag_now_fix (), frag_now);
-       /* Special stabd NULL name indicator.  */
-       S_SET_NAME (symbolP, NULL);
-       break;
-
-      case 'n':
-      case 's':
-       symbolP = symbol_new (string, undefined_section, (valueT) 0,
-                             &zero_address_frag);
-       pseudo_set (symbolP);
-       break;
-
-      default:
-       as_bad(_("unrecognized stab type '%c'"), (char)what);
-       abort ();
-       break;
-    }
-
-  s = (bfd_mach_o_asymbol *) symbol_get_bfdsym (symbolP);
-  s->n_type = type;
-  s->n_desc = desc;
-  /* For stabd, this will eventually get overwritten by the section number.  */
-  s->n_sect = other;
-
-  /* It's a debug symbol.  */
-  s->symbol.flags |= BSF_DEBUGGING;
+  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
@@ -1777,7 +1919,7 @@ obj_mach_o_process_stab (int what, const char *string,
    being made.  */
 
 int
-obj_mach_o_allow_local_subtract (expressionS * left ATTRIBUTE_UNUSED, 
+obj_mach_o_allow_local_subtract (expressionS * left ATTRIBUTE_UNUSED,
                                 expressionS * right ATTRIBUTE_UNUSED,
                                 segT seg)
 {
@@ -1788,3 +1930,67 @@ obj_mach_o_allow_local_subtract (expressionS * left ATTRIBUTE_UNUSED,
   /* 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;
+}