diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 180f363dc54..2473d6ef3b1 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,5 +1,9 @@
 2015-12-08  DJ Delorie  <dj@redhat.com>
 
+	* elf32-rl78.c (rl78_offset_for_reloc): Add more relocs.
+	(rl78_elf_relax_section): Add bc/bz/bnc/bnz/bh/bnh.  Fix reloc
+	choices.
+
 	* elf32-rx.c (rx_elf_object_p): Ignore empty and nobits sections.
 
 2015-12-07  Nick Clifton  <nickc@redhat.com>
diff --git a/bfd/elf32-rl78.c b/bfd/elf32-rl78.c
index 723cb4b07d4..cf192bf0e14 100644
--- a/bfd/elf32-rl78.c
+++ b/bfd/elf32-rl78.c
@@ -1974,7 +1974,15 @@ rl78_offset_for_reloc (bfd *                    abfd,
 
 	default:
 	reloc_computes_value:
-	  symval = rl78_compute_complex_reloc (r_type, 0, input_section);
+	  symval = rl78_compute_complex_reloc (r_type, symval, input_section);
+	case R_RL78_DIR32:
+	case R_RL78_DIR24S:
+	case R_RL78_DIR16:
+	case R_RL78_DIR16U:
+	case R_RL78_DIR16S:
+	case R_RL78_DIR24S_PCREL:
+	case R_RL78_DIR16S_PCREL:
+	case R_RL78_DIR8S_PCREL:
 	  if (lrel)
 	    *lrel = rel;
 	  return symval;
@@ -2316,6 +2324,27 @@ rl78_elf_relax_section
 
 	  switch (insn[0])
 	    {
+	    case 0xdc: /* BC */
+	    case 0xdd: /* BZ */
+	    case 0xde: /* BNC */
+	    case 0xdf: /* BNZ */
+	      if (insn[1] == 0x03 && insn[2] == 0xee /* BR */
+		  && (srel->r_offset - irel->r_offset) > 1) /* a B<c> without its own reloc */
+		{
+		  /* This is a "long" conditional as generated by gas:
+		     DC 03 EE ad.dr  */
+		  if (pcrel < 127
+		      && pcrel > -127)
+		    {
+		      insn[0] ^= 0x02; /* invert conditional */
+		      SNIPNR (4, 1);
+		      SNIP (1, 2, R_RL78_DIR8S_PCREL);
+		      insn[1] = pcrel;
+		      *again = TRUE;
+		    }
+		}
+	      break;
+
 	    case 0xec: /* BR !!abs20 */
 
 	      if (pcrel < 127
@@ -2331,7 +2360,7 @@ rl78_elf_relax_section
 		  insn[0] = 0xed;
 		  insn[1] = symval & 0xff;
 		  insn[2] = symval >> 8;
-		  SNIP (2, 1, R_RL78_DIR16S);
+		  SNIP (2, 1, R_RL78_DIR16U);
 		  *again = TRUE;
 		}
 	      else if (pcrel < 32767
@@ -2363,7 +2392,7 @@ rl78_elf_relax_section
 		  insn[0] = 0xfd;
 		  insn[1] = symval & 0xff;
 		  insn[2] = symval >> 8;
-		  SNIP (2, 1, R_RL78_DIR16S);
+		  SNIP (2, 1, R_RL78_DIR16U);
 		  *again = TRUE;
 		}
 	      else if (pcrel < 32767
@@ -2386,6 +2415,25 @@ rl78_elf_relax_section
 		 here anyway. */
 	      switch (insn[1])
 		{
+		case 0xd3: /* BNH */
+		case 0xc3: /* BH */
+		  if (insn[2] == 0x03 && insn[3] == 0xee
+		      && (srel->r_offset - irel->r_offset) > 2) /* a B<c> without its own reloc */
+		    {
+		      /* Another long branch by gas:
+			 61 D3 03 EE ad.dr  */
+		      if (pcrel < 127
+			  && pcrel > -127)
+			{
+			  insn[1] ^= 0x10; /* invert conditional */
+			  SNIPNR (5, 1);
+			  SNIP (2, 2, R_RL78_DIR8S_PCREL);
+			  insn[2] = pcrel;
+			  *again = TRUE;
+			}
+		    }
+		  break;
+
 		case 0xc8: /* SKC */
 		  if (insn[2] == 0xef)
 		    {
diff --git a/gas/ChangeLog b/gas/ChangeLog
index 8a50708d67c..6fb81a1acaf 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,3 +1,23 @@
+2015-12-08  DJ Delorie  <dj@redhat.com>
+
+	* config/rl78-parse.y: Make all branches relaxable via
+	rl78_linkrelax_branch().
+	* config/tc-rl78.c (rl78_linkrelax_branch): Mark all relaxable
+	branches with relocs.
+	(options): Add OPTION_NORELAX.
+	(md_longopts): Add -mnorelax.
+	(md_parse_option): Support OPTION_NORELAX.
+	(op_type_T): Add bh, sk, call, and br.
+	(rl78_opcode_type): Likewise.
+	(rl78_relax_frag): Fix not-relaxing logic.  Add sk.
+	(md_convert_frag): Fix relocation handling.
+	(tc_gen_reloc): Strip relax relocs when not linker relaxing.
+	(md_apply_fix): Defer overflow handling for anything that needs a
+	PLT, to the linker.
+	* config/tc-rl78.h (TC_FORCE_RELOCATION): Force all relocations to
+	the linker when linker relaxing.
+	* doc/c-rl78.texi (norelax): Add.
+
 2015-12-07  Alan Modra  <amodra@gmail.com>
 
 	* config/tc-ppc.c (md_apply_fix): Localize variables.  Reduce casts.
diff --git a/gas/config/rl78-parse.y b/gas/config/rl78-parse.y
index b8795815f7b..ff017cfb140 100644
--- a/gas/config/rl78-parse.y
+++ b/gas/config/rl78-parse.y
@@ -294,22 +294,22 @@ statement :
 /* ---------------------------------------------------------------------- */
 
 	| BC '$' EXPR
-	  { B1 (0xdc); PC1 ($3); rl78_relax (RL78_RELAX_BRANCH, 0); }
+	  { B1 (0xdc); PC1 ($3); rl78_linkrelax_branch (); }
 
 	| BNC '$' EXPR
-	  { B1 (0xde); PC1 ($3); rl78_relax (RL78_RELAX_BRANCH, 0); }
+	  { B1 (0xde); PC1 ($3); rl78_linkrelax_branch (); }
 
 	| BZ '$' EXPR
-	  { B1 (0xdd); PC1 ($3); rl78_relax (RL78_RELAX_BRANCH, 0); }
+	  { B1 (0xdd); PC1 ($3); rl78_linkrelax_branch (); }
 
 	| BNZ '$' EXPR
-	  { B1 (0xdf); PC1 ($3); rl78_relax (RL78_RELAX_BRANCH, 0); }
+	  { B1 (0xdf); PC1 ($3); rl78_linkrelax_branch (); }
 
 	| BH '$' EXPR
-	  { B2 (0x61, 0xc3); PC1 ($3); rl78_relax (RL78_RELAX_BRANCH, 0); }
+	  { B2 (0x61, 0xc3); PC1 ($3); rl78_linkrelax_branch (); }
 
 	| BNH '$' EXPR
-	  { B2 (0x61, 0xd3); PC1 ($3); rl78_relax (RL78_RELAX_BRANCH, 0); }
+	  { B2 (0x61, 0xd3); PC1 ($3); rl78_linkrelax_branch (); }
 
 /* ---------------------------------------------------------------------- */
 
@@ -337,7 +337,7 @@ statement :
 	  { B2 (0x61, 0xcb); }
 
 	| BR '$' EXPR
-	  { B1 (0xef); PC1 ($3); }
+	  { B1 (0xef); PC1 ($3); rl78_linkrelax_branch (); }
 
 	| BR '$' '!' EXPR
 	  { B1 (0xee); PC2 ($4); rl78_linkrelax_branch (); }
@@ -1022,22 +1022,22 @@ statement :
 /* ---------------------------------------------------------------------- */
 
 	| SKC
-	  { B2 (0x61, 0xc8); rl78_linkrelax_branch (); }
+	  { B2 (0x61, 0xc8); rl78_relax (RL78_RELAX_BRANCH, 0); }
 
 	| SKH
-	  { B2 (0x61, 0xe3); rl78_linkrelax_branch (); }
+	  { B2 (0x61, 0xe3); rl78_relax (RL78_RELAX_BRANCH, 0); }
 
 	| SKNC
-	  { B2 (0x61, 0xd8); rl78_linkrelax_branch (); }
+	  { B2 (0x61, 0xd8); rl78_relax (RL78_RELAX_BRANCH, 0); }
 
 	| SKNH
-	  { B2 (0x61, 0xf3); rl78_linkrelax_branch (); }
+	  { B2 (0x61, 0xf3); rl78_relax (RL78_RELAX_BRANCH, 0); }
 
 	| SKNZ
-	  { B2 (0x61, 0xf8); rl78_linkrelax_branch (); }
+	  { B2 (0x61, 0xf8); rl78_relax (RL78_RELAX_BRANCH, 0); }
 
 	| SKZ
-	  { B2 (0x61, 0xe8); rl78_linkrelax_branch (); }
+	  { B2 (0x61, 0xe8); rl78_relax (RL78_RELAX_BRANCH, 0); }
 
 /* ---------------------------------------------------------------------- */
 
@@ -1161,8 +1161,8 @@ andor1	: AND1 { $$ = 0x05; rl78_bit_insn = 1; }
 	| XOR1 { $$ = 0x07; rl78_bit_insn = 1; }
 	;
 
-bt_bf	: BT { $$ = 0x02;    rl78_bit_insn = 1; rl78_relax (RL78_RELAX_BRANCH, 0); }
-	| BF { $$ = 0x04;    rl78_bit_insn = 1; rl78_relax (RL78_RELAX_BRANCH, 0); }
+bt_bf	: BT { $$ = 0x02;    rl78_bit_insn = 1; rl78_linkrelax_branch (); }
+	| BF { $$ = 0x04;    rl78_bit_insn = 1; rl78_linkrelax_branch (); }
 	| BTCLR { $$ = 0x00; rl78_bit_insn = 1; }
 	;
 
diff --git a/gas/config/tc-rl78.c b/gas/config/tc-rl78.c
index 9fbaa42b5a7..ad04795c13c 100644
--- a/gas/config/tc-rl78.c
+++ b/gas/config/tc-rl78.c
@@ -102,6 +102,7 @@ rl78_linkrelax_addr16 (void)
 void
 rl78_linkrelax_branch (void)
 {
+  rl78_relax (RL78_RELAX_BRANCH, 0);
   rl78_bytes.link_relax |= RL78_RELAXA_BRA;
 }
 
@@ -280,6 +281,7 @@ rl78_field (int val, int pos, int sz)
 enum options
 {
   OPTION_RELAX = OPTION_MD_BASE,
+  OPTION_NORELAX,
   OPTION_G10,
   OPTION_G13,
   OPTION_G14,
@@ -294,6 +296,7 @@ const char * md_shortopts = RL78_SHORTOPTS;
 struct option md_longopts[] =
 {
   {"relax", no_argument, NULL, OPTION_RELAX},
+  {"norelax", no_argument, NULL, OPTION_NORELAX},
   {"mg10", no_argument, NULL, OPTION_G10},
   {"mg13", no_argument, NULL, OPTION_G13},
   {"mg14", no_argument, NULL, OPTION_G14},
@@ -312,6 +315,9 @@ md_parse_option (int c, char * arg ATTRIBUTE_UNUSED)
     case OPTION_RELAX:
       linkrelax = 1;
       return 1;
+    case OPTION_NORELAX:
+      linkrelax = 0;
+      return 1;
 
     case OPTION_G10:
       elf_flags &= ~ E_FLAG_RL78_CPU_MASK;
@@ -757,7 +763,10 @@ typedef enum
   OT_bt_sfr,
   OT_bt_es,
   OT_bc,
-  OT_bh
+  OT_bh,
+  OT_sk,
+  OT_call,
+  OT_br,
 } op_type_T;
 
 /* We're looking for these types of relaxations:
@@ -780,8 +789,10 @@ typedef enum
    a different size later.  */
 
 static op_type_T
-rl78_opcode_type (char * op)
+rl78_opcode_type (char * ops)
 {
+  unsigned char *op = (unsigned char *)ops;
+
   if (op[0] == 0x31
       && ((op[1] & 0x0f) == 0x05
 	  || (op[1] & 0x0f) == 0x03))
@@ -805,6 +816,20 @@ rl78_opcode_type (char * op)
       && (op[1] & 0xef) == 0xc3)
     return OT_bh;
 
+  if (op[0] == 0x61
+      && (op[1] & 0xcf) == 0xc8)
+    return OT_sk;
+
+  if (op[0] == 0x61
+      && (op[1] & 0xef) == 0xe3)
+    return OT_sk;
+
+  if (op[0] == 0xfc)
+    return OT_call;
+
+  if ((op[0] & 0xec) == 0xec)
+    return OT_br;
+
   return OT_other;
 }
 
@@ -901,6 +926,11 @@ rl78_relax_frag (segT segment ATTRIBUTE_UNUSED, fragS * fragP, long stretch)
 			   fragP->tc_frag_data->relax[ri].type != RL78_RELAX_BRANCH,
 			   & sym_addr))
     {
+      /* If we don't expect the linker to do relaxing, don't emit
+	 expanded opcodes that only the linker will relax.  */
+      if (!linkrelax)
+	return newsize - oldsize;
+
       /* If we don't, we must use the maximum size for the linker.  */
       switch (fragP->tc_frag_data->relax[ri].type)
 	{
@@ -920,7 +950,10 @@ rl78_relax_frag (segT segment ATTRIBUTE_UNUSED, fragS * fragP, long stretch)
 	    case OT_bh:
 	      newsize = 6;
 	      break;
-	    case OT_other:
+	    case OT_sk:
+	      newsize = 2;
+	      break;
+	    default:
 	      newsize = oldsize;
 	      break;
 	    }
@@ -967,7 +1000,10 @@ rl78_relax_frag (segT segment ATTRIBUTE_UNUSED, fragS * fragP, long stretch)
 	  else
 	    newsize = 6;
 	  break;
-	case OT_other:
+	case OT_sk:
+	  newsize = 2;
+	  break;
+	default:
 	  newsize = oldsize;
 	  break;
 	}
@@ -1062,6 +1098,7 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
 	case OPCODE (OT_bt, 3): /* BT A,$ - no change.  */
 	  disp -= 3;
 	  op[2] = disp;
+	  reloc_type = keep_reloc ? BFD_RELOC_8_PCREL : BFD_RELOC_NONE;
 	  break;
 
 	case OPCODE (OT_bt, 6): /* BT A,$ - long version.  */
@@ -1079,6 +1116,7 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
 	case OPCODE (OT_bt_sfr, 4): /* BT PSW,$ - no change.  */
 	  disp -= 4;
 	  op[3] = disp;
+	  reloc_type = keep_reloc ? BFD_RELOC_8_PCREL : BFD_RELOC_NONE;
 	  break;
 
 	case OPCODE (OT_bt_sfr, 7): /* BT PSW,$ - long version.  */
@@ -1096,6 +1134,7 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
 	case OPCODE (OT_bt_es, 4): /* BT ES:[HL],$ - no change.  */
 	  disp -= 4;
 	  op[3] = disp;
+	  reloc_type = keep_reloc ? BFD_RELOC_8_PCREL : BFD_RELOC_NONE;
 	  break;
 
 	case OPCODE (OT_bt_es, 7): /* BT PSW,$ - long version.  */
@@ -1113,6 +1152,7 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
 	case OPCODE (OT_bc, 2): /* BC $ - no change.  */
 	  disp -= 2;
 	  op[1] = disp;
+	  reloc_type = keep_reloc ? BFD_RELOC_8_PCREL : BFD_RELOC_NONE;
 	  break;
 
 	case OPCODE (OT_bc, 5): /* BC $ - long version.  */
@@ -1130,6 +1170,7 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
 	case OPCODE (OT_bh, 3): /* BH $ - no change.  */
 	  disp -= 3;
 	  op[2] = disp;
+	  reloc_type = keep_reloc ? BFD_RELOC_8_PCREL : BFD_RELOC_NONE;
 	  break;
 
 	case OPCODE (OT_bh, 6): /* BC $ - long version.  */
@@ -1144,11 +1185,13 @@ md_convert_frag (bfd *   abfd ATTRIBUTE_UNUSED,
 	  reloc_adjust = 2;
 	  break;
 
-	default:
-	  fprintf(stderr, "Missed case %d %d at 0x%lx\n",
-		  rl78_opcode_type (fragP->fr_opcode), fragP->fr_subtype, mypc);
-	  abort ();
+	case OPCODE (OT_sk, 2): /* SK<cond> - no change */
+	  reloc_type = keep_reloc ? BFD_RELOC_16_PCREL : BFD_RELOC_NONE;
+	  break;
 
+	default:
+	  reloc_type = fix ? fix->fx_r_type : BFD_RELOC_NONE;
+	  break;
 	}
       break;
 
@@ -1215,6 +1258,12 @@ tc_gen_reloc (asection * seg ATTRIBUTE_UNUSED, fixS * fixp)
       return reloc;
     }
 
+  if (fixp->fx_r_type == BFD_RELOC_RL78_RELAX && !linkrelax)
+    {
+      reloc[0] = NULL;
+      return reloc;
+    }
+
   if (fixp->fx_subsy
       && S_GET_SEGMENT (fixp->fx_subsy) == absolute_section)
     {
@@ -1376,6 +1425,11 @@ md_apply_fix (struct fix * f ATTRIBUTE_UNUSED,
   char * op;
   unsigned long val;
 
+  /* We always defer overflow checks for these to the linker, as it
+     needs to do PLT stuff.  */
+  if (f->fx_r_type == BFD_RELOC_RL78_CODE)
+    f->fx_no_overflow = 1;
+
   if (f->fx_addsy && S_FORCE_RELOC (f->fx_addsy, 1))
     return;
   if (f->fx_subsy && S_FORCE_RELOC (f->fx_subsy, 1))
@@ -1384,13 +1438,16 @@ md_apply_fix (struct fix * f ATTRIBUTE_UNUSED,
   op = f->fx_frag->fr_literal + f->fx_where;
   val = (unsigned long) * t;
 
+  if (f->fx_addsy == NULL)
+    f->fx_done = 1;
+
   switch (f->fx_r_type)
     {
     case BFD_RELOC_NONE:
       break;
 
     case BFD_RELOC_RL78_RELAX:
-      f->fx_done = 1;
+      f->fx_done = 0;
       break;
 
     case BFD_RELOC_8_PCREL:
@@ -1461,8 +1518,6 @@ md_apply_fix (struct fix * f ATTRIBUTE_UNUSED,
       break;
     }
 
-  if (f->fx_addsy == NULL)
-    f->fx_done = 1;
 }
 
 valueT
diff --git a/gas/config/tc-rl78.h b/gas/config/tc-rl78.h
index b9ede61674c..82f798ec350 100644
--- a/gas/config/tc-rl78.h
+++ b/gas/config/tc-rl78.h
@@ -99,3 +99,5 @@ extern void rl78_elf_final_processing (void);
    || TC_FORCE_RELOCATION (FIX))
 
 #define DWARF2_USE_FIXED_ADVANCE_PC 1
+
+#define TC_FORCE_RELOCATION(FIX) (linkrelax)
diff --git a/gas/doc/c-rl78.texi b/gas/doc/c-rl78.texi
index 5cb568f3ec2..49e4ee41738 100644
--- a/gas/doc/c-rl78.texi
+++ b/gas/doc/c-rl78.texi
@@ -28,6 +28,9 @@
 @item relax
 Enable support for link-time relaxation.
 
+@item norelax
+Disable support for link-time relaxation (default).
+
 @item mg10
 Mark the generated binary as targeting the G10 variant of the RL78
 architecture.