From 05e9452c8210da86b07b621562f4a89bd6f637d4 Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Mon, 26 Mar 2007 12:23:48 +0000 Subject: [PATCH] * doc/as.texinfo (Reloc): Document. * read.c (potable): Add "reloc". (s_reloc): New function. * write.c (reloc_list): New global var. (resolve_reloc_expr_symbols): New function. (write_object_file): Call it. (write_relocs): Process reloc_list. * write.h (struct reloc_list): New. (reloc_list): Declare. --- gas/ChangeLog | 12 +++++ gas/doc/as.texinfo | 15 ++++++ gas/read.c | 109 +++++++++++++++++++++++++++++++++++++++++ gas/write.c | 118 +++++++++++++++++++++++++++++++++++++++++++++ gas/write.h | 24 +++++++++ 5 files changed, 278 insertions(+) diff --git a/gas/ChangeLog b/gas/ChangeLog index 8b9bf906909..bcddbf17228 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,15 @@ +2007-03-26 Alan Modra + + * doc/as.texinfo (Reloc): Document. + * read.c (potable): Add "reloc". + (s_reloc): New function. + * write.c (reloc_list): New global var. + (resolve_reloc_expr_symbols): New function. + (write_object_file): Call it. + (write_relocs): Process reloc_list. + * write.h (struct reloc_list): New. + (reloc_list): Declare. + 2007-03-24 Paul Brook * config/tc-arm.c (do_t_ldmstm): Error on Thumb-2 addressing modes. diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo index 41c80a9a812..01f2bf8d528 100644 --- a/gas/doc/as.texinfo +++ b/gas/doc/as.texinfo @@ -3886,6 +3886,7 @@ Some machine configurations provide additional directives. @end ifset * Quad:: @code{.quad @var{bignums}} +* Reloc:: @code{.reloc @var{offset}, @var{reloc_name}[, @var{expression}]} * Rept:: @code{.rept @var{count}} * Sbttl:: @code{.sbttl "@var{subheading}"} @ifset COFF @@ -5433,6 +5434,20 @@ warning message; and just takes the lowest order 16 bytes of the bignum. @cindex integer, 16-byte @end ifset +@node Reloc +@section @code{.reloc @var{offset}, @var{reloc_name}[, @var{expression}]} + +@cindex @code{reloc} directive +Generate a relocation at @var{offset} of type @var{reloc_name} with value +@var{expression}. If @var{offset} is a number, the relocation is generated in +the current section. If @var{offset} is an expression that resolves to a +symbol plus offset, the relocation is generated in the given symbol's section. +@var{expression}, if present, must resolve to a symbol plus addend or to an +absolute value, but note that not all targets support an addend. e.g. ELF REL +targets such as i386 store an addend in the section contents rather than in the +relocation. This low level interface does not support addends stored in the +section. + @node Rept @section @code{.rept @var{count}} diff --git a/gas/read.c b/gas/read.c index c622eb26068..5782f23dfd8 100644 --- a/gas/read.c +++ b/gas/read.c @@ -213,6 +213,7 @@ static void do_align (int, char *, int, int); static void s_align (int, int); static void s_altmacro (int); static void s_bad_end (int); +static void s_reloc (int); static int hex_float (int, char *); static segT get_known_segmented_expression (expressionS * expP); static void pobegin (void); @@ -391,6 +392,7 @@ static const pseudo_typeS potable[] = { {"psize", listing_psize, 0}, /* Set paper size. */ {"purgem", s_purgem, 0}, {"quad", cons, 8}, + {"reloc", s_reloc, 0}, {"rep", s_rept, 0}, {"rept", s_rept, 0}, {"rva", s_rva, 4}, @@ -3669,6 +3671,113 @@ s_rva (int size) cons_worker (size, 1); } +/* .reloc offset, reloc_name, symbol+addend. */ + +void +s_reloc (int ignore ATTRIBUTE_UNUSED) +{ + char *stop = NULL; + char stopc = 0; + expressionS exp; + char *r_name; + int c; + struct reloc_list *reloc; + + reloc = xmalloc (sizeof (*reloc)); + + if (flag_mri) + stop = mri_comment_field (&stopc); + + expression (&exp); + switch (exp.X_op) + { + case O_illegal: + case O_absent: + case O_big: + case O_register: + as_bad (_("missing or bad offset expression")); + goto err_out; + case O_constant: + exp.X_add_symbol = section_symbol (now_seg); + exp.X_op = O_symbol; + /* Fall thru */ + case O_symbol: + if (exp.X_add_number == 0) + { + reloc->u.a.offset_sym = exp.X_add_symbol; + break; + } + /* Fall thru */ + default: + reloc->u.a.offset_sym = make_expr_symbol (&exp); + break; + } + + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + { + as_bad (_("missing reloc type")); + goto err_out; + } + + ++input_line_pointer; + SKIP_WHITESPACE (); + r_name = input_line_pointer; + c = get_symbol_end (); + reloc->u.a.howto = bfd_reloc_name_lookup (stdoutput, r_name); + *input_line_pointer = c; + if (reloc->u.a.howto == NULL) + { + as_bad (_("unrecognized reloc type")); + goto err_out; + } + + exp.X_op = O_absent; + SKIP_WHITESPACE (); + if (*input_line_pointer == ',') + { + ++input_line_pointer; + expression_and_evaluate (&exp); + } + switch (exp.X_op) + { + case O_illegal: + case O_big: + case O_register: + as_bad (_("bad reloc expression")); + err_out: + ignore_rest_of_line (); + free (reloc); + if (flag_mri) + mri_comment_end (stop, stopc); + return; + case O_absent: + reloc->u.a.sym = NULL; + reloc->u.a.addend = 0; + break; + case O_constant: + reloc->u.a.sym = NULL; + reloc->u.a.addend = exp.X_add_number; + break; + case O_symbol: + reloc->u.a.sym = exp.X_add_symbol; + reloc->u.a.addend = exp.X_add_number; + break; + default: + reloc->u.a.sym = make_expr_symbol (&exp); + reloc->u.a.addend = 0; + break; + } + + as_where (&reloc->file, &reloc->line); + reloc->next = reloc_list; + reloc_list = reloc; + + demand_empty_rest_of_line (); + if (flag_mri) + mri_comment_end (stop, stopc); +} + /* Put the contents of expression EXP into the object file using NBYTES bytes. If need_pass_2 is 1, this does nothing. */ diff --git a/gas/write.c b/gas/write.c index df858a3c5bd..29ea2843857 100644 --- a/gas/write.c +++ b/gas/write.c @@ -117,6 +117,9 @@ symbolS *abs_section_sym; /* Remember the value of dot when parsing expressions. */ addressT dot_value; +/* Relocs generated by ".reloc" pseudo. */ +struct reloc_list* reloc_list; + void print_fixup (fixS *); /* We generally attach relocs to frag chains. However, after we have @@ -624,6 +627,86 @@ dump_section_relocs (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, FILE *stream) #define EMIT_SECTION_SYMBOLS 1 #endif +/* Resolve U.A.OFFSET_SYM and U.A.SYM fields of RELOC_LIST entries, + and check for validity. Convert RELOC_LIST from using U.A fields + to U.B fields. */ +static void +resolve_reloc_expr_symbols (void) +{ + struct reloc_list *r; + + for (r = reloc_list; r; r = r->next) + { + expressionS *symval; + symbolS *sym; + bfd_vma offset, addend; + asection *sec; + reloc_howto_type *howto; + + resolve_symbol_value (r->u.a.offset_sym); + symval = symbol_get_value_expression (r->u.a.offset_sym); + + offset = 0; + sym = NULL; + if (symval->X_op == O_constant) + sym = r->u.a.offset_sym; + else if (symval->X_op == O_symbol) + { + sym = symval->X_add_symbol; + offset = symval->X_add_number; + symval = symbol_get_value_expression (symval->X_add_symbol); + } + if (sym == NULL + || symval->X_op != O_constant + || (sec = S_GET_SEGMENT (sym)) == NULL + || !SEG_NORMAL (sec)) + { + as_bad_where (r->file, r->line, _("invalid offset expression")); + sec = NULL; + } + else + offset += S_GET_VALUE (sym); + + sym = NULL; + addend = r->u.a.addend; + if (r->u.a.sym != NULL) + { + resolve_symbol_value (r->u.a.sym); + symval = symbol_get_value_expression (r->u.a.sym); + if (symval->X_op == O_constant) + sym = r->u.a.sym; + else if (symval->X_op == O_symbol) + { + sym = symval->X_add_symbol; + addend += symval->X_add_number; + symval = symbol_get_value_expression (symval->X_add_symbol); + } + if (symval->X_op != O_constant) + { + as_bad_where (r->file, r->line, _("invalid reloc expression")); + sec = NULL; + } + else if (sym != NULL) + symbol_mark_used_in_reloc (sym); + } + if (sym == NULL) + { + if (abs_section_sym == NULL) + abs_section_sym = section_symbol (absolute_section); + sym = abs_section_sym; + } + + howto = r->u.a.howto; + + r->u.b.sec = sec; + r->u.b.s = symbol_get_bfdsym (sym); + r->u.b.r.sym_ptr_ptr = &r->u.b.s; + r->u.b.r.address = offset; + r->u.b.r.addend = addend; + r->u.b.r.howto = howto; + } +} + /* This pass over fixups decides whether symbols can be replaced with section symbols. */ @@ -1027,6 +1110,7 @@ write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED) segment_info_type *seginfo = seg_info (sec); unsigned int i; unsigned int n; + struct reloc_list *my_reloc_list, **rp, *r; arelent **relocs; fixS *fixp; @@ -1044,6 +1128,22 @@ write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED) n *= MAX_RELOC_EXPANSION; #endif + /* Extract relocs for this section from reloc_list. */ + rp = &reloc_list; + my_reloc_list = NULL; + while ((r = *rp) != NULL) + { + if (r->u.b.sec == sec) + { + *rp = r->next; + r->next = my_reloc_list; + my_reloc_list = r; + n++; + } + else + rp = &r->next; + } + relocs = xcalloc (n, sizeof (arelent *)); i = 0; @@ -1107,6 +1207,23 @@ write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED) } #endif + for (r = my_reloc_list; r != NULL; r = r->next) + { + fragS *f; + for (f = seginfo->frchainP->frch_root; f; f = f->fr_next) + if (f->fr_address <= r->u.b.r.address + && r->u.b.r.address < f->fr_address + f->fr_fix) + break; + if (f == NULL) + as_bad_where (r->file, r->line, + _("reloc not within (fixed part of) section")); + else + { + relocs[n++] = &r->u.b.r; + install_reloc (sec, &r->u.b.r, f, r->file, r->line); + } + } + if (n) { flagword flags = bfd_get_section_flags (abfd, sec); @@ -1564,6 +1681,7 @@ write_object_file (void) resolve_symbol_value (symp); } resolve_local_symbol_values (); + resolve_reloc_expr_symbols (); PROGRESS (1); diff --git a/gas/write.h b/gas/write.h index 6e691a22a35..2cc1bdd73a5 100644 --- a/gas/write.h +++ b/gas/write.h @@ -142,11 +142,35 @@ struct fix typedef struct fix fixS; +struct reloc_list +{ + struct reloc_list *next; + union + { + struct + { + symbolS *offset_sym; + reloc_howto_type *howto; + symbolS *sym; + bfd_vma addend; + } a; + struct + { + asection *sec; + asymbol *s; + arelent r; + } b; + } u; + char *file; + unsigned int line; +}; + extern int finalize_syms; extern symbolS *abs_section_sym; extern addressT dot_value; extern long string_byte_count; extern int section_alignment[]; +extern struct reloc_list* reloc_list; extern void append (char **charPP, char *fromP, unsigned long length); extern void record_alignment (segT seg, int align);