aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gas/ChangeLog12
-rw-r--r--gas/doc/as.texinfo15
-rw-r--r--gas/read.c109
-rw-r--r--gas/write.c118
-rw-r--r--gas/write.h24
5 files changed, 278 insertions, 0 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog
index 8b9bf90..bcddbf1 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,3 +1,15 @@
+2007-03-26 Alan Modra <amodra@bigpond.net.au>
+
+ * 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 <paul@codesourcery.com>
* 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 41c80a9..01f2bf8 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 c622eb2..5782f23 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 df858a3..29ea284 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 6e691a2..2cc1bdd 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);