diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index e0f44ad2c4e..8ceb227b46e 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,5 +1,7 @@
 Fri Nov 12 15:29:36 1993  Jeffrey A. Law  (law@snake.cs.utah.edu)
 
+	* som.c (som_reloc_call): New function.
+
 	* som.c (som_sizeof_headers): Add missing prototype.
 	(som_set_arch_mach): Do not abort.
 
diff --git a/bfd/som.c b/bfd/som.c
index 57a6c98c1b4..53485582367 100644
--- a/bfd/som.c
+++ b/bfd/som.c
@@ -149,6 +149,10 @@ static unsigned char * som_reloc_skip PARAMS ((bfd *, unsigned int,
 static unsigned char * som_reloc_addend PARAMS ((bfd *, int, unsigned char *,
 					         unsigned int *,
 						 struct reloc_queue *));
+static unsigned char * som_reloc_call PARAMS ((bfd *, unsigned char *,
+					       unsigned int *,
+					       arelent *, int,
+					       struct reloc_queue *));
 static unsigned long som_count_spaces PARAMS ((bfd *));
 static unsigned long som_count_subspaces PARAMS ((bfd *));
 static int compare_syms PARAMS ((asymbol **, asymbol **));
@@ -670,6 +674,119 @@ som_reloc_addend (abfd, addend, p, subspace_reloc_sizep, queue)
   return p;
 }
 
+/* Handle a single function call relocation.  */
+
+static unsigned char *
+som_reloc_call (abfd, p, subspace_reloc_sizep, bfd_reloc, sym_num, queue)
+     bfd *abfd;
+     unsigned char *p;
+     unsigned int *subspace_reloc_sizep;
+     arelent *bfd_reloc;
+     int sym_num;
+     struct reloc_queue *queue;
+{
+  int arg_bits = HPPA_R_ARG_RELOC (bfd_reloc->addend);
+  int rtn_bits = arg_bits & 0x3;
+  int type, done = 0;
+  
+  /* You'll never believe all this is necessary to handle relocations
+     for function calls.  Having to compute and pack the argument
+     relocation bits is the real nightmare.
+     
+     If you're interested in how this works, just forget it.  You really
+     do not want to know about this braindamage.  */
+
+  /* First see if this can be done with a "simple" relocation.  Simple
+     relocations have a symbol number < 0x100 and have simple encodings
+     of argument relocations.  */
+
+  if (sym_num < 0x100)
+    {
+      switch (arg_bits)
+	{
+	case 0:
+	case 1:
+	  type = 0;
+	  break;
+	case 1 << 8:
+	case 1 << 8 | 1:
+	  type = 1;
+	  break;
+	case 1 << 8 | 1 << 6:
+	case 1 << 8 | 1 << 6 | 1:
+	  type = 2;
+	  break;
+	case 1 << 8 | 1 << 6 | 1 << 4:
+	case 1 << 8 | 1 << 6 | 1 << 4 | 1:
+	  type = 3;
+	  break;
+	case 1 << 8 | 1 << 6 | 1 << 4 | 1 << 2:
+	case 1 << 8 | 1 << 6 | 1 << 4 | 1 << 2 | 1:
+	  type = 4;
+	  break;
+	default:
+	  /* Not one of the easy encodings.  This will have to be
+	     handled by the more complex code below.  */
+	  type = -1;
+	  break;
+	}
+      if (type != -1)
+	{
+	  /* Account for the return value too.  */
+	  if (rtn_bits)
+	    type += 5;
+
+	  /* Emit a 2 byte relocation.  Then see if it can be handled
+	     with a relocation which is already in the relocation queue.  */
+	  bfd_put_8 (abfd, bfd_reloc->howto->type + type, p);
+	  bfd_put_8 (abfd, sym_num, p + 1);
+	  p = try_prev_fixup (abfd, subspace_reloc_sizep, p, 2, queue);
+	  done = 1;
+	}
+    }
+  
+  /* If this could not be handled with a simple relocation, then do a hard
+     one.  Hard relocations occur if the symbol number was too high or if
+     the encoding of argument relocation bits is too complex.  */
+  if (! done)
+    {
+      /* Don't ask about these magic sequences.  I took them straight
+	 from gas-1.36 which took them from the a.out man page.  */
+      type = rtn_bits;
+      if ((arg_bits >> 6 & 0xf) == 0xe)
+	type += 9 * 40;
+      else
+	type += (3 * (arg_bits >> 8 & 3) + (arg_bits >> 6 & 3)) * 40;
+      if ((arg_bits >> 2 & 0xf) == 0xe)
+	type += 9 * 4;
+      else
+	type += (3 * (arg_bits >> 4 & 3) + (arg_bits >> 2 & 3)) * 4;
+      
+      /* Output the first two bytes of the relocation.  These describe
+	 the length of the relocation and encoding style.  */
+      bfd_put_8 (abfd, bfd_reloc->howto->type + 10
+		 + 2 * (sym_num >= 0x100) + (type >= 0x100),
+		 p);
+      bfd_put_8 (abfd, type, p + 1);
+      
+      /* Now output the symbol index and see if this bizarre relocation
+	 just happened to be in the relocation queue.  */
+      if (sym_num < 0x100)
+	{
+	  bfd_put_8 (abfd, sym_num, p + 2);
+	  p = try_prev_fixup (abfd, subspace_reloc_sizep, p, 3, queue);
+	}
+      else
+	{
+	  bfd_put_8 (abfd, sym_num >> 16, p + 2);
+	  bfd_put_16 (abfd, sym_num, p + 3);
+	  p = try_prev_fixup (abfd, subspace_reloc_sizep, p, 5, queue);
+	}
+    }
+  return p;
+}
+
+
 /* Return the logarithm of X, base 2, considering X unsigned. 
    Abort if X is not a power of two -- this should never happen (FIXME:
    It will happen on corrupt executables.  GDB should give an error, not