/* tc-ia64.c -- Assembler for the HP/Intel IA-64 architecture.
- Copyright 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ Copyright 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
This file is part of GAS, the GNU Assembler.
- optional operands
- directives:
- .alias
.eb
.estate
.lb
extern int target_big_endian;
+void (*ia64_number_to_chars) PARAMS ((char *, valueT, int));
+
+static void ia64_float_to_chars_bigendian
+ PARAMS ((char *, LITTLENUM_TYPE *, int));
+static void ia64_float_to_chars_littleendian
+ PARAMS ((char *, LITTLENUM_TYPE *, int));
+static void (*ia64_float_to_chars)
+ PARAMS ((char *, LITTLENUM_TYPE *, int));
+
+static struct hash_control *alias_hash;
+static struct hash_control *alias_name_hash;
+static struct hash_control *secalias_hash;
+static struct hash_control *secalias_name_hash;
+
/* Characters which always start a comment. */
const char comment_chars[] = "";
{ "pause", PSEUDO_FUNC_CONST, { 0x0 } },
/* unwind-related constants: */
- { "svr4", PSEUDO_FUNC_CONST, { 0 } },
- { "hpux", PSEUDO_FUNC_CONST, { 1 } },
- { "nt", PSEUDO_FUNC_CONST, { 2 } },
+ { "svr4", PSEUDO_FUNC_CONST, { ELFOSABI_NONE } },
+ { "hpux", PSEUDO_FUNC_CONST, { ELFOSABI_HPUX } },
+ { "nt", PSEUDO_FUNC_CONST, { 2 } }, /* conflicts w/ELFOSABI_NETBSD */
+ { "linux", PSEUDO_FUNC_CONST, { ELFOSABI_LINUX } },
+ { "freebsd", PSEUDO_FUNC_CONST, { ELFOSABI_FREEBSD } },
+ { "openvms", PSEUDO_FUNC_CONST, { ELFOSABI_OPENVMS } },
+ { "nsk", PSEUDO_FUNC_CONST, { ELFOSABI_NSK } },
/* unwind-related registers: */
{ "priunat",PSEUDO_FUNC_REG, { REG_PRIUNAT } }
unwind_record r;
unsigned long slot_number;
fragS *slot_frag;
+ unsigned long next_slot_number;
+ fragS *next_slot_frag;
struct unw_rec_list *next;
} unw_rec_list;
static struct
{
- unsigned long next_slot_number;
- fragS *next_slot_frag;
-
/* Maintain a list of unwind entries for the current function. */
unw_rec_list *list;
unw_rec_list *tail;
typedef void (*vbyte_func) PARAMS ((int, char *, char *));
-/* Forward delarations: */
+/* Forward declarations: */
static int ar_is_in_integer_unit PARAMS ((int regnum));
static void set_section PARAMS ((char *name));
static unsigned int set_regstack PARAMS ((unsigned int, unsigned int,
static void output_X3_format PARAMS ((vbyte_func, unw_record_type, int, int, int, unsigned long,
unsigned long));
static void output_X4_format PARAMS ((vbyte_func, int, int, int, int, int, int, unsigned long));
-static void free_list_records PARAMS ((unw_rec_list *));
static unw_rec_list *output_prologue PARAMS ((void));
static unw_rec_list *output_prologue_gr PARAMS ((unsigned int, unsigned int));
static unw_rec_list *output_body PARAMS ((void));
unsigned long, fragS *));
static unw_rec_list *optimize_unw_records PARAMS ((unw_rec_list *));
static void fixup_unw_records PARAMS ((unw_rec_list *));
-static int output_unw_records PARAMS ((unw_rec_list *, void **));
static int convert_expr_to_ab_reg PARAMS ((expressionS *, unsigned int *, unsigned int *));
static int convert_expr_to_xy_reg PARAMS ((expressionS *, unsigned int *, unsigned int *));
-static int generate_unwind_image PARAMS ((const char *));
+static void generate_unwind_image PARAMS ((const char *));
static unsigned int get_saved_prologue_count PARAMS ((unsigned long));
static void save_prologue_count PARAMS ((unsigned long, unsigned int));
static void free_saved_prologue_counts PARAMS ((void));
{
if (letter == 's')
return SHF_IA_64_SHORT;
+ else if (letter == 'o')
+ return SHF_LINK_ORDER;
- *ptr_msg = _("Bad .section directive: want a,s,w,x,M,S,G,T in string");
- return 0;
+ *ptr_msg = _("Bad .section directive: want a,o,s,w,x,M,S,G,T in string");
+ return -1;
}
/* Map SHF_IA_64_SHORT to SEC_SMALL_DATA. */
if (STREQ (ELF_STRING_ia64_unwind_once))
return SHT_IA_64_UNWIND;
+ if (STREQ ("unwind"))
+ return SHT_IA_64_UNWIND;
+
if (STREQ ("init_array"))
return SHT_INIT_ARRAY;
ptr->next = NULL;
ptr->slot_number = SLOT_NUM_NOT_SET;
ptr->r.type = t;
+ ptr->next_slot_number = 0;
+ ptr->next_slot_frag = 0;
return ptr;
}
-/* This function frees an entire list of record structures. */
-
-void
-free_list_records (unw_rec_list *first)
-{
- unw_rec_list *ptr;
- for (ptr = first; ptr != NULL;)
- {
- unw_rec_list *tmp = ptr;
-
- if ((tmp->r.type == prologue || tmp->r.type == prologue_gr)
- && tmp->r.record.r.mask.i)
- free (tmp->r.record.r.mask.i);
-
- ptr = ptr->next;
- free (tmp);
- }
-}
-
static unw_rec_list *
output_prologue ()
{
{
unsigned long start_addr = (unsigned long) &first_frag->fr_literal;
+ if (finalize_syms)
+ {
+ /* We can get the final addresses only after relaxation is
+ done. */
+ if (first_frag->fr_next && first_frag->fr_next->fr_address)
+ index += 3 * ((first_frag->fr_next->fr_address
+ - first_frag->fr_address
+ - first_frag->fr_fix) >> 4);
+ }
+ else
+ /* We don't know what the final addresses will be. We try our
+ best to estimate. */
+ switch (first_frag->fr_type)
+ {
+ default:
+ break;
+
+ case rs_space:
+ as_fatal ("only constant space allocation is supported");
+ break;
+
+ case rs_align:
+ case rs_align_code:
+ case rs_align_test:
+ /* Take alignment into account. Assume the worst case
+ before relaxation. */
+ index += 3 * ((1 << first_frag->fr_offset) >> 4);
+ break;
+
+ case rs_org:
+ if (first_frag->fr_symbol)
+ {
+ as_fatal ("only constant offsets are supported");
+ break;
+ }
+ case rs_fill:
+ index += 3 * (first_frag->fr_offset >> 4);
+ break;
+ }
+
/* Add in the full size of the frag converted to instruction slots. */
index += 3 * (first_frag->fr_fix >> 4);
/* Subtract away the initial part before first_addr. */
/* Given a complete record list, process any records which have
unresolved fields, (ie length counts for a prologue). After
- this has been run, all neccessary information should be available
+ this has been run, all necessary information should be available
within each record to generate an image. */
static void
first_addr = ptr->slot_number;
first_frag = ptr->slot_frag;
- ptr->slot_number = 0;
/* Find either the next body/prologue start, or the end of
the list, and determine the size of the region. */
- last_addr = unwind.next_slot_number;
- last_frag = unwind.next_slot_frag;
+ last_addr = list->next_slot_number;
+ last_frag = list->next_slot_frag;
for (last = ptr->next; last != NULL; last = last->next)
if (last->r.type == prologue || last->r.type == prologue_gr
|| last->r.type == body)
}
}
-/* Helper routine for output_unw_records. Emits the header for the unwind
- info. */
-
-static int
-setup_unwind_header (int size, unsigned char **mem)
+/* This function converts a rs_machine_dependent variant frag into a
+ normal fill frag with the unwind image from the the record list. */
+void
+ia64_convert_frag (fragS *frag)
{
- int x, extra = 0;
+ unw_rec_list *list;
+ int len, size, pad;
valueT flag_value;
- /* pad to pointer-size boundry. */
- x = size % md.pointer_size;
- if (x != 0)
- extra = md.pointer_size - x;
+ list = (unw_rec_list *) frag->fr_opcode;
+ fixup_unw_records (list);
- /* Add 8 for the header + a pointer for the
- personality offset. */
- *mem = xmalloc (size + extra + 8 + md.pointer_size);
+ len = calc_record_size (list);
+ /* pad to pointer-size boundary. */
+ pad = len % md.pointer_size;
+ if (pad != 0)
+ len += md.pointer_size - pad;
+ /* Add 8 for the header + a pointer for the personality offset. */
+ size = len + 8 + md.pointer_size;
- /* Clear the padding area and personality. */
- memset (*mem + 8 + size, 0, extra + md.pointer_size);
+ /* fr_var carries the max_chars that we created the fragment with.
+ We must, of course, have allocated enough memory earlier. */
+ assert (frag->fr_var >= size);
- /* Initialize the header area. */
- if (unwind.personality_routine)
+ /* Initialize the header area. fr_offset is initialized with
+ unwind.personality_routine. */
+ if (frag->fr_offset)
{
if (md.flags & EF_IA_64_ABI64)
flag_value = (bfd_vma) 3 << 32;
else
flag_value = 0;
- md_number_to_chars (*mem, (((bfd_vma) 1 << 48) /* Version. */
- | flag_value /* U & E handler flags. */
- | ((size + extra) / md.pointer_size)), /* Length. */
- 8);
-
- return extra;
-}
-
-/* Generate an unwind image from a record list. Returns the number of
- bytes in the resulting image. The memory image itselof is returned
- in the 'ptr' parameter. */
-static int
-output_unw_records (list, ptr)
- unw_rec_list *list;
- void **ptr;
-{
- int size, extra;
- unsigned char *mem;
-
- *ptr = NULL;
-
- list = optimize_unw_records (list);
- fixup_unw_records (list);
- size = calc_record_size (list);
-
- if (size > 0 || unwind.force_unwind_entry)
- {
- unwind.force_unwind_entry = 0;
- extra = setup_unwind_header (size, &mem);
-
- vbyte_mem_ptr = mem + 8;
- process_unw_records (list, output_vbyte_mem);
-
- *ptr = mem;
+ md_number_to_chars (frag->fr_literal,
+ (((bfd_vma) 1 << 48) /* Version. */
+ | flag_value /* U & E handler flags. */
+ | (len / md.pointer_size)), /* Length. */
+ 8);
- size += extra + 8 + md.pointer_size;
- }
- return size;
+ /* Skip the header. */
+ vbyte_mem_ptr = frag->fr_literal + 8;
+ process_unw_records (list, output_vbyte_mem);
+ frag->fr_fix += size;
+ frag->fr_type = rs_fill;
+ frag->fr_var = 0;
+ frag->fr_offset = 0;
}
static int
add_unwind_entry (output_psp_sprel (e.X_add_number));
}
else
- as_bad ("First operand to .vframesp must be a general register");
+ as_bad ("Operand to .vframesp must be a constant (sp-relative offset)");
}
static void
add_unwind_entry (output_psp_sprel (e.X_add_number));
}
else
- as_bad ("First operand to .vframepsp must be a general register");
+ as_bad ("Operand to .vframepsp must be a constant (psp-relative offset)");
}
static void
add_unwind_entry (output_spill_reg_p (ab, reg, 0, 0, qp));
}
-static int
+static void
generate_unwind_image (text_name)
const char *text_name;
{
- int size;
- unsigned char *unw_rec;
+ int size, pad;
+ unw_rec_list *list;
/* Force out pending instructions, to make sure all unwind records have
a valid slot_number field. */
ia64_flush_insns ();
/* Generate the unwind record. */
- size = output_unw_records (unwind.list, (void **) &unw_rec);
- if (size % md.pointer_size != 0)
- as_bad ("Unwind record is not a multiple of %d bytes.", md.pointer_size);
+ list = optimize_unw_records (unwind.list);
+ fixup_unw_records (list);
+ size = calc_record_size (list);
+
+ if (size > 0 || unwind.force_unwind_entry)
+ {
+ unwind.force_unwind_entry = 0;
+ /* pad to pointer-size boundary. */
+ pad = size % md.pointer_size;
+ if (pad != 0)
+ size += md.pointer_size - pad;
+ /* Add 8 for the header + a pointer for the personality
+ offset. */
+ size += 8 + md.pointer_size;
+ }
/* If there are unwind records, switch sections, and output the info. */
if (size != 0)
{
- unsigned char *where;
char *sec_name;
expressionS exp;
bfd_reloc_code_real_type reloc;
/* Set expression which points to start of unwind descriptor area. */
unwind.info = expr_build_dot ();
-
- where = (unsigned char *) frag_more (size);
-
- /* Issue a label for this address, and keep track of it to put it
- in the unwind section. */
-
- /* Copy the information from the unwind record into this section. The
- data is already in the correct byte order. */
- memcpy (where, unw_rec, size);
+
+ frag_var (rs_machine_dependent, size, size, 0, 0,
+ (offsetT) unwind.personality_routine, (char *) list);
/* Add the personality address to the image. */
if (unwind.personality_routine != 0)
}
}
- free_list_records (unwind.list);
free_saved_prologue_counts ();
unwind.list = unwind.tail = unwind.current_entry = NULL;
-
- return size;
}
static void
dot_byteorder (byteorder)
int byteorder;
{
- target_big_endian = byteorder;
+ segment_info_type *seginfo = seg_info (now_seg);
+
+ if (byteorder == -1)
+ {
+ if (seginfo->tc_segment_info_data.endian == 0)
+ seginfo->tc_segment_info_data.endian
+ = TARGET_BYTES_BIG_ENDIAN ? 1 : 2;
+ byteorder = seginfo->tc_segment_info_data.endian == 1;
+ }
+ else
+ seginfo->tc_segment_info_data.endian = byteorder ? 1 : 2;
+
+ if (target_big_endian != byteorder)
+ {
+ target_big_endian = byteorder;
+ if (target_big_endian)
+ {
+ ia64_number_to_chars = number_to_chars_bigendian;
+ ia64_float_to_chars = ia64_float_to_chars_bigendian;
+ }
+ else
+ {
+ ia64_number_to_chars = number_to_chars_littleendian;
+ ia64_float_to_chars = ia64_float_to_chars_littleendian;
+ }
+ }
}
static void
demand_empty_rest_of_line ();
}
-static void
-dot_alias (dummy)
- int dummy ATTRIBUTE_UNUSED;
-{
- as_bad (".alias not implemented yet");
-}
-
static void
dot_ln (dummy)
int dummy ATTRIBUTE_UNUSED;
stmt_float_cons (kind)
int kind;
{
- size_t size;
+ size_t alignment;
switch (kind)
{
- case 'd': size = 8; break;
- case 'x': size = 10; break;
+ case 'd':
+ alignment = 8;
+ break;
+
+ case 'x':
+ case 'X':
+ alignment = 16;
+ break;
case 'f':
default:
- size = 4;
+ alignment = 4;
break;
}
- ia64_do_align (size);
+ ia64_do_align (alignment);
float_cons (kind);
}
{ "body", dot_body, 0 },
{ "prologue", dot_prologue, 0 },
{ "endp", dot_endp, 0 },
- { "file", (void (*) PARAMS ((int))) dwarf2_directive_file, 0 },
- { "loc", dwarf2_directive_loc, 0 },
{ "fframe", dot_fframe, 0 },
{ "vframe", dot_vframe, 0 },
{ "msb", dot_byteorder, 1 },
{ "psr", dot_psr, 0 },
{ "alias", dot_alias, 0 },
+ { "secalias", dot_alias, 1 },
{ "ln", dot_ln, 0 }, /* source line info (for debugging) */
{ "xdata1", dot_xdata, 1 },
{ "xreal4", dot_xfloat_cons, 'f' },
{ "xreal8", dot_xfloat_cons, 'd' },
{ "xreal10", dot_xfloat_cons, 'x' },
+ { "xreal16", dot_xfloat_cons, 'X' },
{ "xstring", dot_xstringer, 0 },
{ "xstringz", dot_xstringer, 1 },
{ "xreal4.ua", dot_xfloat_cons_ua, 'f' },
{ "xreal8.ua", dot_xfloat_cons_ua, 'd' },
{ "xreal10.ua", dot_xfloat_cons_ua, 'x' },
+ { "xreal16.ua", dot_xfloat_cons_ua, 'X' },
/* annotations/DV checking support */
{ "entry", dot_entry, 0 },
{ "real4", stmt_float_cons, 'f' },
{ "real8", stmt_float_cons, 'd' },
{ "real10", stmt_float_cons, 'x' },
+ { "real16", stmt_float_cons, 'X' },
{ "string", stringer, 0 },
{ "stringz", stringer, 1 },
{ "real4.ua", float_cons, 'f' },
{ "real8.ua", float_cons, 'd' },
{ "real10.ua", float_cons, 'x' },
+ { "real16.ua", float_cons, 'X' },
};
/* Declare a register by creating a symbol for it and entering it in
number_to_chars_littleendian (f + 0, t0, 8);
number_to_chars_littleendian (f + 8, t1, 8);
- unwind.next_slot_number = (unsigned long) f + 16;
- unwind.next_slot_frag = frag_now;
+ if (unwind.list)
+ {
+ unwind.list->next_slot_number = (unsigned long) f + 16;
+ unwind.list->next_slot_frag = frag_now;
+ }
}
int
bfd_set_section_alignment (stdoutput, text_section, 4);
- target_big_endian = TARGET_BYTES_BIG_ENDIAN;
+ /* Make sure function pointers get initialized. */
+ target_big_endian = -1;
+ dot_byteorder (TARGET_BYTES_BIG_ENDIAN);
+
+ alias_hash = hash_new ();
+ alias_name_hash = hash_new ();
+ secalias_hash = hash_new ();
+ secalias_name_hash = hash_new ();
+
pseudo_func[FUNC_DTP_MODULE].u.sym =
symbol_new (".<dtpmod>", undefined_section, FUNC_DTP_MODULE,
&zero_address_frag);
}
}
+/* MASK contains 2 and only 2 PRs which are mutually exclusive. Remove
+ any mutexes which contain one of the PRs and create new ones when
+ needed. */
+
+static int
+update_qp_mutex (valueT mask)
+{
+ int i;
+ int add = 0;
+
+ i = 0;
+ while (i < qp_mutexeslen)
+ {
+ if ((qp_mutexes[i].prmask & mask) != 0)
+ {
+ /* If it destroys and creates the same mutex, do nothing. */
+ if (qp_mutexes[i].prmask == mask
+ && qp_mutexes[i].path == md.path)
+ {
+ i++;
+ add = -1;
+ }
+ else
+ {
+ int keep = 0;
+
+ if (md.debug_dv)
+ {
+ fprintf (stderr, " Clearing mutex relation");
+ print_prmask (qp_mutexes[i].prmask);
+ fprintf (stderr, "\n");
+ }
+
+ /* Deal with the old mutex with more than 3+ PRs only if
+ the new mutex on the same execution path with it.
+
+ FIXME: The 3+ mutex support is incomplete.
+ dot_pred_rel () may be a better place to fix it. */
+ if (qp_mutexes[i].path == md.path)
+ {
+ /* If it is a proper subset of the mutex, create a
+ new mutex. */
+ if (add == 0
+ && (qp_mutexes[i].prmask & mask) == mask)
+ add = 1;
+
+ qp_mutexes[i].prmask &= ~mask;
+ if (qp_mutexes[i].prmask & (qp_mutexes[i].prmask - 1))
+ {
+ /* Modify the mutex if there are more than one
+ PR left. */
+ keep = 1;
+ i++;
+ }
+ }
+
+ if (keep == 0)
+ /* Remove the mutex. */
+ qp_mutexes[i] = qp_mutexes[--qp_mutexeslen];
+ }
+ }
+ else
+ ++i;
+ }
+
+ if (add == 1)
+ add_qp_mutex (mask);
+
+ return add;
+}
+
/* Remove any mutexes which contain any of the PRs indicated in the mask.
Any changes to a PR clears the mutex relations which include that PR. */
else if (idesc->operands[i] == IA64_OPND_PR_ROT)
{
if (idesc->operands[1] & ((valueT) 1 << 43))
- qp_changemask = ~(valueT) 0xFFFFFFFFFFF | idesc->operands[1];
+ qp_changemask = -((valueT) 1 << 44) | idesc->operands[1];
else
qp_changemask = idesc->operands[1];
qp_changemask &= ~(valueT) 0xFFFF;
{
int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P;
int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P;
- valueT p1mask = (valueT) 1 << p1;
- valueT p2mask = (valueT) 1 << p2;
+ valueT p1mask = (p1 != 0) ? (valueT) 1 << p1 : 0;
+ valueT p2mask = (p2 != 0) ? (valueT) 1 << p2 : 0;
- /* If one of the PRs is PR0, we can't really do anything. */
- if (p1 == 0 || p2 == 0)
+ /* If both PRs are PR0, we can't really do anything. */
+ if (p1 == 0 && p2 == 0)
{
if (md.debug_dv)
fprintf (stderr, " Ignoring PRs due to inclusion of p0\n");
else if (has_suffix_p (idesc->name, ".or.andcm")
|| has_suffix_p (idesc->name, ".and.orcm"))
{
- add_qp_mutex (p1mask | p2mask);
clear_qp_implies (p2mask, p1mask);
}
else if (has_suffix_p (idesc->name, ".andcm")
}
else
{
+ int added = 0;
+
clear_qp_implies (p1mask | p2mask, p1mask | p2mask);
- if (has_suffix_p (idesc->name, ".unc"))
+
+ /* If one of the PRs is PR0, we call clear_qp_mutex. */
+ if (p1 == 0 || p2 == 0)
+ clear_qp_mutex (p1mask | p2mask);
+ else
+ added = update_qp_mutex (p1mask | p2mask);
+
+ if (CURR_SLOT.qp_regno == 0
+ || has_suffix_p (idesc->name, ".unc"))
{
- add_qp_mutex (p1mask | p2mask);
+ if (added == 0 && p1 && p2)
+ add_qp_mutex (p1mask | p2mask);
if (CURR_SLOT.qp_regno != 0)
{
- add_qp_imply (CURR_SLOT.opnd[0].X_add_number - REG_P,
- CURR_SLOT.qp_regno);
- add_qp_imply (CURR_SLOT.opnd[1].X_add_number - REG_P,
- CURR_SLOT.qp_regno);
+ if (p1)
+ add_qp_imply (p1, CURR_SLOT.qp_regno);
+ if (p2)
+ add_qp_imply (p2, CURR_SLOT.qp_regno);
}
}
- else if (CURR_SLOT.qp_regno == 0)
- {
- add_qp_mutex (p1mask | p2mask);
- }
- else
- {
- clear_qp_mutex (p1mask | p2mask);
- }
}
}
/* Look for mov imm insns into GRs. */
break;
}
- return S_FORCE_RELOC (fix->fx_addsy);
+ return generic_force_reloc (fix);
}
/* Decide from what point a pc-relative relocation is relative to,
return off;
}
+
+/* Used to emit section-relative relocs for the dwarf2 debug data. */
+void
+ia64_dwarf2_emit_offset (symbolS *symbol, unsigned int size)
+{
+ expressionS expr;
+
+ expr.X_op = O_pseudo_fixup;
+ expr.X_op_symbol = pseudo_func[FUNC_SEC_RELATIVE].u.sym;
+ expr.X_add_number = 0;
+ expr.X_add_symbol = symbol;
+ emit_expr (&expr, size);
+}
+
/* This is called whenever some data item (not an instruction) needs a
fixup. We pick the right reloc code depending on the byteorder
currently in effect. */
code = BFD_RELOC_IA64_IPLTMSB;
else
code = BFD_RELOC_IA64_IPLTLSB;
-
exp->X_op = O_symbol;
break;
}
ignore_rest_of_line ();
return;
}
+
if (exp->X_op == O_pseudo_fixup)
{
- /* ??? */
exp->X_op = O_symbol;
code = ia64_gen_real_reloc_type (exp->X_op_symbol, code);
+ /* ??? If code unchanged, unsupported. */
}
fix = fix_new_exp (f, where, nbytes, exp, 0, code);
case FUNC_DTP_RELATIVE:
switch (r_type)
{
+ case BFD_RELOC_IA64_DIR64MSB:
+ new = BFD_RELOC_IA64_DTPREL64MSB; break;
+ case BFD_RELOC_IA64_DIR64LSB:
+ new = BFD_RELOC_IA64_DTPREL64LSB; break;
case BFD_RELOC_IA64_IMM14:
new = BFD_RELOC_IA64_DTPREL14; break;
case BFD_RELOC_IA64_IMM22:
default:
abort ();
}
+
/* Hmmmm. Should this ever occur? */
if (new)
return new;
default:
break;
}
-
- return;
}
static void
int *size;
{
LITTLENUM_TYPE words[MAX_LITTLENUMS];
- LITTLENUM_TYPE *word;
char *t;
int prec;
t = atof_ieee (input_line_pointer, type, words);
if (t)
input_line_pointer = t;
- *size = prec * sizeof (LITTLENUM_TYPE);
- for (word = words + prec - 1; prec--;)
+ (*ia64_float_to_chars) (lit, words, prec);
+
+ if (type == 'X')
{
- md_number_to_chars (lit, (long) (*word--), sizeof (LITTLENUM_TYPE));
- lit += sizeof (LITTLENUM_TYPE);
+ /* It is 10 byte floating point with 6 byte padding. */
+ memset (&lit [10], 0, 6);
+ *size = 8 * sizeof (LITTLENUM_TYPE);
}
- return 0;
-}
-
-/* Round up a section's size to the appropriate boundary. */
-valueT
-md_section_align (seg, size)
- segT seg;
- valueT size;
-{
- int align = bfd_get_section_alignment (stdoutput, seg);
- valueT mask = ((valueT) 1 << align) - 1;
+ else
+ *size = prec * sizeof (LITTLENUM_TYPE);
- return (size + mask) & ~mask;
+ return 0;
}
/* Handle ia64 specific semantics of the align directive. */
fragS *fragp;
{
/* Use mfi bundle of nops with no stop bits. */
- static const unsigned char be_nop[]
- = { 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c};
static const unsigned char le_nop[]
= { 0x0c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00};
fragp->fr_fix += fix;
}
- memcpy (p, (target_big_endian ? be_nop : le_nop), 16);
+ /* Instruction bundles are always little-endian. */
+ memcpy (p, le_nop, 16);
fragp->fr_var = 16;
}
+
+static void
+ia64_float_to_chars_bigendian (char *lit, LITTLENUM_TYPE *words,
+ int prec)
+{
+ while (prec--)
+ {
+ number_to_chars_bigendian (lit, (long) (*words++),
+ sizeof (LITTLENUM_TYPE));
+ lit += sizeof (LITTLENUM_TYPE);
+ }
+}
+
+static void
+ia64_float_to_chars_littleendian (char *lit, LITTLENUM_TYPE *words,
+ int prec)
+{
+ while (prec--)
+ {
+ number_to_chars_littleendian (lit, (long) (words[prec]),
+ sizeof (LITTLENUM_TYPE));
+ lit += sizeof (LITTLENUM_TYPE);
+ }
+}
+
+void
+ia64_elf_section_change_hook (void)
+{
+ dot_byteorder (-1);
+}
+
+/* Check if a label should be made global. */
+void
+ia64_check_label (symbolS *label)
+{
+ if (*input_line_pointer == ':')
+ {
+ S_SET_EXTERNAL (label);
+ input_line_pointer++;
+ }
+}
+
+/* Used to remember where .alias and .secalias directives are seen. We
+ will rename symbol and section names when we are about to output
+ the relocatable file. */
+struct alias
+{
+ char *file; /* The file where the directive is seen. */
+ unsigned int line; /* The line number the directive is at. */
+ const char *name; /* The orignale name of the symbol. */
+};
+
+/* Called for .alias and .secalias directives. If SECTION is 1, it is
+ .secalias. Otherwise, it is .alias. */
+static void
+dot_alias (int section)
+{
+ char *name, *alias;
+ char delim;
+ char *end_name;
+ int len;
+ const char *error_string;
+ struct alias *h;
+ const char *a;
+ struct hash_control *ahash, *nhash;
+ const char *kind;
+
+ name = input_line_pointer;
+ delim = get_symbol_end ();
+ end_name = input_line_pointer;
+ *end_name = delim;
+
+ if (name == end_name)
+ {
+ as_bad (_("expected symbol name"));
+ discard_rest_of_line ();
+ return;
+ }
+
+ SKIP_WHITESPACE ();
+
+ if (*input_line_pointer != ',')
+ {
+ *end_name = 0;
+ as_bad (_("expected comma after \"%s\""), name);
+ *end_name = delim;
+ ignore_rest_of_line ();
+ return;
+ }
+
+ input_line_pointer++;
+ *end_name = 0;
+
+ /* We call demand_copy_C_string to check if alias string is valid.
+ There should be a closing `"' and no `\0' in the string. */
+ alias = demand_copy_C_string (&len);
+ if (alias == NULL)
+ {
+ ignore_rest_of_line ();
+ return;
+ }
+
+ /* Make a copy of name string. */
+ len = strlen (name) + 1;
+ obstack_grow (¬es, name, len);
+ name = obstack_finish (¬es);
+
+ if (section)
+ {
+ kind = "section";
+ ahash = secalias_hash;
+ nhash = secalias_name_hash;
+ }
+ else
+ {
+ kind = "symbol";
+ ahash = alias_hash;
+ nhash = alias_name_hash;
+ }
+
+ /* Check if alias has been used before. */
+ h = (struct alias *) hash_find (ahash, alias);
+ if (h)
+ {
+ if (strcmp (h->name, name))
+ as_bad (_("`%s' is already the alias of %s `%s'"),
+ alias, kind, h->name);
+ goto out;
+ }
+
+ /* Check if name already has an alias. */
+ a = (const char *) hash_find (nhash, name);
+ if (a)
+ {
+ if (strcmp (a, alias))
+ as_bad (_("%s `%s' already has an alias `%s'"), kind, name, a);
+ goto out;
+ }
+
+ h = (struct alias *) xmalloc (sizeof (struct alias));
+ as_where (&h->file, &h->line);
+ h->name = name;
+
+ error_string = hash_jam (ahash, alias, (PTR) h);
+ if (error_string)
+ {
+ as_fatal (_("inserting \"%s\" into %s alias hash table failed: %s"),
+ alias, kind, error_string);
+ goto out;
+ }
+
+ error_string = hash_jam (nhash, name, (PTR) alias);
+ if (error_string)
+ {
+ as_fatal (_("inserting \"%s\" into %s name hash table failed: %s"),
+ alias, kind, error_string);
+out:
+ obstack_free (¬es, name);
+ obstack_free (¬es, alias);
+ }
+
+ demand_empty_rest_of_line ();
+}
+
+/* It renames the original symbol name to its alias. */
+static void
+do_alias (const char *alias, PTR value)
+{
+ struct alias *h = (struct alias *) value;
+ symbolS *sym = symbol_find (h->name);
+
+ if (sym == NULL)
+ as_warn_where (h->file, h->line,
+ _("symbol `%s' aliased to `%s' is not used"),
+ h->name, alias);
+ else
+ S_SET_NAME (sym, (char *) alias);
+}
+
+/* Called from write_object_file. */
+void
+ia64_adjust_symtab (void)
+{
+ hash_traverse (alias_hash, do_alias);
+}
+
+/* It renames the original section name to its alias. */
+static void
+do_secalias (const char *alias, PTR value)
+{
+ struct alias *h = (struct alias *) value;
+ segT sec = bfd_get_section_by_name (stdoutput, h->name);
+
+ if (sec == NULL)
+ as_warn_where (h->file, h->line,
+ _("section `%s' aliased to `%s' is not used"),
+ h->name, alias);
+ else
+ sec->name = alias;
+}
+
+/* Called from write_object_file. */
+void
+ia64_frob_file (void)
+{
+ hash_traverse (secalias_hash, do_secalias);
+}