* config/tc-ppc.c (md_assemble): When handling @l, always sign
authorIan Lance Taylor <ian@airs.com>
Fri, 24 Oct 1997 21:29:10 +0000 (21:29 +0000)
committerIan Lance Taylor <ian@airs.com>
Fri, 24 Oct 1997 21:29:10 +0000 (21:29 +0000)
extend if the operand expects a signed value.
PR 13667.

gas/ChangeLog
gas/config/tc-ppc.c

index 6339f86..5a5eddb 100644 (file)
@@ -1,5 +1,8 @@
 Fri Oct 24 15:56:47 1997  Ian Lance Taylor  <ian@cygnus.com>
 
+       * config/tc-ppc.c (md_assemble): When handling @l, always sign
+       extend if the operand expects a signed value.
+
        * config/tc-mips.h (LOCAL_LABELS_DOLLAR): Don't define; use
        default which is to permit dollar labels.
 
index 873a8d1..4f8771e 100644 (file)
@@ -1852,7 +1852,11 @@ md_assemble (str)
                break;
 
              case BFD_RELOC_LO16:
-               if (ex.X_unsigned)
+               /* X_unsigned is the default, so if the user has done
+                   something which cleared it, we always produce a
+                   signed value.  */
+               if (ex.X_unsigned
+                   && (operand->flags & PPC_OPERAND_SIGNED) == 0)
                  ex.X_add_number &= 0xffff;
                else
                  ex.X_add_number = (((ex.X_add_number & 0xffff)
@@ -2424,6 +2428,7 @@ ppc_change_csect (sym)
     {
       symbolS **list_ptr;
       int after_toc;
+      int hold_chunksize;
       symbolS *list;
 
       /* This is a new csect.  We need to look at the symbol class to
@@ -2464,7 +2469,16 @@ ppc_change_csect (sym)
          abort ();
        }
 
+      /* We set the obstack chunk size to a small value before
+         changing subsegments, so that we don't use a lot of memory
+         space for what may be a small section.  */
+      hold_chunksize = chunksize;
+      chunksize = 64;
+
       subseg_new (segment_name (S_GET_SEGMENT (sym)), sym->sy_tc.subseg);
+
+      chunksize = hold_chunksize;
+
       if (after_toc)
        ppc_after_toc_frag = frag_now;
 
@@ -4005,7 +4019,7 @@ ppc_frob_symbol (sym)
       ppc_last_function = sym;
       if (sym->sy_tc.size != (symbolS *) NULL)
        {
-         resolve_symbol_value (sym->sy_tc.size);
+         resolve_symbol_value (sym->sy_tc.size, 1);
          SA_SET_SYM_FSIZE (sym, (long) S_GET_VALUE (sym->sy_tc.size));
        }
     }
@@ -4064,7 +4078,7 @@ ppc_frob_symbol (sym)
                                     - S_GET_VALUE (sym));
          else
            {
-             resolve_symbol_value (sym->sy_tc.next);
+             resolve_symbol_value (sym->sy_tc.next, 1);
              a->x_csect.x_scnlen.l = (S_GET_VALUE (sym->sy_tc.next)
                                       - S_GET_VALUE (sym));
            }
@@ -4117,7 +4131,7 @@ ppc_frob_symbol (sym)
            }
          else
            {
-             resolve_symbol_value (next);
+             resolve_symbol_value (next, 1);
              a->x_csect.x_scnlen.l = (S_GET_VALUE (next)
                                       - S_GET_VALUE (sym));
            }
@@ -4148,7 +4162,7 @@ ppc_frob_symbol (sym)
            {
              while (csect->sy_tc.next != (symbolS *) NULL)
                {
-                 resolve_symbol_value (csect->sy_tc.next);
+                 resolve_symbol_value (csect->sy_tc.next, 1);
                  if (S_GET_VALUE (csect->sy_tc.next) > S_GET_VALUE (sym))
                    break;
                  csect = csect->sy_tc.next;
@@ -4189,7 +4203,7 @@ ppc_frob_symbol (sym)
       /* The value is the offset from the enclosing csect.  */
       block = sym->sy_tc.within;
       csect = block->sy_tc.within;
-      resolve_symbol_value (csect);
+      resolve_symbol_value (csect, 1);
       S_SET_VALUE (sym, S_GET_VALUE (sym) - S_GET_VALUE (csect));
     }
   else if (S_GET_STORAGE_CLASS (sym) == C_BINCL
@@ -4414,7 +4428,7 @@ ppc_fix_adjustable (fix)
 {
   valueT val;
 
-  resolve_symbol_value (fix->fx_addsy);
+  resolve_symbol_value (fix->fx_addsy, 1);
   val = S_GET_VALUE (fix->fx_addsy);
   if (ppc_toc_csect != (symbolS *) NULL
       && fix->fx_addsy != (symbolS *) NULL
@@ -4434,7 +4448,7 @@ ppc_fix_adjustable (fix)
            continue;
          if (sy->sy_tc.class != XMC_TC)
            break;
-         resolve_symbol_value (sy);
+         resolve_symbol_value (sy, 1);
          if (val == S_GET_VALUE (sy))
            {
              fix->fx_addsy = sy;
@@ -4513,7 +4527,7 @@ ppc_fix_adjustable (fix)
       && S_GET_SEGMENT (fix->fx_addsy) == bss_section
       && ! S_IS_EXTERNAL (fix->fx_addsy))
     {
-      resolve_symbol_value (fix->fx_addsy->sy_frag->fr_symbol);
+      resolve_symbol_value (fix->fx_addsy->sy_frag->fr_symbol, 1);
       fix->fx_offset += (S_GET_VALUE (fix->fx_addsy)
                         - S_GET_VALUE (fix->fx_addsy->sy_frag->fr_symbol));
       fix->fx_addsy = fix->fx_addsy->sy_frag->fr_symbol;
@@ -4798,9 +4812,35 @@ md_apply_fix3 (fixp, valuep, seg)
 
        case BFD_RELOC_24_PLT_PCREL:
        case BFD_RELOC_PPC_LOCAL24PC:
-         if (!fixp->fx_pcrel)
+         if (!fixp->fx_pcrel && !fixp->fx_done)
            abort ();
 
+         if (fixp->fx_done)
+         {
+           char *where;
+           unsigned long insn;
+           
+           /* Fetch the instruction, insert the fully resolved operand
+              value, and stuff the instruction back again.  */
+           where = fixp->fx_frag->fr_literal + fixp->fx_where;
+           if (target_big_endian)
+             insn = bfd_getb32 ((unsigned char *) where);
+           else
+             insn = bfd_getl32 ((unsigned char *) where);
+           if ((value & 3) != 0)
+             as_bad_where (fixp->fx_file, fixp->fx_line,
+                           "must branch to an address a multiple of 4");
+           if ((long)value << 6 >> 6 != value)
+             as_bad_where (fixp->fx_file, fixp->fx_line,
+                           "@local or @plt branch destination is too far "
+                           "away, %ld bytes",
+                           value);
+           insn = insn | (value & 0x03fffffc);
+           if (target_big_endian)
+             bfd_putb32 ((bfd_vma) insn, (unsigned char *) where);
+           else
+             bfd_putl32 ((bfd_vma) insn, (unsigned char *) where);
+         }
          break;
 
        default: