This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[patch][rfc] Complex Relocations gas and bfd


Hi,

The attached patch contains work which is used by the soon-to-be-submitted port for Toshiba's MeP architecture, however, it is general enough to be considered separately. It is an implementation of so-called 'complex relocations'. It allows a fairly rich set of expressions on symbols to be specified in the assembler and resolved by the linker. Below are some notes which were written by Graydon Hoare, the author of the work.

I'm seeking approval to commit.

Dave
----------------------------------------------------------------------------------------------------------------------

the assembler and linker have been modified to support "IEE695-like"
relocations. these are intended to permit arbitrary assembly
expressions, rather than classical symbol+offset relocs, to be saved
for the linker and evaluated at link time.

* the expressions are necessarily communicated by two channels:

** #1: BFD_RELOC_RELC (unsigned) and BFD_RELOC_SRELC (signed) relocs
  themslelves, which we call "complex relocs", which specify the
  beginning bit number, width, word size, chunk size, truncation
  flag, lsb0 value, etc. which are values of the target bit field the
  reloc is supposed to act on.

*** these values are encoded, packed, in the "addend" word (32 bits)
   of a classical ELF reloc with an external reloc type R_RELC or
   R_SRELC.

** #2: the BSF_RELC / STT_RELC (unsigned) and BSF_SRELC / STT_SRELC
  (signed) symbol types, which we call "complex symbols", which
  encode the expression *in* the symbol name as a prefix-notation
  string.

*** the encoding grammar is as follows:

  <node> := <literal>
         |  <unary-operator> ':' <node>
         |  <binary-operator> ':' <node> ':' <node>
	  ;

  <literal> := 's' <digits=N> ':' <N character symbol name>
            |  'S' <digits=N> ':' <N character section name>
	     |  '#' <hexdigits>
	     ;

** these can be combined 3 ways:

*** complex relocs can refer to complex symbols

*** complex relocs can refer to "normal" ELF symbols

*** "normal" ELF relocs can refer to complex symbols

* the actual preservation of expressions as symbols and relocs is done
 in gas/symbols.c and gas/cgen.c. the extraction and evaluation of
 symbols and relocs is done in bfd/elflink.h. the evaluator is simple
 recursive calculator.

* the assembler has been modified to accept "percent functions" of the
form %foo(bleh,blah) which expand to more complex expressions.

* we decided to add a textual substitution pass (macro expansion) to
 the assembly loop. the bodies of the macros are stored in a static
 string table in cgen/cpu/mep.opc and can easily be extended. the
 macro expander is hard-coded into the opc file.


bfd/ChangeLog:
2007-01-12  Dave Brolley  <brolley@redhat.com>

	* Contribute the following changes:
	2004-07-23  Richard Sandiford  <rsandifo@redhat.com>

	* elflink.c (perform_complex_relocation): Detect relocations against
	undefined symbols.

	2002-04-02  DJ Delorie  <dj@redhat.com>

	* elflink.h (resolve_symbol): Handle weak symbols.

	2002-03-07  Graydon Hoare  <graydon@redhat.com>

	* elflink.h (perform_complex_relocations): Minor debug message
	touchup.

	2002-01-23  Graydon Hoare  <graydon@redhat.com>

	* bfd-in2.h: Add signed RELC (SRELC) symbols.
	* elf.c (swap_out_syms): Likewise.
	* elfcode.h (elf_slurp_symbol_table): Likewise.
	(elf_symbol_flags): Likewise.
	* syms.c: Likewise.
	* elflink.h (eval_symbol): Introduce signed RELC evaluation.
	(evaluate_complex_relocation_symbols): Likewise.
	(perform_complex_relocations): Remove sign-extension logic,
	which was broken anyways.

	2002-01-15  Graydon Hoare  <graydon@redhat.com>

	* elflink.h (decode_complex_addend): Add oplen, signed_p, trunc_p
	params.
	(perform_complex_relocations): Call decode with new args,
	Sign extend leftmost signed fields, delegate overflow check to
	bfd_check_overflow in reloc.c, enhance overflow error messages.

	2002-01-08  Graydon Hoare  <graydon@redhat.com>

	* elflink.h (resolve_symbol): Fix symbol -> section relationship.
	(resolve_symbol): Remove unused parameter.
	(eval_symbol): Remove unused arg from resolve_symbol call.

	2002-01-01  Graydon Hoare  <graydon@redhat.com>

	* elflink.h (perform_complex_relocations): Add overflow
	checking for RELC fragments.

	2001-12-15  Nick Clifton  <nickc@cambridge.redhat.com>

	* elflink.h (evaluate_complex_relocation_symbols): Formatting
	tidy ups.  Process all symbols, even if an error is
	encountered.

	2001-12-10  Graydon Hoare  <graydon@redhat.com>

	* elflink.h (evaluate_complex_relocation_symbols): Resolve
	global RELC symbols using separate logic from local symbols.

	2001-12-10  Nick Clifton  <nickc@cambridge.redhat.com>

	Various formatting tidy ups and compile time warning removals,
	plus:
	
	* elflink.h (eval_symbol): Set bfd_error for failure returns.
	(evaluate_complex_relocation_symbols): Only evaluate local
	symbols.

	2001-11-24  Graydon Hoare  <graydon@redhat.com>

	* elflink.h.c (resolve_symbol): Skip non-local syms in 
	local lookup loop; get section offset from section 
	symbol occurs in (rather than reloc section); handle 
	common symbols.

	2001-11-24  Graydon Hoare  <graydon@redhat.com>

	* elflink.h.c (resolve_symbol): Use
	bfd_elf_string_from_elf_section to look up local symbols in
	current bfd.
	(eval_symbol): Add input_bfd parameter for resolve_symbol's
	use.
	(evaluate_complex_relocation_symbols): Pass parameter.
	(resolve_section) Fix silly typo.

	2001-10-16  Jeff Holcomb  <jeffh@redhat.com>

	* elflink.h (eval_symbol): Buffer size needs to be an integer for
	Solaris cc.

	2001-11-15  graydon hoare  <graydon@redhat.com>

	* elf32-mep.c (mep_info_to_howto_rela): Add complex reloc
	howto.
	* elflink.h (resolve_section): Add support for pseudo-sections
	in complex symbols.
	(evaluate_complex_relocation_symbols): Permit evaluating RELC
	symbols on non-RELC relocs. They are distinct issues.

	2001-10-15  graydon hoare  <graydon@redhat.com>

	* reloc.c (bfd_reloc_code_type): Define BFD_RELOC_RELC,
	in the _correct_ place.

	2001-10-11  graydon hoare  <graydon@redhat.com>

	* elflink.h (eval_symbol): Fix strtol calls with undefined
	behavior.

	2001-10-03  graydon hoare  <graydon@redhat.com>

	* elflink.h (set_symbol_value): New function for relc.
	(resolve_symbol): Likewise.
	(resolve_section): Likewise.
	(undefined_reference): Likewise.
	(eval_symbol): Likewise.
	(evaluate_complex_relocation_symbols): Likewise.
	(put_value): Likewise.
	(get_value): Likewise.
	(decode_complex_addend): Likewise.
	(perform_complex_relocations): Likewise.
	(elf_link_input_bfd): Insert hook into
	evaluate_complex_relocation_symbols.
	(elf_link_input_bfd): Insert hook into
	perform_complex_relocations.

	* bfd-in2.h: Define BFD_RELOC_RELC, BSF_RELC.

	* elf.c (swap_out_syms): Add BSF_RELC / STT_RELC support.

	* elfcode.h (elf_slurp_symbol_table): Likewise.
	(elf_symbol_flags): Likewise.

	* libbfd.h: Add "BFD_RELOC_RELC" to bfd_reloc_code_real_names
	table.

	2001-06-20  Frank Ch. Eigler  <fche@redhat.com>

	* syms.c (BSF_RELC): New symbol flag.
	* elf.c (swap_out_syms): Map BSF_RELC -> STT_RELC.
	* elfcode.h (elf_slurp_symbol_table): Map STT_RELC -> BSF_RELC.
	(elf_symbol_flags): Display BSF_RELC as "relc".
	* bfd-in2.h: Regenerated.

gas/ChangeLog:
2007-01-12  Dave Brolley  <brolley@redhat.com>

	* Contribute the following changes:
	2002-06-06  Graydon Hoare  <graydon@redhat.com>

	* symbols.c (use_complex_relocs_for): Tighten up conditions on
	resolving expression symbols.

	2002-04-04  DJ Delorie  <dj@redhat.com>

	* symbols.c (use_complex_relocs_for): New, to decide
	when to use complex relocs.
	(resolve_symbol_value): Use it.

	2002-03-07  Graydon Hoare  <graydon@redhat.com>

	* cgen.c: Minor debugging touchups, warning removal.

	2002-02-17  Catherine Moore  <clm@redhat.com>

	* cgen.c (gas_cgen_md_apply_fix3):  Only set signed_p if RELC.

	2002-01-23  Graydon Hoare  <graydon@redhat.com>

	* cgen.c (gas_cgen_parse_operand): Add signed RELC support.
	(queue_fixup_recursively): Likewise.
	(make_right_shifted_expr): Likewise.
	* symbols.c (resolve_symbol_value): Likewise.

	2002-01-15  Graydon Hoare  <graydon@redhat.com>

	* write.h (struct fix): Add msb_field_p to fx_cgen sub-struct.
	* cgen.c (make_masked_expr): Remove.
	(gas_cgen_encode_addend): Add oplen, signed_p, trunc_p params.
	(gas_cgen_md_apply_fix3): Call encode_addend with new args.
	(queue_fixup_recursively): Change from masked expr to trunc flag.
	(queue_fixup_recursively): Restore assignment of sub-field value to
	temporary in fixups array (lost in recent merge).

	2002-01-01  Graydon Hoare  <graydon@redhat.com>

	* cgen.c (make_masked_expr): Add.
	(queue_fixup_recursively): Call make_masked_expr on non-rightmost
	fragments of multi-ifield complex relocs.
	(gas_cgen_parse_operand): Reflect changed meaning of last arg to
	queue_fixup_recursively.

	2001-12-18  Graydon Hoare  <graydon@redhat.com>

	* cgen.c (weak_operand_overflow_check): Improve accuracy of
	detecting overflows.

	2001-12-17  Nick Clifton  <nickc@cambridge.redhat.com>

	* cgen.c: Tidy up RELC code after the merge.

	2001-11-15  graydon hoare  <graydon@redhat.com>

	* cgen.c (fixup): Add cgen_maybe_multi_ifield member.
	(make_right_shifted_expr): New function.
	(queue_fixup): Change to recursive function that fragments
	fixups if operand has a multi-ifield.
	(gas_cgen_parse_operand): Add RELC code to wrap expressions in
	symbols, call weak_operand_overflow_check, and fragment call
	queue_fixup with operand fields.
	(gas_cgen_finish_insn) Modify to manage ifield pointer.
	(gas_cgen_md_apply_fix3) Modify to get start, length from
	ifield whenever it is set. Also change condition on which
	self-describing relocs are encoded.
	(weak_operand_overflow_check): New function to try to select
	insns correctly.
	* cgen.h (GAS_CGEN_MAX_FIXUPS): Bump from 3 up to 32.
	* write.h (struct fix): Add cgen_maybe_multi_ifield field to
	fx_cgen substructure
	* config/tc-mep.c (md_cgen_lookup_reloc): Fall back to
	BFD_RELOC_RELC when no other reloc types can be found.

	2001-10-03  graydon hoare  <graydon@redhat.com>

	* symbols.c (resolve_symbol_value): Unconditionally encode
	expression symbols as mangled complex relocation symbols (when
	compiled with -DOBJ_COMPLEX_RELOC)

	* cgen.c (gas_cgen_encode_addend): New function for relc.
	(gas_cgen_install_complex_reloc): Likewise.
	(gas_cgen_md_apply_fix3): Add hook into gas_cgen_encode_addend.
	(gas_cgen_tc_gen_reloc): Add hook into gas_cgen_install_complex_reloc.

	2001-06-24  Michael Chastain  <chastain@redhat.com>

	* symbols.c (symbol_relc_make_expr): Conform to K & R C.

	2001-06-20  Frank Ch. Eigler  <fche@redhat.com>

	* symbols.c (resolve_symbol_value): Conditionally generate relc
	symbols from unresolved expressions.
	(symbol_relc_make_sym,value,expr): New traversal/conversion routines.
	* symbols.h: Declare them.

include/elf/ChangeLog:
2007-01-12  Dave Brolley  <brolley@redhat.com>

	From Graydon Hoare  <graydon@redhat.com>:
	* common.h (STT_RELC, STT_SRELC, R_RELC): New macros.

Index: bfd/elf.c
===================================================================
RCS file: /cvs/src/src/bfd/elf.c,v
retrieving revision 1.369
diff -c -p -r1.369 elf.c
*** bfd/elf.c	19 Dec 2006 08:49:38 -0000	1.369
--- bfd/elf.c	11 Jan 2007 19:09:36 -0000
*************** Unable to find equivalent output section
*** 6388,6393 ****
--- 6388,6397 ----
  	type = STT_FUNC;
        else if ((flags & BSF_OBJECT) != 0)
  	type = STT_OBJECT;
+       else if ((flags & BSF_RELC) != 0)
+ 	type = STT_RELC;
+       else if ((flags & BSF_SRELC) != 0)
+ 	type = STT_SRELC;
        else
  	type = STT_NOTYPE;
  
Index: bfd/elfcode.h
===================================================================
RCS file: /cvs/src/src/bfd/elfcode.h,v
retrieving revision 1.79
diff -c -p -r1.79 elfcode.h
*** bfd/elfcode.h	22 Sep 2006 13:16:45 -0000	1.79
--- bfd/elfcode.h	11 Jan 2007 19:09:36 -0000
*************** elf_slurp_symbol_table (bfd *abfd, asymb
*** 1237,1242 ****
--- 1237,1248 ----
  	    case STT_TLS:
  	      sym->symbol.flags |= BSF_THREAD_LOCAL;
  	      break;
+ 	    case STT_RELC:
+ 	      sym->symbol.flags |= BSF_RELC;
+ 	      break;
+ 	    case STT_SRELC:
+ 	      sym->symbol.flags |= BSF_SRELC;
+ 	      break;
  	    }
  
  	  if (dynamic)
Index: bfd/elflink.c
===================================================================
RCS file: /cvs/src/src/bfd/elflink.c,v
retrieving revision 1.243
diff -c -p -r1.243 elflink.c
*** bfd/elflink.c	29 Dec 2006 17:56:32 -0000	1.243
--- bfd/elflink.c	11 Jan 2007 19:09:36 -0000
*************** struct elf_outext_info
*** 6336,6341 ****
--- 6336,7081 ----
    struct elf_final_link_info *finfo;
  };
  
+ 
+ /* Support for evaluating a complex relocation.
+ 
+    Complex relocations are generalized, self-describing relocations.  The
+    implementation of them consists of two parts: complex symbols, and the
+    relocations themselves. 
+ 
+    The relocations are use a reserved elf-wide relocation type code (R_RELC
+    external / BFD_RELOC_RELC internal) and an encoding of relocation field
+    information (start bit, end bit, word width, etc) into the addend.  This
+    information is extracted from CGEN-generated operand tables within gas.
+ 
+    Complex symbols are mangled symbols (BSF_RELC external / STT_RELC
+    internal) representing prefix-notation expressions, including but not
+    limited to those sorts of expressions normally encoded as addends in the
+    addend field.  The symbol mangling format is:
+ 
+    <node> := <literal>
+           |  <unary-operator> ':' <node>
+           |  <binary-operator> ':' <node> ':' <node>
+ 	  ;
+ 
+    <literal> := 's' <digits=N> ':' <N character symbol name>
+              |  'S' <digits=N> ':' <N character section name>
+ 	     |  '#' <hexdigits>
+ 	     ;
+ 
+    <binary-operator> := as in C
+    <unary-operator> := as in C, plus "0-" for unambiguous negation.  */
+ 
+ static void
+ set_symbol_value (bfd *                         bfd_with_globals,
+ 		  struct elf_final_link_info *  finfo,    
+ 		  int                           symidx,
+ 		  bfd_vma                       val)
+ {
+   bfd_boolean                    is_local;
+   Elf_Internal_Sym *             sym;
+   struct elf_link_hash_entry **  sym_hashes;
+   struct elf_link_hash_entry *   h;
+ 
+   sym_hashes = elf_sym_hashes (bfd_with_globals);
+   sym = finfo->internal_syms + symidx;  
+   is_local = ELF_ST_BIND(sym->st_info) == STB_LOCAL;
+   
+   if (is_local)
+     {
+       /* It is a local symbol: move it to the
+ 	 "absolute" section and give it a value.  */
+       sym->st_shndx = SHN_ABS;
+       sym->st_value = val;
+     }
+   else 
+     {
+       /* It is a global symbol: set its link type
+ 	 to "defined" and give it a value.  */
+       h = sym_hashes [symidx];	  
+       while (h->root.type == bfd_link_hash_indirect
+ 	     || h->root.type == bfd_link_hash_warning)
+ 	h = (struct elf_link_hash_entry *) h->root.u.i.link;
+       h->root.type = bfd_link_hash_defined;
+       h->root.u.def.value = val;
+       h->root.u.def.section = bfd_abs_section_ptr;
+     }
+ }
+ 
+ static bfd_boolean 
+ resolve_symbol (const char *                  name,
+ 		bfd *                         input_bfd,
+ 		struct elf_final_link_info *  finfo,
+ 		bfd_vma *                     result,
+ 		size_t                        locsymcount)
+ {
+   Elf_Internal_Sym *            sym;
+   struct bfd_link_hash_entry *  global_entry;
+   const char *                  candidate = NULL;
+   Elf_Internal_Shdr *           symtab_hdr;
+   asection *                    sec = NULL;
+   size_t                        i;
+   
+   symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
+ 
+   for (i = 0; i < locsymcount; ++ i)
+     {
+       sym = finfo->internal_syms + i;
+       sec = finfo->sections [i];
+ 
+       if (ELF_ST_BIND (sym->st_info) != STB_LOCAL)
+ 	continue;
+ 
+       candidate = bfd_elf_string_from_elf_section (input_bfd,
+ 						   symtab_hdr->sh_link,
+ 						   sym->st_name);
+ #ifdef DEBUG
+       printf ("Comparing string: '%s' vs. '%s' = 0x%x\n", 
+ 	      name, candidate, (unsigned int)sym->st_value);
+ #endif
+       if (candidate && strcmp (candidate, name) == 0)
+ 	{
+ 	  * result = sym->st_value;
+ 
+ 	  if (sym->st_shndx > SHN_UNDEF && 
+ 	      sym->st_shndx < SHN_LORESERVE)
+ 	    {
+ #ifdef DEBUG
+ 	      printf ("adjusting for sec '%s' @ 0x%x + 0x%x\n",
+ 		      sec->output_section->name, 
+ 		      (unsigned int)sec->output_section->vma, 
+ 		      (unsigned int)sec->output_offset);
+ #endif
+ 	      * result += sec->output_offset + sec->output_section->vma;
+ 	    }
+ #ifdef DEBUG
+ 	  printf ("Found symbol with effective value %8.8x\n", (unsigned int)* result);
+ #endif
+ 	  return TRUE;
+ 	}
+     }
+ 
+   /* Hmm, haven't found it yet. perhaps it is a global.  */
+   global_entry = bfd_link_hash_lookup (finfo->info->hash, name, FALSE, FALSE, TRUE);
+   if (!global_entry)
+     return FALSE;
+   
+   if (global_entry->type == bfd_link_hash_defined
+       || global_entry->type == bfd_link_hash_defweak)
+     {
+       * result = global_entry->u.def.value 
+ 	+ global_entry->u.def.section->output_section->vma 
+ 	+ global_entry->u.def.section->output_offset;
+ #ifdef DEBUG
+       printf ("Found GLOBAL symbol '%s' with value %8.8x\n",
+ 	      global_entry->root.string, (unsigned int)*result);
+ #endif
+       return TRUE;
+     } 
+ 
+   if (global_entry->type == bfd_link_hash_common)
+     {
+       *result = global_entry->u.def.value +
+ 	bfd_com_section_ptr->output_section->vma +
+ 	bfd_com_section_ptr->output_offset;
+ #ifdef DEBUG
+       printf ("Found COMMON symbol '%s' with value %8.8x\n",
+ 	      global_entry->root.string, (unsigned int)*result);
+ #endif
+       return TRUE;
+     }
+   
+   return FALSE;
+ }
+ 
+ static bfd_boolean
+ resolve_section (const char *  name,
+ 		 asection *    sections,
+ 		 bfd_vma *     result)
+ {
+   asection *    curr;
+   unsigned int  len;
+ 
+   for (curr = sections; curr; curr = curr->next)    
+     if (strcmp (curr->name, name) == 0)
+       {
+ 	*result = curr->vma;
+ 	return TRUE;
+       }
+ 
+   /* Hmm. still haven't found it. try pseudo-section names.  */
+   for (curr = sections; curr; curr = curr->next)    
+     {
+       len = strlen (curr->name);
+       if (len > strlen (name)) 
+ 	continue;
+ 
+       if (strncmp (curr->name, name, len) == 0)
+ 	{
+ 	  if (strncmp (".end", name + len, 4) == 0)
+ 	    {
+ 	      *result = curr->vma + curr->size;
+ 	      return TRUE;
+ 	    }
+ 
+ 	  /* Insert more pseudo-section names here, if you like.  */
+ 	}
+     }
+   
+   return FALSE;
+ }
+ 
+ static void
+ undefined_reference (const char *  reftype,
+ 		     const char *  name)
+ {
+   _bfd_error_handler (_("undefined %s reference in complex symbol: %s"), reftype, name);
+ }
+ 
+ static bfd_boolean
+ eval_symbol (bfd_vma *                     result,
+ 	     char *                        sym,
+ 	     char **                       advanced,
+ 	     bfd *                         input_bfd,
+ 	     struct elf_final_link_info *  finfo,
+ 	     bfd_vma                       addr,
+ 	     bfd_vma                       section_offset,
+ 	     size_t                        locsymcount,
+ 	     int                           signed_p)
+ {
+   int           len;
+   int           symlen;
+   bfd_vma       a;
+   bfd_vma       b;
+   const int     bufsz = 4096;
+   char          symbuf [bufsz];
+   const char *  symend;
+   bfd_boolean   symbol_is_section = FALSE;
+ 
+   len = strlen (sym);
+   symend = sym + len;
+ 
+   if (len < 1 || len > bufsz)
+     {
+       bfd_set_error (bfd_error_invalid_operation);
+       return FALSE;
+     }
+   
+   switch (* sym)
+     {
+     case '.':
+       * result = addr + section_offset;
+       * advanced = sym + 1;
+       return TRUE;
+ 
+     case '#':
+       ++ sym;
+       * result = strtoul (sym, advanced, 16);
+       return TRUE;
+ 
+     case 'S':
+       symbol_is_section = TRUE;
+     case 's':      
+       ++ sym;
+       symlen = strtol (sym, &sym, 10);
+       ++ sym; /* Skip the trailing ':'.  */
+ 
+       if ((symend < sym) || ((symlen + 1) > bufsz))
+ 	{
+ 	  bfd_set_error (bfd_error_invalid_operation);
+ 	  return FALSE;
+ 	}
+ 
+       memcpy (symbuf, sym, symlen);
+       symbuf [symlen] = '\0';
+       * advanced = sym + symlen;
+       
+       /* Is it always possible, with complex symbols, that gas "mis-guessed" 
+ 	 the symbol as a section, or vice-versa. so we're pretty liberal in our
+ 	 interpretation here; section means "try section first", not "must be a
+ 	 section", and likewise with symbol.  */
+ 
+       if (symbol_is_section) 
+ 	{
+ 	  if ((resolve_section (symbuf, finfo->output_bfd->sections, result) != TRUE)
+ 	      && (resolve_symbol (symbuf, input_bfd, finfo, result, locsymcount) != TRUE))
+ 	    {
+ 	      undefined_reference ("section", symbuf);
+ 	      return FALSE;
+ 	    }
+ 	} 
+       else 
+ 	{
+ 	  if ((resolve_symbol (symbuf, input_bfd, finfo, result, locsymcount) != TRUE)
+ 	      && (resolve_section (symbuf, finfo->output_bfd->sections,
+ 				   result) != TRUE))
+ 	    {
+ 	      undefined_reference ("symbol", symbuf);
+ 	      return FALSE;
+ 	    }
+ 	}
+ 
+       return TRUE;
+       
+       /* All that remains are operators.  */
+ 
+ #define UNARY_OP(op)						\
+   if (strncmp (sym, #op, strlen (#op)) == 0)			\
+     {								\
+       sym += strlen (#op);					\
+       if (* sym == ':')						\
+         ++ sym;							\
+       if (eval_symbol (& a, sym, & sym, input_bfd, finfo, addr, \
+                        section_offset, locsymcount, signed_p)   \
+ 	                                             != TRUE)	\
+         return FALSE;						\
+       if (signed_p)                                             \
+         * result = op ((signed)a);         			\
+       else                                                      \
+         * result = op a;                                        \
+       * advanced = sym; 					\
+       return TRUE;						\
+     }
+ 
+ #define BINARY_OP(op)						\
+   if (strncmp (sym, #op, strlen (#op)) == 0)			\
+     {								\
+       sym += strlen (#op);					\
+       if (* sym == ':')						\
+         ++ sym;							\
+       if (eval_symbol (& a, sym, & sym, input_bfd, finfo, addr, \
+                        section_offset, locsymcount, signed_p)   \
+                                                      != TRUE)	\
+         return FALSE;						\
+       ++ sym;							\
+       if (eval_symbol (& b, sym, & sym, input_bfd, finfo, addr, \
+                        section_offset, locsymcount, signed_p)   \
+                                                      != TRUE)	\
+         return FALSE;						\
+       if (signed_p)                                             \
+         * result = ((signed) a) op ((signed) b);	        \
+       else                                                      \
+         * result = a op b;                                      \
+       * advanced = sym;						\
+       return TRUE;						\
+     }
+ 
+     default:
+       UNARY_OP  (0-);
+       BINARY_OP (<<);
+       BINARY_OP (>>);
+       BINARY_OP (==);
+       BINARY_OP (!=);
+       BINARY_OP (<=);
+       BINARY_OP (>=);
+       BINARY_OP (&&);
+       BINARY_OP (||);
+       UNARY_OP  (~);
+       UNARY_OP  (!);
+       BINARY_OP (*);
+       BINARY_OP (/);
+       BINARY_OP (%);
+       BINARY_OP (^);
+       BINARY_OP (|);
+       BINARY_OP (&);
+       BINARY_OP (+);
+       BINARY_OP (-);
+       BINARY_OP (<);
+       BINARY_OP (>);
+ #undef UNARY_OP
+ #undef BINARY_OP
+       _bfd_error_handler (_("unknown operator '%c' in complex symbol"), * sym);
+       bfd_set_error (bfd_error_invalid_operation);
+       return FALSE;
+     }
+ }
+ 
+ /* Entry point to evaluator, called from elf_link_input_bfd.  */
+ 
+ static bfd_boolean
+ evaluate_complex_relocation_symbols (bfd * input_bfd,
+ 				     struct elf_final_link_info * finfo,
+ 				     size_t locsymcount)
+ {
+   const struct elf_backend_data * bed;
+   Elf_Internal_Shdr *             symtab_hdr;
+   struct elf_link_hash_entry **   sym_hashes;
+   asection *                      reloc_sec;
+   bfd_boolean                     result = TRUE;
+ 
+   /* For each section, we're going to check and see if it has any
+      complex relocations, and we're going to evaluate any of them
+      we can.  */
+ 
+   if (finfo->info->relocatable)
+     return TRUE;
+ 
+   symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
+   sym_hashes = elf_sym_hashes (input_bfd);
+   bed = get_elf_backend_data (input_bfd);
+ 
+   for (reloc_sec = input_bfd->sections; reloc_sec; reloc_sec = reloc_sec->next)
+     {
+       Elf_Internal_Rela * internal_relocs;
+       unsigned long i;
+ 
+       /* This section was omitted from the link.  */
+       if (! reloc_sec->linker_mark)
+ 	continue;
+ 
+       /* Only process sections containing relocs.  */
+       if ((reloc_sec->flags & SEC_RELOC) == 0)
+ 	continue;
+ 
+       if (reloc_sec->reloc_count == 0)
+ 	continue;
+ 
+       /* Read in the relocs for this section.  */
+       internal_relocs
+ 	= _bfd_elf_link_read_relocs (input_bfd, reloc_sec, NULL,
+ 				     (Elf_Internal_Rela *) NULL,
+ 				     FALSE);
+       if (internal_relocs == NULL)
+ 	continue;
+ 
+       for (i = reloc_sec->reloc_count; i--;)
+ 	{
+ 	  Elf_Internal_Rela * rel;
+ 	  char * sym_name;
+ 	  unsigned long index;
+ 	  Elf_Internal_Sym * sym;
+ 	  bfd_vma result;
+ 	  bfd_vma section_offset;
+ 	  bfd_vma addr;
+ 	  int signed_p = 0;
+ 
+ 	  rel = internal_relocs + i;
+ 	  section_offset = reloc_sec->output_section->vma
+ 	    + reloc_sec->output_offset;
+ 	  addr = rel->r_offset;
+ 
+ 	  index = ELF32_R_SYM (rel->r_info);
+ 	  if (bed->s->arch_size == 64)
+ 	    index >>= 24;
+  
+ 	  if (index < locsymcount)
+ 	    {
+ 	      /* The symbol is local.  */
+ 	      sym = finfo->internal_syms + index;
+ 
+ 	      /* We're only processing STT_RELC or STT_SRELC type symbols.  */
+ 	      if ((ELF_ST_TYPE (sym->st_info) != STT_RELC) &&
+ 		  (ELF_ST_TYPE (sym->st_info) != STT_SRELC))
+ 		continue;
+ 
+ 	      sym_name = bfd_elf_string_from_elf_section
+ 		(input_bfd, symtab_hdr->sh_link, sym->st_name);
+ 
+ 	      signed_p = (ELF_ST_TYPE (sym->st_info) == STT_SRELC);
+ 	    }
+ 	  else
+ 	    {
+ 	      /* The symbol is global.  */
+ 	      struct elf_link_hash_entry * h;
+ 
+ 	      if (elf_bad_symtab (input_bfd))
+ 		continue;
+ 
+ 	      h = sym_hashes [index - locsymcount];
+ 	      while (   h->root.type == bfd_link_hash_indirect
+ 		     || h->root.type == bfd_link_hash_warning)
+ 		h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ 
+ 	      if (h->type != STT_RELC)
+ 		continue;
+ 
+ 	      signed_p = (h->type == STT_SRELC);
+ 	      sym_name = (char *) h->root.root.string;
+ 	    }
+ #ifdef DEBUG
+ 	  printf ("Encountered a complex symbol!");
+ 	  printf (" (input_bfd %s, section %s, reloc %ld\n",
+ 		  input_bfd->filename, reloc_sec->name, i);
+ 	  printf (" symbol: idx  %8.8lx, name %s\n",
+ 		  index, sym_name);
+ 	  printf (" reloc : info %8.8lx, addr %8.8lx\n",
+ 		  rel->r_info, addr);
+ 	  printf (" Evaluating '%s' ...\n ", sym_name);
+ #endif
+ 	  if (eval_symbol (& result, sym_name, & sym_name, input_bfd, 
+ 			   finfo, addr, section_offset, locsymcount,
+ 			   signed_p))
+ 	    /* Symbol evaluated OK.  Update to absolute value.  */
+ 	    set_symbol_value (input_bfd, finfo, index, result);
+ 
+ 	  else
+ 	    result = FALSE;
+ 	}
+ 
+       if (internal_relocs != elf_section_data (reloc_sec)->relocs)
+ 	free (internal_relocs);
+     }
+ 
+   /* If nothing went wrong, then we adjusted 
+      everything we wanted to adjust.  */
+   return result;
+ }
+ 
+ static void
+ put_value (bfd_vma        size,
+ 	   unsigned long  chunksz,
+ 	   bfd *          input_bfd,
+ 	   bfd_vma        x,
+ 	   bfd_byte *     location)
+ {
+   location += (size - chunksz);
+ 
+   for (; size; size -= chunksz, location -= chunksz, x >>= (chunksz * 8)) 
+     {
+       switch (chunksz)
+ 	{
+ 	default:
+ 	case 0:
+ 	  abort ();
+ 	case 1:
+ 	  bfd_put_8 (input_bfd, x, location);
+ 	  break;
+ 	case 2:
+ 	  bfd_put_16 (input_bfd, x, location);
+ 	  break;
+ 	case 4:
+ 	  bfd_put_32 (input_bfd, x, location);
+ 	  break;
+ 	case 8:
+ #ifdef BFD64
+ 	  bfd_put_64 (input_bfd, x, location);
+ #else
+ 	  abort ();
+ #endif
+ 	  break;
+ 	}
+     }
+ }
+ 
+ static bfd_vma 
+ get_value (bfd_vma        size,
+ 	   unsigned long  chunksz,
+ 	   bfd *          input_bfd,
+ 	   bfd_byte *     location)
+ {
+   bfd_vma x = 0;
+ 
+   for (; size; size -= chunksz, location += chunksz) 
+     {
+       switch (chunksz)
+ 	{
+ 	default:
+ 	case 0:
+ 	  abort ();
+ 	case 1:
+ 	  x = (x << (8 * chunksz)) | bfd_get_8 (input_bfd, location);
+ 	  break;
+ 	case 2:
+ 	  x = (x << (8 * chunksz)) | bfd_get_16 (input_bfd, location);
+ 	  break;
+ 	case 4:
+ 	  x = (x << (8 * chunksz)) | bfd_get_32 (input_bfd, location);
+ 	  break;
+ 	case 8:
+ #ifdef BFD64
+ 	  x = (x << (8 * chunksz)) | bfd_get_64 (input_bfd, location);
+ #else
+ 	  abort ();
+ #endif
+ 	  break;
+ 	}
+     }
+   return x;
+ }
+ 
+ static void 
+ decode_complex_addend
+     (unsigned long * start,   /* in bits */
+      unsigned long * oplen,   /* in bits */
+      unsigned long * len,     /* in bits */
+      unsigned long * wordsz,  /* in bytes */
+      unsigned long * chunksz,  /* in bytes */
+      unsigned long * lsb0_p,
+      unsigned long * signed_p,
+      unsigned long * trunc_p,
+      unsigned long encoded)
+ {
+   * start     =  encoded        & 0x3F;
+   * len       = (encoded >>  6) & 0x3F;
+   * oplen     = (encoded >> 12) & 0x3F;
+   * wordsz    = (encoded >> 18) & 0xF;
+   * chunksz   = (encoded >> 22) & 0xF;
+   * lsb0_p    = (encoded >> 27) & 1;
+   * signed_p  = (encoded >> 28) & 1;
+   * trunc_p   = (encoded >> 29) & 1;
+ }
+ 
+ static void
+ perform_complex_relocations
+     (bfd *                   output_bfd ATTRIBUTE_UNUSED,
+      struct bfd_link_info *  info,
+      bfd *                   input_bfd,
+      asection *              input_section,
+      bfd_byte *              contents,
+      Elf_Internal_Rela *     relocs,
+      Elf_Internal_Sym *      local_syms,
+      asection **             local_sections)
+ {
+   const struct elf_backend_data * bed;
+   Elf_Internal_Rela *             new_relocs;
+   Elf_Internal_Rela *             rel;
+   Elf_Internal_Rela *             relend;
+   Elf_Internal_Shdr *             symtab_hdr;
+   struct elf_link_hash_entry **   sym_hashes;
+   unsigned long                   non_complex_count = 0;
+ 
+   if (info->relocatable)
+     return;
+ 
+   relend = relocs + input_section->reloc_count;  
+   sym_hashes = elf_sym_hashes (input_bfd);
+   symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr;
+   bed = get_elf_backend_data (input_bfd);
+ 
+   new_relocs = xmalloc (input_section->reloc_count * sizeof (Elf_Internal_Rela));
+ 
+   for (rel = relocs; rel < relend; rel ++)
+     {
+       if (ELF32_R_TYPE (rel->r_info) != R_RELC)
+ 	{
+ 	  /* Preserve this reloc for our caller; it is not complex.  */
+ 	  memcpy (new_relocs + (non_complex_count++),
+ 		  rel, sizeof (Elf_Internal_Rela));
+ 	}
+       else 
+ 	{
+ 	  /*  Perform this reloc, since it is complex
+ 
+ 	      (this is not to say that it necessarily refers to a complex
+ 	      symbol; merely that it is a self-describing CGEN based reloc.
+ 	      i.e. the addend has the complete reloc information (bit start, end,
+ 	      word size, etc) encoded within it.)  */ 
+ 	  unsigned long start, oplen, len, wordsz, 
+ 	    chunksz, lsb0_p, signed_p, trunc_p;
+ 	  Elf_Internal_Sym * sym = NULL;
+ 	  asection * sec = NULL;
+ 	  struct elf_link_hash_entry * h = NULL;
+ 	  bfd_vma relocation = 0, shift = 0, x = 0;
+ 	  unsigned long r_symndx;
+ 	  bfd_vma mask;
+ 
+ 	  r_symndx = ELF32_R_SYM (rel->r_info);
+ 	  if (bed->s->arch_size == 64)
+ 	    r_symndx >>= 24;
+ #ifdef DEBUG
+ 	  printf ("Performing complex relocation %ld...\n", r_symndx);
+ #endif
+ 	  if (r_symndx < symtab_hdr->sh_info)
+ 	    {
+ 	      /* The symbol is local.  */
+ 	      sym = local_syms + r_symndx;
+ 	      sec = local_sections [r_symndx];
+ 	      relocation = sym->st_value;
+ 	      if (sym->st_shndx > SHN_UNDEF && 
+ 		  sym->st_shndx < SHN_LORESERVE)
+ 		relocation += (sec->output_offset +
+ 			       sec->output_section->vma);
+ 	    }
+ 	  else
+ 	    {
+ 	      /* The symbol is global.  */
+ 	      h = sym_hashes [r_symndx];
+ 
+ 	      while (h->root.type == bfd_link_hash_indirect
+ 		     || h->root.type == bfd_link_hash_warning)
+ 		h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ 
+ 	      if (h->root.type == bfd_link_hash_defined
+ 		  || h->root.type == bfd_link_hash_defweak)
+ 		{
+ 		  sec = h->root.u.def.section;
+ 		  relocation = h->root.u.def.value;
+ 
+ 		  if (! bfd_is_abs_section (sec))
+ 		    relocation += (sec->output_section->vma 
+ 				   + sec->output_offset); 
+ 		}
+ 	      if (h->root.type == bfd_link_hash_undefined
+ 		  && !((*info->callbacks->undefined_symbol)
+ 		       (info, h->root.root.string, input_bfd,
+ 			input_section, rel->r_offset,
+ 			info->unresolved_syms_in_objects == RM_GENERATE_ERROR
+ 			|| ELF_ST_VISIBILITY (h->other))))
+ 		continue;
+ 	    }
+ 
+ 	  decode_complex_addend (& start, & oplen, & len, & wordsz, 
+ 				 & chunksz, & lsb0_p, & signed_p, 
+ 				 & trunc_p, rel->r_addend);
+ 
+ 	  mask = (((1L << (len - 1)) - 1) << 1) | 1;
+ 
+ 	  if (lsb0_p)
+ 	    shift = (start + 1) - len;
+ 	  else
+ 	    shift = (8 * wordsz) - (start + len);
+ 
+ 	  x = get_value (wordsz, chunksz, input_bfd, contents + rel->r_offset);	  
+ 
+ #ifdef DEBUG
+ 	  printf ("Doing complex reloc: "
+ 		  "lsb0? %ld, signed? %ld, trunc? %ld, wordsz %ld, "
+ 		  "chunksz %ld, start %ld, len %ld, oplen %ld\n"
+ 		  "    dest: %8.8lx, mask: %8.8lx, reloc: %8.8lx\n",
+ 		  lsb0_p, signed_p, trunc_p, wordsz, chunksz, start, len,
+ 		  oplen, x, mask,  relocation);
+ #endif
+ 
+ 	  if (! trunc_p)
+ 	    {
+ 	      /* Now do an overflow check.  */
+ 	      if (bfd_check_overflow ((signed_p ? 
+ 				       complain_overflow_signed : 
+ 				       complain_overflow_unsigned),
+ 				      len, 0, (8 * wordsz), 
+ 				      relocation) == bfd_reloc_overflow)
+ 		(*_bfd_error_handler) 
+ 		  ("%s (%s + 0x%lx): relocation overflow: 0x%lx %sdoes not fit "
+ 		   "within 0x%lx", 
+ 		   input_bfd->filename, input_section->name, rel->r_offset,
+ 		   relocation, (signed_p ? "(signed) " : ""), mask);
+ 	    }
+ 	  
+ 	  /* Do the deed.  */
+ 	  x = (x & ~(mask << shift)) | ((relocation & mask) << shift);
+ 
+ #ifdef DEBUG
+ 	  printf ("           relocation: %8.8lx\n"
+ 		  "         shifted mask: %8.8lx\n"
+ 		  " shifted/masked reloc: %8.8lx\n"
+ 		  "               result: %8.8lx\n",
+ 		  relocation, (mask << shift), 
+ 		  ((relocation & mask) << shift), x);
+ #endif
+ 	  put_value (wordsz, chunksz, input_bfd, x, contents + rel->r_offset);
+   	}
+     }
+ 
+   /* Now copy back the remaining relocs.  */
+   if (non_complex_count != input_section->reloc_count) 
+     {
+       memcpy (relocs, new_relocs, sizeof (Elf_Internal_Rela) * non_complex_count);
+       input_section->reloc_count = non_complex_count;
+     }
+ 
+   free (new_relocs);
+ }
+ 
  /* When performing a relocatable link, the input relocations are
     preserved.  But, if they reference global symbols, the indices
     referenced must be updated.  Update all the relocations in
*************** elf_link_input_bfd (struct elf_final_lin
*** 7497,7502 ****
--- 8237,8245 ----
  	return FALSE;
      }
  
+   if (! evaluate_complex_relocation_symbols (input_bfd, finfo, locsymcount))
+     return FALSE;
+ 
    /* Relocate the contents of each section.  */
    sym_hashes = elf_sym_hashes (input_bfd);
    for (o = input_bfd->sections; o != NULL; o = o->next)
*************** elf_link_input_bfd (struct elf_final_lin
*** 7549,7554 ****
--- 8292,8305 ----
  	      && o->reloc_count > 0)
  	    return FALSE;
  
+ 	  /* Perform any complex relocations in the internal_relocs array,
+ 	     remove them, and decrement o->reloc_count appropriately.  */
+ 	  perform_complex_relocations (output_bfd, finfo->info,
+ 				       input_bfd, o, contents,
+ 				       internal_relocs,
+ 				       finfo->internal_syms,
+ 				       finfo->sections);
+ 
  	  if (bed->s->arch_size == 32)
  	    {
  	      r_type_mask = 0xff;
Index: bfd/reloc.c
===================================================================
RCS file: /cvs/src/src/bfd/reloc.c,v
retrieving revision 1.160
diff -c -p -r1.160 reloc.c
*** bfd/reloc.c	3 Nov 2006 00:58:10 -0000	1.160
--- bfd/reloc.c	11 Jan 2007 19:09:37 -0000
*************** ENUMDOC
*** 4671,4676 ****
--- 4715,4726 ----
    Sony Xstormy16 Relocations.
  
  ENUM
+   BFD_RELOC_RELC
+ ENUMDOC
+   Self-describing complex relocations.
+ COMMENT
+ 
+ ENUM
    BFD_RELOC_XC16X_PAG
  ENUMX
    BFD_RELOC_XC16X_POF
Index: bfd/syms.c
===================================================================
RCS file: /cvs/src/src/bfd/syms.c,v
retrieving revision 1.41
diff -c -p -r1.41 syms.c
*** bfd/syms.c	4 May 2005 15:53:39 -0000	1.41
--- bfd/syms.c	11 Jan 2007 19:09:37 -0000
*************** CODE_FRAGMENT
*** 288,293 ****
--- 288,301 ----
  .  {* This symbol is thread local.  Used in ELF.  *}
  .#define BSF_THREAD_LOCAL  0x40000
  .
+ .  {* This symbol represents a complex relocation expression,
+ .     with the expression tree serialized in the symbol name.  *}
+ .#define BSF_RELC 0x80000
+ .
+ .  {* This symbol represents a signed complex relocation expression,
+ .     with the expression tree serialized in the symbol name.  *}
+ .#define BSF_SRELC 0x100000
+ .
  .  flagword flags;
  .
  .  {* A pointer to the section to which this symbol is
Index: binutils/readelf.c
===================================================================
RCS file: /cvs/src/src/binutils/readelf.c,v
retrieving revision 1.356
diff -c -p -r1.356 readelf.c
*** binutils/readelf.c	8 Jan 2007 18:42:36 -0000	1.356
--- binutils/readelf.c	11 Jan 2007 19:09:38 -0000
*************** dump_relocations (FILE *file,
*** 1169,1174 ****
--- 1169,1177 ----
  	  break;
  	}
  
+       if (rtype == NULL && type == R_RELC)
+ 	rtype = "R_RELC";
+ 
        if (rtype == NULL)
  #ifdef _bfd_int64_low
  	printf (_("unrecognized: %-7lx"), _bfd_int64_low (type));
*************** get_symbol_type (unsigned int type)
*** 6843,6848 ****
--- 6856,6863 ----
      case STT_FILE:	return "FILE";
      case STT_COMMON:	return "COMMON";
      case STT_TLS:	return "TLS";
+     case STT_RELC:      return "RELC";
+     case STT_SRELC:     return "SRELC";
      default:
        if (type >= STT_LOPROC && type <= STT_HIPROC)
  	{
Index: gas/cgen.c
===================================================================
RCS file: /cvs/src/src/gas/cgen.c,v
retrieving revision 1.31
diff -c -p -r1.31 cgen.c
*** gas/cgen.c	7 Jun 2006 11:27:57 -0000	1.31
--- gas/cgen.c	11 Jan 2007 19:09:39 -0000
***************
*** 26,31 ****
--- 26,55 ----
  #include "cgen.h"
  #include "dwarf2dbg.h"
  
+ #include "symbols.h"
+ #include "struc-symbol.h"
+ 
+ #ifdef OBJ_COMPLEX_RELC
+ static expressionS * make_right_shifted_expr
+   (expressionS *, const int, const int);
+ 
+ static unsigned long gas_cgen_encode_addend
+   (const unsigned long, const unsigned long, const unsigned long, \
+    const unsigned long, const unsigned long, const unsigned long, \
+    const unsigned long);
+ 
+ static bfd_reloc_status_type gas_cgen_install_complex_reloc
+   (bfd *, arelent *, asymbol *, void *, asection *, bfd *, char **);
+ 
+ static char * weak_operand_overflow_check
+   (const expressionS *, const CGEN_OPERAND *);
+ 
+ static void queue_fixup_recursively
+   (const int, const int, expressionS *, \
+    const CGEN_MAYBE_MULTI_IFLD *, const int, const int);
+ 
+ static int rightshift = 0;
+ #endif
  static void queue_fixup (int, int, expressionS *);
  
  /* Opcode table descriptor, must be set by md_begin.  */
*************** struct fixup
*** 63,68 ****
--- 87,94 ----
    int opindex;
    int opinfo;
    expressionS exp;
+   struct cgen_maybe_multi_ifield * field;
+   int msb_field_p;
  };
  
  static struct fixup fixups[GAS_CGEN_MAX_FIXUPS];
*************** gas_cgen_record_fixup (frag, where, insn
*** 246,251 ****
--- 272,279 ----
  		     + (int) operand->type));
    fixP->fx_cgen.insn = insn;
    fixP->fx_cgen.opinfo = opinfo;
+   fixP->fx_cgen.field = NULL;
+   fixP->fx_cgen.msb_field_p = 0;
  
    return fixP;
  }
*************** gas_cgen_record_fixup_exp (frag, where, 
*** 284,293 ****
--- 312,337 ----
  			 + (int) operand->type));
    fixP->fx_cgen.insn = insn;
    fixP->fx_cgen.opinfo = opinfo;
+   fixP->fx_cgen.field = NULL;
+   fixP->fx_cgen.msb_field_p = 0;
  
    return fixP;
  }
  
+ #ifdef OBJ_COMPLEX_RELC
+ static symbolS *
+ expr_build_binary (operatorT op, symbolS * s1, symbolS * s2)
+ {
+   expressionS e;
+ 
+   e.X_op = op;
+   e.X_add_symbol = s1;
+   e.X_op_symbol = s2;
+   e.X_add_number = 0;
+   return make_expr_symbol (& e);
+ }
+ #endif
+ 
  /* Used for communication between the next two procedures.  */
  static jmp_buf expr_jmp_buf;
  static int expr_jmp_buf_p;
*************** static int expr_jmp_buf_p;
*** 305,311 ****
--- 349,360 ----
  
  const char *
  gas_cgen_parse_operand (cd, want, strP, opindex, opinfo, resultP, valueP)
+ 
+ #ifdef OBJ_COMPLEX_RELC
+      CGEN_CPU_DESC cd;
+ #else
       CGEN_CPU_DESC cd ATTRIBUTE_UNUSED;
+ #endif
       enum cgen_parse_operand_type want;
       const char **strP;
       int opindex;
*************** gas_cgen_parse_operand (cd, want, strP, 
*** 326,331 ****
--- 375,387 ----
    const char *errmsg;
    expressionS exp;
  
+ #ifdef OBJ_COMPLEX_RELC
+   volatile int              signed_p = 0;
+   symbolS *                 stmp = NULL;
+   bfd_reloc_code_real_type  reloc_type;
+   const CGEN_OPERAND *      operand;
+   fixS                      dummy_fixup;
+ #endif
    if (want == CGEN_PARSE_OPERAND_INIT)
      {
        gas_cgen_init_parse ();
*************** gas_cgen_parse_operand (cd, want, strP, 
*** 383,391 ****
--- 439,520 ----
        break;
      de_fault:
      default:
+ #ifdef OBJ_COMPLEX_RELC
+       /* Look up operand, check to see if there's an obvious
+ 	 overflow (this helps disambiguate some insn parses).  */
+       operand = cgen_operand_lookup_by_num (cd, opindex);
+       errmsg = weak_operand_overflow_check (& exp, operand);
+ 
+       if (! errmsg)
+ 	{
+ 	  /* Fragment the expression as necessary, and queue a reloc.  */
+ 	  memset (& dummy_fixup, 0, sizeof (fixS));
+ 
+ 	  reloc_type = md_cgen_lookup_reloc (0, operand, & dummy_fixup);
+ 
+ 	  if (exp.X_op == O_symbol
+ 	      && reloc_type == BFD_RELOC_RELC
+ 	      && exp.X_add_symbol->sy_value.X_op == O_constant
+ 	      && exp.X_add_symbol->bsym->section != expr_section
+ 	      && exp.X_add_symbol->bsym->section != absolute_section
+ 	      && exp.X_add_symbol->bsym->section != undefined_section)
+ 	    {
+ 	      /* Local labels will have been (eagerly) turned into constants
+ 		 by now, due to the inappropriately deep insight of the
+ 		 expression parser.  Unfortunately make_expr_symbol
+ 		 prematurely dives into the symbol evaluator, and in this
+ 		 case it gets a bad answer, so we manually create the
+ 		 expression symbol we want here.  */
+ 	      stmp = symbol_create (FAKE_LABEL_NAME, expr_section, 0,
+ 				    & zero_address_frag);
+ 	      symbol_set_value_expression (stmp, & exp);
+ 	    } 
+ 	  else 
+ 	    stmp = make_expr_symbol (& exp);
+ 
+ 	  /* If this is a pc-relative RELC operand, we
+ 	     need to subtract "." from the expression.  */	  
+  	  if (reloc_type == BFD_RELOC_RELC
+ 	      && CGEN_OPERAND_ATTR_VALUE (operand, CGEN_OPERAND_PCREL_ADDR))
+  	    stmp = expr_build_binary (O_subtract, stmp, expr_build_dot ()); 
+ 
+ 	  /* FIXME: this is not a perfect heuristic for figuring out
+ 	     whether an operand is signed: it only works when the operand
+ 	     is an immediate. it's not terribly likely that any other
+ 	     values will be signed relocs, but it's possible. */
+ 	  if (operand && (operand->hw_type == HW_H_SINT))
+ 	    signed_p = 1;
+ 	  
+ 	  if (stmp->bsym && (stmp->bsym->section == expr_section))
+ 	    {
+ 	      if (signed_p)
+ 		stmp->bsym->flags |= BSF_SRELC;
+ 	      else
+ 		stmp->bsym->flags |= BSF_RELC;
+ 	    }
+ 	  
+ 	  /* Now package it all up for the fixup emitter.  */
+ 	  exp.X_op = O_symbol;
+ 	  exp.X_op_symbol = 0;
+ 	  exp.X_add_symbol = stmp;
+ 	  exp.X_add_number = 0;
+ 	      
+ 	  /* Re-init rightshift quantity, just in case.  */
+ 	  rightshift = operand->length;
+ 	  queue_fixup_recursively (opindex, opinfo_1, & exp,  
+ 				   (reloc_type == BFD_RELOC_RELC) ?
+ 				   & (operand->index_fields) : 0,
+ 				   signed_p, -1);
+ 	}
+       * resultP = errmsg
+ 	? CGEN_PARSE_OPERAND_RESULT_ERROR
+ 	: CGEN_PARSE_OPERAND_RESULT_QUEUED;
+       *valueP = 0;
+ #else
        queue_fixup (opindex, opinfo_1, &exp);
        *valueP = 0;
        *resultP = CGEN_PARSE_OPERAND_RESULT_QUEUED;
+ #endif      
        break;
      }
  
*************** gas_cgen_finish_insn (insn, buf, length,
*** 553,558 ****
--- 682,689 ----
  				       insn, length, operand,
  				       fixups[i].opinfo,
  				       &fixups[i].exp);
+       fixP->fx_cgen.field = fixups[i].field;
+       fixP->fx_cgen.msb_field_p = fixups[i].msb_field_p;
        if (result)
  	result->fixups[i] = fixP;
      }
*************** gas_cgen_finish_insn (insn, buf, length,
*** 564,569 ****
--- 695,861 ----
      }
  }
  
+ #ifdef OBJ_COMPLEX_RELC
+ /* Queue many fixups, recursively. If the field is a multi-ifield,
+    repeatedly queue its sub-parts, right shifted to fit into the field (we
+    assume here multi-fields represent a left-to-right, MSB0-LSB0
+    reading). */
+ 
+ static void
+ queue_fixup_recursively (const int                      opindex,
+ 			 const int                      opinfo,
+ 			 expressionS *                  expP,
+ 			 const CGEN_MAYBE_MULTI_IFLD *  field,
+ 			 const int                      signed_p,
+ 			 const int                      part_of_multi)
+ {
+   if (field && field->count)
+     {
+       int i;
+   
+       for (i = 0; i < field->count; ++ i)
+ 	queue_fixup_recursively (opindex, opinfo, expP, 
+ 				 & (field->val.multi[i]), signed_p, i);
+     }
+   else
+     {
+       expressionS * new_exp = expP;
+ 
+ #ifdef DEBUG
+       printf ("queueing fixup for field %s\n",
+ 	      (field ? field->val.leaf->name : "??"));
+       print_symbol_value (expP->X_add_symbol);
+ #endif
+       if (field && part_of_multi != -1)
+ 	{
+ 	  rightshift -= field->val.leaf->length;
+ 
+ 	  /* Shift reloc value by number of bits remaining after this
+ 	     field.  */
+ 	  if (rightshift)
+ 	    new_exp = make_right_shifted_expr (expP, rightshift, signed_p);	  
+ 	}
+       
+       /* Truncate reloc values to length, *after* leftmost one.  */
+       fixups[num_fixups].msb_field_p = (part_of_multi <= 0);
+       fixups[num_fixups].field = (CGEN_MAYBE_MULTI_IFLD *) field;
+       
+       queue_fixup (opindex, opinfo, new_exp);
+     }
+ }
+ 
+ /* Encode the self-describing RELC reloc format's addend.  */
+ 
+ static unsigned long 
+ gas_cgen_encode_addend (const unsigned long start,    /* in bits */
+ 			const unsigned long len,      /* in bits */
+ 			const unsigned long oplen,    /* in bits */
+ 			const unsigned long wordsz,   /* in bytes */
+ 			const unsigned long chunksz,  /* in bytes */
+ 			const unsigned long signed_p,
+ 			const unsigned long trunc_p)
+ {
+   unsigned long res = 0L;
+ 
+   res |= start    & 0x3F;
+   res |= (oplen   & 0x3F) << 6;
+   res |= (len     & 0x3F) << 12;
+   res |= (wordsz  & 0xF)  << 18;
+   res |= (chunksz & 0xF)  << 22;
+   res |= (CGEN_INSN_LSB0_P ? 1 : 0) << 27;
+   res |= signed_p << 28;
+   res |= trunc_p << 29;
+ 
+   return res;
+ }
+ 
+ /* Purpose: make a weak check that the expression doesn't overflow the
+    operand it's to be inserted into.
+ 
+    Rationale: some insns used to use %operators to disambiguate during a
+    parse. when these %operators are translated to expressions by the macro
+    expander, the ambiguity returns. we attempt to disambiguate by field
+    size.
+    
+    Method: check to see if the expression's top node is an O_and operator,
+    and the mask is larger than the operand length. This would be an
+    overflow, so signal it by returning an error string. Any other case is
+    ambiguous, so we assume it's OK and return NULL.  */
+ 
+ static char *
+ weak_operand_overflow_check (const expressionS *  exp,
+ 			     const CGEN_OPERAND * operand)
+ {
+   const unsigned long len = operand->length;
+   unsigned long mask;
+   unsigned long opmask = (((1L << (len - 1)) - 1) << 1) | 1;
+ 
+   if (!exp)
+     return NULL;
+ 
+   if (exp->X_op != O_bit_and)
+     {
+       /* Check for implicit overflow flag.  */
+       if (CGEN_OPERAND_ATTR_VALUE 
+ 	  (operand, CGEN_OPERAND_RELOC_IMPLIES_OVERFLOW))
+ 	return _("a reloc on this operand implies an overflow");
+       return NULL;
+     }
+   
+   mask = exp->X_add_number;
+ 
+   if (exp->X_add_symbol &&
+       exp->X_add_symbol->sy_value.X_op == O_constant)
+     mask |= exp->X_add_symbol->sy_value.X_add_number;
+ 
+   if (exp->X_op_symbol &&
+       exp->X_op_symbol->sy_value.X_op == O_constant)
+     mask |= exp->X_op_symbol->sy_value.X_add_number;
+ 
+   /* Want to know if mask covers more bits than opmask. 
+      this is the same as asking if mask has any bits not in opmask,
+      or whether (mask & ~opmask) is nonzero.  */
+   if (mask && (mask & ~opmask))
+     {
+ #ifdef DEBUG
+       printf ("overflow: (mask = %8.8x, ~opmask = %8.8x, AND = %8.8x)\n",
+ 	      mask, ~opmask, (mask & ~opmask));
+ #endif
+       return _("operand mask overflow");
+     }
+ 
+   return NULL;  
+ }
+ 
+ 
+ static expressionS *
+ make_right_shifted_expr (expressionS * exp,
+ 			 const int     amount,
+ 			 const int     signed_p)
+ {
+   symbolS * stmp = 0;
+   expressionS * new_exp;
+ 
+   stmp = expr_build_binary (O_right_shift, 
+ 			    make_expr_symbol (exp),
+ 			    expr_build_uconstant (amount));
+   
+   if (signed_p)
+     stmp->bsym->flags |= BSF_SRELC;
+   else
+     stmp->bsym->flags |= BSF_RELC;
+   
+   /* Then wrap that in a "symbol expr" for good measure.  */
+   new_exp = xmalloc (sizeof (expressionS));
+   memset (new_exp, 0, sizeof (expressionS));
+   new_exp->X_op = O_symbol;
+   new_exp->X_op_symbol = 0;
+   new_exp->X_add_symbol = stmp;
+   new_exp->X_add_number = 0;
+   
+   return new_exp;
+ }
+ #endif
  /* Apply a fixup to the object code.  This is called for all the
     fixups we generated by the call to fix_new_exp, above.  In the call
     above we used a reloc code which was the largest legal reloc code
*************** gas_cgen_md_apply_fix (fixP, valP, seg)
*** 602,607 ****
--- 894,923 ----
        bfd_reloc_code_real_type reloc_type;
        CGEN_FIELDS *fields = alloca (CGEN_CPU_SIZEOF_FIELDS (cd));
        const CGEN_INSN *insn = fixP->fx_cgen.insn;
+       int start;
+       int length;
+       int signed_p = 0;
+ 
+       if (fixP->fx_cgen.field)
+ 	{	      
+ 	  /* Use the twisty little pointer path
+ 	     back to the ifield if it exists.  */
+ 	  start = fixP->fx_cgen.field->val.leaf->start;
+ 	  length = fixP->fx_cgen.field->val.leaf->length;
+ 	}
+       else
+ 	{
+ 	  /* Or the far less useful operand-size guesstimate.  */
+ 	  start = operand->start;
+ 	  length = operand->length;
+ 	}
+ 
+       /* FIXME: this is not a perfect heuristic for figuring out
+          whether an operand is signed: it only works when the operand
+          is an immediate. it's not terribly likely that any other
+          values will be signed relocs, but it's possible. */
+       if (operand && (operand->hw_type == HW_H_SINT))
+         signed_p = 1;
  
        /* If the reloc has been fully resolved finish the operand here.  */
        /* FIXME: This duplicates the capabilities of code in BFD.  */
*************** gas_cgen_md_apply_fix (fixP, valP, seg)
*** 644,649 ****
--- 960,977 ----
  	 partial_inplace == false.  */
  
        reloc_type = md_cgen_lookup_reloc (insn, operand, fixP);
+ #ifdef OBJ_COMPLEX_RELC
+       if (reloc_type == BFD_RELOC_RELC)
+ 	{
+ 	  /* Change addend to "self-describing" form,
+ 	     for BFD to handle in the linker.  */
+ 	  value = gas_cgen_encode_addend (start, operand->length,
+ 					  length, fixP->fx_size, 
+ 					  cd->insn_chunk_bitsize / 8, 
+ 					  signed_p, 
+ 					  ! (fixP->fx_cgen.msb_field_p));
+ 	}
+ #endif
  
        if (reloc_type != BFD_RELOC_NONE)
  	fixP->fx_r_type = reloc_type;
*************** gas_cgen_md_apply_fix (fixP, valP, seg)
*** 689,694 ****
--- 1017,1036 ----
    fixP->fx_addnumber = value;
  }
  
+ #ifdef OBJ_COMPLEX_RELC
+ 
+ static bfd_reloc_status_type
+ gas_cgen_install_complex_reloc (bfd *      abfd          ATTRIBUTE_UNUSED,
+ 				arelent *  reloc_entry   ATTRIBUTE_UNUSED,
+ 				asymbol *  symbol        ATTRIBUTE_UNUSED,
+ 				PTR        data          ATTRIBUTE_UNUSED,
+ 				asection * input_section ATTRIBUTE_UNUSED,
+ 				bfd *      output_bfd    ATTRIBUTE_UNUSED,
+ 				char **    error_message ATTRIBUTE_UNUSED)
+ {
+   return bfd_reloc_ok;
+ }
+ #endif
  /* Translate internal representation of relocation info to BFD target format.
  
     FIXME: To what extent can we get all relevant targets to use this?  */
*************** gas_cgen_tc_gen_reloc (section, fixP)
*** 699,707 ****
--- 1041,1062 ----
       fixS *     fixP;
  {
    arelent *reloc;
+ #ifdef OBJ_COMPLEX_RELC
+   static reloc_howto_type complex_reloc_installation_howto =
+     HOWTO (R_RELC, 0, 0, 0, 0, 0, complain_overflow_dont,
+ 	   gas_cgen_install_complex_reloc, 
+ 	   "RELOC_RELC", FALSE, 0, 0, 0);
+ #endif
  
    reloc = (arelent *) xmalloc (sizeof (arelent));
  
+ #ifdef OBJ_COMPLEX_RELC
+   if (fixP->fx_r_type == BFD_RELOC_RELC) 
+     reloc->howto = &complex_reloc_installation_howto;
+ 
+   else 
+     {
+ #endif
    reloc->howto = bfd_reloc_type_lookup (stdoutput, fixP->fx_r_type);
    if (reloc->howto == (reloc_howto_type *) NULL)
      {
*************** gas_cgen_tc_gen_reloc (section, fixP)
*** 711,716 ****
--- 1066,1074 ----
      }
  
    assert (!fixP->fx_pcrel == !reloc->howto->pc_relative);
+ #ifdef OBJ_COMPLEX_RELC
+     }
+ #endif
  
    reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
    *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixP->fx_addsy);
*************** gas_cgen_begin ()
*** 737,739 ****
--- 1095,1098 ----
    else
      cgen_clear_signed_overflow_ok (gas_cgen_cpu_desc);
  }
+ 
Index: gas/symbols.c
===================================================================
RCS file: /cvs/src/src/gas/symbols.c,v
retrieving revision 1.80
diff -c -p -r1.80 symbols.c
*** gas/symbols.c	15 Nov 2006 21:47:49 -0000	1.80
--- gas/symbols.c	11 Jan 2007 19:09:45 -0000
*************** verify_symbol_chain (symbolS *rootP, sym
*** 881,886 ****
--- 881,949 ----
    assert (lastP == symbolP);
  }
  
+ #ifdef OBJ_COMPLEX_RELC
+ 
+ static int
+ use_complex_relocs_for (symbolS * symp)
+ {
+   switch (symp->sy_value.X_op)
+     {
+     case O_constant:
+       return 0;
+ 
+     case O_symbol:
+     case O_symbol_rva:
+     case O_uminus:
+     case O_bit_not:
+     case O_logical_not:
+       if (  (S_IS_COMMON (symp->sy_value.X_add_symbol)
+ 	   || S_IS_LOCAL (symp->sy_value.X_add_symbol))
+ 	  &&
+ 	      (S_IS_DEFINED (symp->sy_value.X_add_symbol)
+ 	   && S_GET_SEGMENT (symp->sy_value.X_add_symbol) != expr_section))
+ 	return 0;
+       break;
+ 
+     case O_multiply:
+     case O_divide:
+     case O_modulus:
+     case O_left_shift:
+     case O_right_shift:
+     case O_bit_inclusive_or:
+     case O_bit_or_not:
+     case O_bit_exclusive_or:
+     case O_bit_and:
+     case O_add:
+     case O_subtract:
+     case O_eq:
+     case O_ne:
+     case O_lt:
+     case O_le:
+     case O_ge:
+     case O_gt:
+     case O_logical_and:
+     case O_logical_or:
+ 
+       if (  (S_IS_COMMON (symp->sy_value.X_add_symbol)
+ 	   || S_IS_LOCAL (symp->sy_value.X_add_symbol))
+ 	  && 
+ 	    (S_IS_COMMON (symp->sy_value.X_op_symbol)
+ 	   || S_IS_LOCAL (symp->sy_value.X_op_symbol))
+ 
+ 	  && S_IS_DEFINED (symp->sy_value.X_add_symbol)
+ 	  && S_IS_DEFINED (symp->sy_value.X_op_symbol)
+ 	  && S_GET_SEGMENT (symp->sy_value.X_add_symbol) != expr_section
+ 	  && S_GET_SEGMENT (symp->sy_value.X_op_symbol) != expr_section)
+ 	return 0;
+       break;
+       
+     default:
+       break;
+     }
+   return 1;
+ }
+ #endif
+ 
  static void
  report_op_error (symbolS *symp, symbolS *left, symbolS *right)
  {
*************** resolve_symbol_value (symbolS *symp)
*** 983,988 ****
--- 1046,1098 ----
        final_val = 0;
        resolved = 1;
      }
+ #ifdef OBJ_COMPLEX_RELC
+   else if (final_seg == expr_section
+ 	   && use_complex_relocs_for (symp))
+     {
+       symbolS * relc_symbol = NULL;
+       char * relc_symbol_name = NULL;
+ 
+       relc_symbol_name = symbol_relc_make_expr (& symp->sy_value);
+ 
+       /* For debugging, print out conversion input & output.  */
+ #ifdef DEBUG_SYMS
+       print_expr (& symp->sy_value);
+       if (relc_symbol_name)
+ 	fprintf (stderr, "-> relc symbol: %s\n", relc_symbol_name);
+ #endif
+ 
+       if (relc_symbol_name != NULL)
+ 	relc_symbol = symbol_new (relc_symbol_name, undefined_section,
+ 				  0, & zero_address_frag);
+ 
+       if (relc_symbol == NULL)
+ 	{
+ 	  as_bad (_("cannot convert expression symbol %s to complex relocation"),
+ 		  S_GET_NAME (symp));
+ 	  resolved = 0;
+ 	}
+       else
+ 	{
+ 	  symbol_table_insert (relc_symbol);
+ 
+  	  /* S_CLEAR_EXTERNAL (relc_symbol); */
+ 	  if (symp->bsym->flags & BSF_SRELC)
+ 	    relc_symbol->bsym->flags |= BSF_SRELC;
+ 	  else
+ 	    relc_symbol->bsym->flags |= BSF_RELC;	  
+ 	  /* symp->bsym->flags |= BSF_RELC; */
+ 	  copy_symbol_attributes (symp, relc_symbol);
+ 	  symp->sy_value.X_op = O_symbol;
+ 	  symp->sy_value.X_add_symbol = relc_symbol;
+ 	  symp->sy_value.X_add_number = 0;
+ 	  resolved = 1;
+ 	}
+ 
+       final_seg = undefined_section;
+       goto exit_dont_set_value;
+     }
+ #endif
    else
      {
        symbolS *add_symbol, *op_symbol;
*************** symbol_print_statistics (FILE *file)
*** 2827,2829 ****
--- 2937,3155 ----
    fprintf (file, "%lu mini local symbols created, %lu converted\n",
  	   local_symbol_count, local_symbol_conversion_count);
  }
+ 
+ #ifdef OBJ_COMPLEX_RELC
+ 
+ /* Convert given symbol to a new complex-relocation symbol name.  This
+    may bee a recursive function, since it might be called for non-leaf
+    nodes (plain symbols) in the expression tree.  The caller owns the
+    returning string, so should free() it eventually.  Errors are
+    indicated via as_bad() and a NULL return value.  The given symbol
+    is marked with sy_used_in_reloc.  */
+ 
+ char *
+ symbol_relc_make_sym (symbolS * sym)
+ {
+   char * terminal = NULL;
+   const char * sname;
+   char typetag;
+   int sname_len;
+ 
+   assert (sym != NULL);
+ 
+   /* Recurse to symbol_relc_make_expr if this symbol
+      is defined as an expression or a plain value.  */
+   if (   S_GET_SEGMENT (sym) == expr_section
+       || S_GET_SEGMENT (sym) == absolute_section)
+     return symbol_relc_make_expr (& sym->sy_value);
+ 
+   /* This may be a "fake symbol" L0\001, referring to ".".
+      Write out a special null symbol to refer to this position.  */
+   if (! strcmp (S_GET_NAME (sym), FAKE_LABEL_NAME))
+     return xstrdup (".");
+ 
+   /* We hope this is a plain leaf symbol.  Construct the encoding
+      as {S,s}II...:CCCCCCC....
+      where 'S'/'s' means section symbol / plain symbol
+      III is decimal for the symbol name length
+      CCC is the symbol name itself.  */
+   symbol_mark_used_in_reloc (sym);
+ 
+   sname = S_GET_NAME (sym);
+   sname_len = strlen (sname);
+   typetag = symbol_section_p (sym) ? 'S' : 's';
+ 
+   terminal = xmalloc (1 /* S or s */
+ 		      + 8 /* sname_len in decimal */
+ 		      + 1 /* _ spacer */
+ 		      + sname_len /* name itself */
+ 		      + 1 /* \0 */ );
+ 
+   sprintf (terminal, "%c%d:%s", typetag, sname_len, sname);
+   return terminal;
+ }
+ 
+ /* Convert given value to a new complex-relocation symbol name.  This
+    is a non-recursive function, since it is be called for leaf nodes
+    (plain values) in the expression tree.  The caller owns the
+    returning string, so should free() it eventually.  No errors.  */
+ 
+ char *
+ symbol_relc_make_value (offsetT val)
+ {
+   char * terminal = xmalloc (28);  /* Enough for long long.  */
+ 
+   terminal[0] = '#';
+   sprintf_vma (& terminal[1], val);
+   return terminal;
+ }
+ 
+ /* Convert given expression to a new complex-relocation symbol name.
+    This is a recursive function, since it traverses the entire given
+    expression tree.  The caller owns the returning string, so should
+    free() it eventually.  Errors are indicated via as_bad() and a NULL
+    return value.  */
+ 
+ char *
+ symbol_relc_make_expr (expressionS * exp)
+ {
+   char * opstr = NULL; /* Operator prefix string.  */
+   int    arity = 0;    /* Arity of this operator.  */
+   char * operands[3];  /* Up to three operands.  */
+   char * concat_string = NULL;
+ 
+   operands[0] = operands[1] = operands[2] = NULL;
+ 
+   assert (exp != NULL);
+ 
+   /* Match known operators -> fill in opstr, arity, operands[] and fall
+      through to construct subexpression fragments; may instead return 
+      string directly for leaf nodes.  */
+ 
+   /* See expr.h for the meaning of all these enums.  Many operators 
+      have an unnatural arity (X_add_number implicitly added).  The
+      conversion logic expands them to explicit "+" subexpressions.   */
+ 
+   switch (exp->X_op)
+     {
+     default:
+       as_bad ("Unknown expression operator (enum %d)", exp->X_op);
+       break;
+ 
+       /* Leaf nodes.  */
+     case O_constant:
+       return symbol_relc_make_value (exp->X_add_number);
+ 
+     case O_symbol:
+       if (exp->X_add_number) 
+ 	{ 
+ 	  arity = 2; 
+ 	  opstr = "+"; 
+ 	  operands[0] = symbol_relc_make_sym (exp->X_add_symbol);
+ 	  operands[1] = symbol_relc_make_value (exp->X_add_number);
+ 	  break;
+ 	}
+       else
+ 	return symbol_relc_make_sym (exp->X_add_symbol);
+ 
+       /* Helper macros for nesting nodes.  */
+ 
+ #define HANDLE_XADD_OPT1(str_) 						\
+       if (exp->X_add_number)						\
+         {								\
+           arity = 2;							\
+           opstr = "+:" str_;						\
+           operands[0] = symbol_relc_make_sym (exp->X_add_symbol);	\
+           operands[1] = symbol_relc_make_value (exp->X_add_number);	\
+           break;							\
+         }								\
+       else								\
+         {								\
+           arity = 1;							\
+           opstr = str_;							\
+           operands[0] = symbol_relc_make_sym (exp->X_add_symbol);	\
+         }								\
+       break
+       
+ #define HANDLE_XADD_OPT2(str_) 						\
+       if (exp->X_add_number)						\
+         {								\
+           arity = 3;							\
+           opstr = "+:" str_;						\
+           operands[0] = symbol_relc_make_sym (exp->X_add_symbol);	\
+           operands[1] = symbol_relc_make_sym (exp->X_op_symbol);	\
+           operands[2] = symbol_relc_make_value (exp->X_add_number);	\
+         }								\
+       else								\
+         {								\
+           arity = 2;							\
+           opstr = str_;							\
+           operands[0] = symbol_relc_make_sym (exp->X_add_symbol);	\
+           operands[1] = symbol_relc_make_sym (exp->X_op_symbol);	\
+         } 								\
+       break
+ 
+       /* Nesting nodes.  */
+ 
+     case O_uminus:       	HANDLE_XADD_OPT1 ("0-");
+     case O_bit_not:      	HANDLE_XADD_OPT1 ("~");
+     case O_logical_not:  	HANDLE_XADD_OPT1 ("!");
+     case O_multiply:     	HANDLE_XADD_OPT2 ("*");
+     case O_divide:       	HANDLE_XADD_OPT2 ("/");
+     case O_modulus:      	HANDLE_XADD_OPT2 ("%");
+     case O_left_shift:   	HANDLE_XADD_OPT2 ("<<");
+     case O_right_shift:  	HANDLE_XADD_OPT2 (">>");
+     case O_bit_inclusive_or:	HANDLE_XADD_OPT2 ("|");
+     case O_bit_exclusive_or:	HANDLE_XADD_OPT2 ("^");
+     case O_bit_and:      	HANDLE_XADD_OPT2 ("&");
+     case O_add:          	HANDLE_XADD_OPT2 ("+");
+     case O_subtract:     	HANDLE_XADD_OPT2 ("-");
+     case O_eq:           	HANDLE_XADD_OPT2 ("==");
+     case O_ne:           	HANDLE_XADD_OPT2 ("!=");
+     case O_lt:           	HANDLE_XADD_OPT2 ("<");
+     case O_le:           	HANDLE_XADD_OPT2 ("<=");
+     case O_ge:           	HANDLE_XADD_OPT2 (">=");
+     case O_gt:           	HANDLE_XADD_OPT2 (">");
+     case O_logical_and:  	HANDLE_XADD_OPT2 ("&&");
+     case O_logical_or:   	HANDLE_XADD_OPT2 ("||");
+     }
+ 
+   /* Validate & reject early.  */
+   if (arity >= 1 && ((operands[0] == NULL) || (strlen (operands[0]) == 0)))
+     opstr = NULL;
+   if (arity >= 2 && ((operands[1] == NULL) || (strlen (operands[1]) == 0)))
+     opstr = NULL;
+   if (arity >= 3 && ((operands[2] == NULL) || (strlen (operands[2]) == 0)))
+     opstr = NULL;
+ 
+   if (opstr == NULL)
+     concat_string = NULL;
+   else
+     {
+       /* Allocate new string; include inter-operand padding gaps etc.  */
+       concat_string = xmalloc (strlen (opstr) 
+ 			       + 1
+ 			       + (arity >= 1 ? (strlen (operands[0]) + 1 ) : 0)
+ 			       + (arity >= 2 ? (strlen (operands[1]) + 1 ) : 0)
+ 			       + (arity >= 3 ? (strlen (operands[2]) + 0 ) : 0)
+ 			       + 1);
+       assert (concat_string != NULL);
+       
+       /* Format the thing.  */
+       sprintf (concat_string, 
+ 	       (arity == 0 ? "%s" :
+ 		arity == 1 ? "%s:%s" :
+ 		arity == 2 ? "%s:%s:%s" :
+ 		/* arity == 3 */ "%s:%s:%s:%s"),
+ 	       opstr, operands[0], operands[1], operands[2]);
+     }
+ 
+   /* Free operand strings (not opstr).  */
+   if (arity >= 1) xfree (operands[0]);
+   if (arity >= 2) xfree (operands[1]);
+   if (arity >= 3) xfree (operands[2]);
+ 
+   return concat_string;
+ }
+ 
+ #endif
Index: gas/symbols.h
===================================================================
RCS file: /cvs/src/src/gas/symbols.h,v
retrieving revision 1.28
diff -c -p -r1.28 symbols.h
*** gas/symbols.h	15 Nov 2006 15:59:25 -0000	1.28
--- gas/symbols.h	11 Jan 2007 19:09:45 -0000
*************** extern int symbol_table_frozen;
*** 35,40 ****
--- 35,43 ----
     default.  */
  extern int symbols_case_sensitive;
  
+ char * symbol_relc_make_expr  (expressionS *);
+ char * symbol_relc_make_sym   (symbolS *);
+ char * symbol_relc_make_value (offsetT);
  char *decode_local_label_name (char *s);
  symbolS *symbol_find (const char *name);
  symbolS *symbol_find_noref (const char *name, int noref);
Index: gas/write.h
===================================================================
RCS file: /cvs/src/src/gas/write.h,v
retrieving revision 1.15
diff -c -p -r1.15 write.h
*** gas/write.h	7 May 2006 23:03:48 -0000	1.15
--- gas/write.h	11 Jan 2007 19:09:50 -0000
*************** struct fix
*** 132,137 ****
--- 132,141 ----
      const struct cgen_insn *insn;
      /* Target specific data, usually reloc number.  */
      int opinfo;
+     /* Which ifield this fixup applies to. */
+     struct cgen_maybe_multi_ifield * field;
+     /* is this field is the MSB field in a set? */
+     int msb_field_p;
    } fx_cgen;
  #endif
  
Index: include/elf/common.h
===================================================================
RCS file: /cvs/src/src/include/elf/common.h,v
retrieving revision 1.80
diff -c -p -r1.80 common.h
*** include/elf/common.h	25 Oct 2006 06:49:18 -0000	1.80
--- include/elf/common.h	11 Jan 2007 19:10:16 -0000
***************
*** 475,485 ****
--- 475,489 ----
  #define STT_FILE	4		/* Symbol gives a file name */
  #define STT_COMMON	5		/* An uninitialised common block */
  #define STT_TLS		6		/* Thread local data object */
+ #define STT_RELC        8               /* Complex relocation expression */
+ #define STT_SRELC       9               /* Signed Complex relocation expression */
  #define STT_LOOS	10		/* OS-specific semantics */
  #define STT_HIOS	12		/* OS-specific semantics */
  #define STT_LOPROC	13		/* Application-specific semantics */
  #define STT_HIPROC	15		/* Application-specific semantics */
  
+ #define R_RELC          0xff            /* Complex relocation type code (see ELFXX_R_TYPE()) */
+ 
  /* Special section indices, which may show up in st_shndx fields, among
     other places.  */
  

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]