X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-ia64.c;h=24e33c0662648347dae5c46e5274cfb78ee2a4c9;hb=8ad30312fff34325113307cb27bcae49d220df15;hp=659ad411d4699e6ae6761841eb29d93a8ac0814e;hpb=5a080f897a2ec36ef530f27209ad35e2673e1274;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/config/tc-ia64.c b/gas/config/tc-ia64.c index 659ad411d4..24e33c0662 100644 --- a/gas/config/tc-ia64.c +++ b/gas/config/tc-ia64.c @@ -1,5 +1,5 @@ /* tc-ia64.c -- Assembler for the HP/Intel IA-64 architecture. - Copyright (C) 1998, 1999, 2000 Free Software Foundation. + Copyright 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. Contributed by David Mosberger-Tang This file is part of GAS, the GNU Assembler. @@ -24,7 +24,6 @@ - optional operands - directives: - .alias .eb .estate .lb @@ -43,6 +42,7 @@ */ #include "as.h" +#include "safe-ctype.h" #include "dwarf2dbg.h" #include "subsegs.h" @@ -61,26 +61,38 @@ enum special_section { + /* IA-64 ABI section pseudo-ops. */ SPECIAL_SECTION_BSS = 0, SPECIAL_SECTION_SBSS, SPECIAL_SECTION_SDATA, SPECIAL_SECTION_RODATA, SPECIAL_SECTION_COMMENT, SPECIAL_SECTION_UNWIND, - SPECIAL_SECTION_UNWIND_INFO + SPECIAL_SECTION_UNWIND_INFO, + /* HPUX specific section pseudo-ops. */ + SPECIAL_SECTION_INIT_ARRAY, + SPECIAL_SECTION_FINI_ARRAY, }; enum reloc_func { + FUNC_DTP_MODULE, + FUNC_DTP_RELATIVE, FUNC_FPTR_RELATIVE, FUNC_GP_RELATIVE, FUNC_LT_RELATIVE, + FUNC_LT_RELATIVE_X, FUNC_PC_RELATIVE, FUNC_PLT_RELATIVE, FUNC_SEC_RELATIVE, FUNC_SEG_RELATIVE, + FUNC_TP_RELATIVE, FUNC_LTV_RELATIVE, FUNC_LT_FPTR_RELATIVE, + FUNC_LT_DTP_MODULE, + FUNC_LT_DTP_RELATIVE, + FUNC_LT_TP_RELATIVE, + FUNC_IPLT_RELOC, }; enum reg_symbol @@ -124,6 +136,13 @@ enum dynreg_type DYNREG_NUM_TYPES }; +enum operand_match_result + { + OPERAND_MATCH, + OPERAND_OUT_OF_RANGE, + OPERAND_MISMATCH + }; + /* On the ia64, we can't know the address of a text label until the instructions are packed into a bundle. To handle this, we keep track of the list of labels that appear in front of each @@ -136,6 +155,20 @@ struct label_fix 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[] = ""; @@ -190,7 +223,8 @@ static struct explicit_mode : 1, /* which mode we're in */ default_explicit_mode : 1, /* which mode is the default */ mode_explicitly_set : 1, /* was the current mode explicitly set? */ - auto_align : 1; + auto_align : 1, + keep_pending_output : 1; /* Each bundle consists of up to three instructions. We keep track of four most recent instructions so we can correctly set @@ -271,6 +305,9 @@ static struct int g_reg_set_conditionally[128]; } last_groups[3]; int group_idx; + + int pointer_size; /* size in bytes of a pointer */ + int pointer_size_shift; /* shift size of a pointer for alignment */ } md; @@ -459,15 +496,23 @@ static struct pseudo_func[] = { /* reloc pseudo functions (these must come first!): */ - { "fptr", PSEUDO_FUNC_RELOC }, - { "gprel", PSEUDO_FUNC_RELOC }, - { "ltoff", PSEUDO_FUNC_RELOC }, - { "pcrel", PSEUDO_FUNC_RELOC }, - { "pltoff", PSEUDO_FUNC_RELOC }, - { "secrel", PSEUDO_FUNC_RELOC }, - { "segrel", PSEUDO_FUNC_RELOC }, - { "ltv", PSEUDO_FUNC_RELOC }, - { 0, }, /* placeholder for FUNC_LT_FPTR_RELATIVE */ + { "dtpmod", PSEUDO_FUNC_RELOC, { 0 } }, + { "dtprel", PSEUDO_FUNC_RELOC, { 0 } }, + { "fptr", PSEUDO_FUNC_RELOC, { 0 } }, + { "gprel", PSEUDO_FUNC_RELOC, { 0 } }, + { "ltoff", PSEUDO_FUNC_RELOC, { 0 } }, + { "ltoffx", PSEUDO_FUNC_RELOC, { 0 } }, + { "pcrel", PSEUDO_FUNC_RELOC, { 0 } }, + { "pltoff", PSEUDO_FUNC_RELOC, { 0 } }, + { "secrel", PSEUDO_FUNC_RELOC, { 0 } }, + { "segrel", PSEUDO_FUNC_RELOC, { 0 } }, + { "tprel", PSEUDO_FUNC_RELOC, { 0 } }, + { "ltv", PSEUDO_FUNC_RELOC, { 0 } }, + { "", 0, { 0 } }, /* placeholder for FUNC_LT_FPTR_RELATIVE */ + { "", 0, { 0 } }, /* placeholder for FUNC_LT_DTP_MODULE */ + { "", 0, { 0 } }, /* placeholder for FUNC_LT_DTP_RELATIVE */ + { "", 0, { 0 } }, /* placeholder for FUNC_LT_TP_RELATIVE */ + { "iplt", PSEUDO_FUNC_RELOC, { 0 } }, /* mbtype4 constants: */ { "alt", PSEUDO_FUNC_CONST, { 0xa } }, @@ -489,10 +534,17 @@ pseudo_func[] = { "natval", PSEUDO_FUNC_CONST, { 0x100 } }, /* old usage */ + /* hint constants: */ + { "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 } } @@ -515,7 +567,13 @@ static const bfd_vma nop[IA64_NUM_UNITS] = static char special_section_name[][20] = { {".bss"}, {".sbss"}, {".sdata"}, {".rodata"}, {".comment"}, - {".IA_64.unwind"}, {".IA_64.unwind_info"} + {".IA_64.unwind"}, {".IA_64.unwind_info"}, + {".init_array"}, {".fini_array"} + }; + +static char *special_linkonce_name[] = + { + ".gnu.linkonce.ia64unw.", ".gnu.linkonce.ia64unwi." }; /* The best template for a particular sequence of up to three @@ -539,7 +597,7 @@ static struct rsrc { int data_srlz; /* current data serialization state */ int qp_regno; /* qualifying predicate for this usage */ char *file; /* what file marked this dependency */ - int line; /* what line marked this dependency */ + unsigned int line; /* what line marked this dependency */ struct mem_offset mem_offset; /* optional memory offset hint */ enum { CMP_NONE, CMP_OR, CMP_AND } cmp_type; /* OR or AND compare? */ int path; /* corresponding code entry index */ @@ -576,7 +634,7 @@ static struct gr { unsigned known:1; int path; valueT value; -} gr_values[128] = {{ 1, 0 }}; +} gr_values[128] = {{ 1, 0, 0 }}; /* These are the routines required to output the various types of unwind records. */ @@ -597,7 +655,16 @@ typedef struct unw_rec_list { struct unw_rec_list *next; } unw_rec_list; -#define SLOT_NUM_NOT_SET -1 +#define SLOT_NUM_NOT_SET (unsigned)-1 + +/* Linked list of saved prologue counts. A very poor + implementation of a map from label numbers to prologue counts. */ +typedef struct label_prologue_count +{ + struct label_prologue_count *next; + unsigned long label_number; + unsigned int prologue_count; +} label_prologue_count; static struct { @@ -617,10 +684,16 @@ static struct symbolS *proc_end; symbolS *info; /* pointer to unwind info */ symbolS *personality_routine; + segT saved_text_seg; + subsegT saved_text_subseg; + unsigned int force_unwind_entry : 1; /* force generation of unwind entry? */ /* TRUE if processing unwind directives in a prologue region. */ int prologue; int prologue_mask; + unsigned int prologue_count; /* number of .prologues seen so far */ + /* Prologue counts at previous .label_state directives. */ + struct label_prologue_count * saved_prologue_counts; } unwind; typedef void (*vbyte_func) PARAMS ((int, char *, char *)); @@ -686,10 +759,12 @@ static void add_unwind_entry PARAMS((unw_rec_list *ptr)); static symbolS *declare_register PARAMS ((const char *name, int regnum)); static void declare_register_set PARAMS ((const char *, int, int)); static unsigned int operand_width PARAMS ((enum ia64_opnd)); -static int operand_match PARAMS ((const struct ia64_opcode *idesc, - int index, expressionS *e)); +static enum operand_match_result operand_match PARAMS ((const struct ia64_opcode *idesc, + int index, + expressionS *e)); static int parse_operand PARAMS ((expressionS *e)); static struct ia64_opcode * parse_operands PARAMS ((struct ia64_opcode *)); +static int errata_nop_necessary_p PARAMS ((struct slot *, enum ia64_unit)); static void build_insn PARAMS ((struct slot *, bfd_vma *)); static void emit_one_bundle PARAMS ((void)); static void fix_insn PARAMS ((fixS *, const struct ia64_operand *, valueT)); @@ -703,6 +778,7 @@ static void add_qp_imply PARAMS((int p1, int p2)); static void clear_qp_branch_flag PARAMS((valueT mask)); static void clear_qp_mutex PARAMS((valueT mask)); static void clear_qp_implies PARAMS((valueT p1_mask, valueT p2_mask)); +static int has_suffix_p PARAMS((const char *, const char *)); static void clear_register_values PARAMS ((void)); static void print_dependency PARAMS ((const char *action, int depind)); static void instruction_serialization PARAMS ((void)); @@ -825,11 +901,40 @@ static void set_imask PARAMS ((unw_rec_list *, unsigned long, unsigned long, uns static int count_bits PARAMS ((unsigned long)); static unsigned long slot_index PARAMS ((unsigned long, fragS *, 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 ((void)); +static int 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)); + +/* Build the unwind section name by appending the (possibly stripped) + text section NAME to the unwind PREFIX. The resulting string + pointer is assigned to RESULT. The string is allocated on the + stack, so this must be a macro... */ +#define make_unw_section_name(special, text_name, result) \ + { \ + const char *_prefix = special_section_name[special]; \ + const char *_suffix = text_name; \ + size_t _prefix_len, _suffix_len; \ + char *_result; \ + if (strncmp (text_name, ".gnu.linkonce.t.", \ + sizeof (".gnu.linkonce.t.") - 1) == 0) \ + { \ + _prefix = special_linkonce_name[special - SPECIAL_SECTION_UNWIND]; \ + _suffix += sizeof (".gnu.linkonce.t.") - 1; \ + } \ + _prefix_len = strlen (_prefix), _suffix_len = strlen (_suffix); \ + _result = alloca (_prefix_len + _suffix_len + 1); \ + memcpy (_result, _prefix, _prefix_len); \ + memcpy (_result + _prefix_len, _suffix, _suffix_len); \ + _result[_prefix_len + _suffix_len] = '\0'; \ + result = _result; \ + } \ +while (0) /* Determine if application register REGNUM resides in the integer unit (as opposed to the memory unit). */ @@ -862,18 +967,66 @@ set_section (name) input_line_pointer = saved_input_line_pointer; } +/* Map 's' to SHF_IA_64_SHORT. */ + +int +ia64_elf_section_letter (letter, ptr_msg) + int letter; + char **ptr_msg; +{ + if (letter == 's') + return SHF_IA_64_SHORT; + else if (letter == 'o') + return SHF_LINK_ORDER; + + *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. */ flagword ia64_elf_section_flags (flags, attr, type) flagword flags; - int attr, type; + int attr, type ATTRIBUTE_UNUSED; { if (attr & SHF_IA_64_SHORT) flags |= SEC_SMALL_DATA; return flags; } +int +ia64_elf_section_type (str, len) + const char *str; + size_t len; +{ +#define STREQ(s) ((len == sizeof (s) - 1) && (strncmp (str, s, sizeof (s) - 1) == 0)) + + if (STREQ (ELF_STRING_ia64_unwind_info)) + return SHT_PROGBITS; + + if (STREQ (ELF_STRING_ia64_unwind_info_once)) + return SHT_PROGBITS; + + if (STREQ (ELF_STRING_ia64_unwind)) + return SHT_IA_64_UNWIND; + + 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; + + if (STREQ ("fini_array")) + return SHT_FINI_ARRAY; + + return -1; +#undef STREQ +} + static unsigned int set_regstack (ins, locs, outs, rots) unsigned int ins, locs, outs, rots; @@ -909,6 +1062,7 @@ ia64_flush_insns () struct label_fix *lfix; segT saved_seg; subsegT saved_subseg; + unw_rec_list *ptr; if (!md.last_text_seg) return; @@ -936,6 +1090,22 @@ ia64_flush_insns () } CURR_SLOT.tag_fixups = 0; + /* In case there are unwind directives following the last instruction, + resolve those now. We only handle body and prologue directives here. + Give an error for others. */ + for (ptr = unwind.current_entry; ptr; ptr = ptr->next) + { + if (ptr->r.type == prologue || ptr->r.type == prologue_gr + || ptr->r.type == body) + { + ptr->slot_number = (unsigned long) frag_more (0); + ptr->slot_frag = frag_now; + } + else + as_bad (_("Unwind directive not followed by an instruction.")); + } + unwind.current_entry = NULL; + subseg_set (saved_seg, saved_subseg); if (md.qp.X_op == O_register) @@ -973,7 +1143,7 @@ void output_vbyte_mem (count, ptr, comment) int count; char *ptr; - char *comment; + char *comment ATTRIBUTE_UNUSED; { int x; if (vbyte_mem_ptr == NULL) @@ -990,8 +1160,8 @@ static int vbyte_count = 0; void count_output (count, ptr, comment) int count; - char *ptr; - char *comment; + char *ptr ATTRIBUTE_UNUSED; + char *comment ATTRIBUTE_UNUSED; { vbyte_count += count; } @@ -2478,6 +2648,25 @@ slot_index (slot_addr, slot_frag, first_addr, first_frag) return index; } +/* Optimize unwind record directives. */ + +static unw_rec_list * +optimize_unw_records (list) + unw_rec_list *list; +{ + if (!list) + return NULL; + + /* If the only unwind record is ".prologue" or ".prologue" followed + by ".body", then we can optimize the unwind directives away. */ + if (list->r.type == prologue + && (list->next == NULL + || (list->next->r.type == body && list->next->next == NULL))) + return NULL; + + return list; +} + /* 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 @@ -2559,7 +2748,11 @@ fixup_unw_records (list) size = (slot_index (last_addr, last_frag, first_addr, first_frag) + dir_len); rlen = ptr->r.record.r.rlen = size; - region = ptr; + if (ptr->r.type == body) + /* End of region. */ + region = 0; + else + region = ptr; break; } case epilogue: @@ -2653,6 +2846,47 @@ fixup_unw_records (list) } } +/* Helper routine for output_unw_records. Emits the header for the unwind + info. */ + +static int +setup_unwind_header (int size, unsigned char **mem) +{ + int x, extra = 0; + valueT flag_value; + + /* pad to pointer-size boundry. */ + x = size % md.pointer_size; + if (x != 0) + extra = md.pointer_size - x; + + /* Add 8 for the header + a pointer for the + personality offset. */ + *mem = xmalloc (size + extra + 8 + md.pointer_size); + + /* Clear the padding area and personality. */ + memset (*mem + 8 + size, 0, extra + md.pointer_size); + + /* Initialize the header area. */ + if (unwind.personality_routine) + { + if (md.flags & EF_IA_64_ABI64) + flag_value = (bfd_vma) 3 << 32; + else + /* 32-bit unwind info block. */ + flag_value = (bfd_vma) 0x1003 << 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. */ @@ -2661,34 +2895,28 @@ output_unw_records (list, ptr) unw_rec_list *list; void **ptr; { - int size, x, extra = 0; + int size, extra; unsigned char *mem; + *ptr = NULL; + + list = optimize_unw_records (list); fixup_unw_records (list); size = calc_record_size (list); - /* pad to 8 byte boundry. */ - x = size % 8; - if (x != 0) - extra = 8 - x; - /* Add 8 for the header + 8 more bytes for the personality offset. */ - mem = xmalloc (size + extra + 16); + if (size > 0 || unwind.force_unwind_entry) + { + unwind.force_unwind_entry = 0; + extra = setup_unwind_header (size, &mem); - vbyte_mem_ptr = mem + 8; - /* Clear the padding area and personality. */ - memset (mem + 8 + size, 0 , extra + 8); - /* Initialize the header area. */ - md_number_to_chars (mem, (((bfd_vma) 1 << 48) /* version */ - | (unwind.personality_routine - ? ((bfd_vma) 3 << 32) /* U & E handler flags */ - : 0) - | ((size + extra) / 8)), /* length (dwords) */ - 8); + vbyte_mem_ptr = mem + 8; + process_unw_records (list, output_vbyte_mem); - process_unw_records (list, output_vbyte_mem); + *ptr = mem; - *ptr = mem; - return size + extra + 16; + size += extra + 8 + md.pointer_size; + } + return size; } static int @@ -2703,18 +2931,18 @@ convert_expr_to_ab_reg (e, ab, regp) return 0; reg = e->X_add_number; - if (reg >= REG_GR + 4 && reg <= REG_GR + 7) + if (reg >= (REG_GR + 4) && reg <= (REG_GR + 7)) { *ab = 0; *regp = reg - REG_GR; } - else if ((reg >= REG_FR + 2 && reg <= REG_FR + 5) - || (reg >= REG_FR + 16 && reg <= REG_FR + 31)) + else if ((reg >= (REG_FR + 2) && reg <= (REG_FR + 5)) + || (reg >= (REG_FR + 16) && reg <= (REG_FR + 31))) { *ab = 1; *regp = reg - REG_FR; } - else if (reg >= REG_BR + 1 && reg <= REG_BR + 5) + else if (reg >= (REG_BR + 1) && reg <= (REG_BR + 5)) { *ab = 2; *regp = reg - REG_BR; @@ -2756,17 +2984,17 @@ convert_expr_to_xy_reg (e, xy, regp) reg = e->X_add_number; - if (reg >= REG_GR && reg <= REG_GR + 127) + if (/* reg >= REG_GR && */ reg <= (REG_GR + 127)) { *xy = 0; *regp = reg - REG_GR; } - else if (reg >= REG_FR && reg <= REG_FR + 127) + else if (reg >= REG_FR && reg <= (REG_FR + 127)) { *xy = 1; *regp = reg - REG_FR; } - else if (reg >= REG_BR && reg <= REG_BR + 7) + else if (reg >= REG_BR && reg <= (REG_BR + 7)) { *xy = 2; *regp = reg - REG_BR; @@ -2778,7 +3006,7 @@ convert_expr_to_xy_reg (e, xy, regp) static void dot_radix (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { int radix; @@ -2818,7 +3046,7 @@ add_unwind_entry (ptr) static void dot_fframe (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e; @@ -2832,7 +3060,7 @@ dot_fframe (dummy) static void dot_vframe (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e; unsigned reg; @@ -2851,7 +3079,7 @@ dot_vframe (dummy) static void dot_vframesp (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e; @@ -2862,12 +3090,12 @@ dot_vframesp (dummy) 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 dot_vframepsp (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e; @@ -2878,12 +3106,12 @@ dot_vframepsp (dummy) 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 dot_save (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e1, e2; int sep; @@ -2960,10 +3188,10 @@ dot_save (dummy) static void dot_restore (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e1, e2; - unsigned long ecount = 0; + unsigned long ecount; /* # of _additional_ regions to pop */ int sep; sep = parse_operand (&e1); @@ -2976,19 +3204,34 @@ dot_restore (dummy) if (sep == ',') { parse_operand (&e2); - if (e1.X_op != O_constant) + if (e2.X_op != O_constant || e2.X_add_number < 0) { - as_bad ("Second operand to .restore must be constant"); + as_bad ("Second operand to .restore must be a constant >= 0"); return; } - ecount = e1.X_op; + ecount = e2.X_add_number; + } + else + ecount = unwind.prologue_count - 1; + + if (ecount >= unwind.prologue_count) + { + as_bad ("Epilogue count of %lu exceeds number of nested prologues (%u)", + ecount + 1, unwind.prologue_count); + return; } + add_unwind_entry (output_epilogue (ecount)); + + if (ecount < unwind.prologue_count) + unwind.prologue_count -= ecount + 1; + else + unwind.prologue_count = 0; } static void dot_restorereg (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { unsigned int ab, reg; expressionS e; @@ -3005,7 +3248,7 @@ dot_restorereg (dummy) static void dot_restorereg_p (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { unsigned int qp, ab, reg; expressionS e1, e2; @@ -3036,26 +3279,38 @@ dot_restorereg_p (dummy) } static int -generate_unwind_image () +generate_unwind_image (text_name) + const char *text_name; { int size; - unsigned char *unw_rec; + void *unw_rec; /* 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 % 8 != 0) - as_bad ("Unwind record is not a multiple of 8 bytes."); + size = output_unw_records (unwind.list, &unw_rec); + if (size % md.pointer_size != 0) + as_bad ("Unwind record is not a multiple of %d bytes.", 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; - set_section ((char *) special_section_name[SPECIAL_SECTION_UNWIND_INFO]); + bfd_reloc_code_real_type reloc; + + make_unw_section_name (SPECIAL_SECTION_UNWIND_INFO, text_name, sec_name); + set_section (sec_name); + bfd_set_section_flags (stdoutput, now_seg, + SEC_LOAD | SEC_ALLOC | SEC_READONLY); + + /* Make sure the section has 4 byte alignment for ILP32 and + 8 byte alignment for LP64. */ + frag_align (md.pointer_size_shift, 0, 0); + record_alignment (now_seg, md.pointer_size_shift); /* Set expression which points to start of unwind descriptor area. */ unwind.info = expr_build_dot (); @@ -3068,20 +3323,37 @@ generate_unwind_image () /* Copy the information from the unwind record into this section. The data is already in the correct byte order. */ memcpy (where, unw_rec, size); + /* Add the personality address to the image. */ if (unwind.personality_routine != 0) { - exp.X_op = O_symbol; + exp.X_op = O_symbol; exp.X_add_symbol = unwind.personality_routine; exp.X_add_number = 0; - fix_new_exp (frag_now, frag_now_fix () - 8, 8, - &exp, 0, BFD_RELOC_IA64_LTOFF_FPTR64LSB); + + if (md.flags & EF_IA_64_BE) + { + if (md.flags & EF_IA_64_ABI64) + reloc = BFD_RELOC_IA64_LTOFF_FPTR64MSB; + else + reloc = BFD_RELOC_IA64_LTOFF_FPTR32MSB; + } + else + { + if (md.flags & EF_IA_64_ABI64) + reloc = BFD_RELOC_IA64_LTOFF_FPTR64LSB; + else + reloc = BFD_RELOC_IA64_LTOFF_FPTR32LSB; + } + + fix_new_exp (frag_now, frag_now_fix () - md.pointer_size, + md.pointer_size, &exp, 0, reloc); unwind.personality_routine = 0; } - obj_elf_previous (0); } free_list_records (unwind.list); + free_saved_prologue_counts (); unwind.list = unwind.tail = unwind.current_entry = NULL; return size; @@ -3089,22 +3361,39 @@ generate_unwind_image () static void dot_handlerdata (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { - generate_unwind_image (); + const char *text_name = segment_name (now_seg); + + /* If text section name starts with ".text" (which it should), + strip this prefix off. */ + if (strcmp (text_name, ".text") == 0) + text_name = ""; + + unwind.force_unwind_entry = 1; + + /* Remember which segment we're in so we can switch back after .endp */ + unwind.saved_text_seg = now_seg; + unwind.saved_text_subseg = now_subseg; + + /* Generate unwind info into unwind-info section and then leave that + section as the currently active one so dataXX directives go into + the language specific data area of the unwind info block. */ + generate_unwind_image (text_name); demand_empty_rest_of_line (); } static void dot_unwentry (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { + unwind.force_unwind_entry = 1; demand_empty_rest_of_line (); } static void dot_altrp (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e; unsigned reg; @@ -3213,7 +3502,7 @@ dot_savemem (psprel) static void dot_saveg (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e1, e2; int sep; @@ -3241,7 +3530,7 @@ dot_saveg (dummy) static void dot_savef (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e1; int sep; @@ -3255,7 +3544,7 @@ dot_savef (dummy) static void dot_saveb (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e1, e2; unsigned int reg; @@ -3290,7 +3579,7 @@ dot_saveb (dummy) static void dot_savegf (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e1, e2; int sep; @@ -3310,7 +3599,7 @@ dot_savegf (dummy) static void dot_spill (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e; unsigned char sep; @@ -3327,7 +3616,7 @@ dot_spill (dummy) static void dot_spillreg (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { int sep, ab, xy, reg, treg; expressionS e1, e2; @@ -3394,7 +3683,7 @@ dot_spillmem (psprel) static void dot_spillreg_p (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { int sep, ab, xy, reg, treg; expressionS e1, e2, e3; @@ -3486,14 +3775,69 @@ dot_spillmem_p (psprel) } if (psprel) - add_unwind_entry (output_spill_psprel_p (qp, ab, reg, e3.X_add_number)); + add_unwind_entry (output_spill_psprel_p (ab, reg, e3.X_add_number, qp)); + else + add_unwind_entry (output_spill_sprel_p (ab, reg, e3.X_add_number, qp)); +} + +static unsigned int +get_saved_prologue_count (lbl) + unsigned long lbl; +{ + label_prologue_count *lpc = unwind.saved_prologue_counts; + + while (lpc != NULL && lpc->label_number != lbl) + lpc = lpc->next; + + if (lpc != NULL) + return lpc->prologue_count; + + as_bad ("Missing .label_state %ld", lbl); + return 1; +} + +static void +save_prologue_count (lbl, count) + unsigned long lbl; + unsigned int count; +{ + label_prologue_count *lpc = unwind.saved_prologue_counts; + + while (lpc != NULL && lpc->label_number != lbl) + lpc = lpc->next; + + if (lpc != NULL) + lpc->prologue_count = count; else - add_unwind_entry (output_spill_sprel_p (qp, ab, reg, e3.X_add_number)); + { + label_prologue_count *new_lpc = xmalloc (sizeof (* new_lpc)); + + new_lpc->next = unwind.saved_prologue_counts; + new_lpc->label_number = lbl; + new_lpc->prologue_count = count; + unwind.saved_prologue_counts = new_lpc; + } +} + +static void +free_saved_prologue_counts () +{ + label_prologue_count *lpc = unwind.saved_prologue_counts; + label_prologue_count *next; + + while (lpc != NULL) + { + next = lpc->next; + free (lpc); + lpc = next; + } + + unwind.saved_prologue_counts = NULL; } static void dot_label_state (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e; @@ -3504,11 +3848,12 @@ dot_label_state (dummy) return; } add_unwind_entry (output_label_state (e.X_add_number)); + save_prologue_count (e.X_add_number, unwind.prologue_count); } static void dot_copy_state (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e; @@ -3519,11 +3864,12 @@ dot_copy_state (dummy) return; } add_unwind_entry (output_copy_state (e.X_add_number)); + unwind.prologue_count = get_saved_prologue_count (e.X_add_number); } static void dot_unwabi (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e1, e2; unsigned char sep; @@ -3555,7 +3901,7 @@ dot_unwabi (dummy) static void dot_personality (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { char *name, *p, c; SKIP_WHITESPACE (); @@ -3563,6 +3909,7 @@ dot_personality (dummy) c = get_symbol_end (); p = input_line_pointer; unwind.personality_routine = symbol_find_or_make (name); + unwind.force_unwind_entry = 1; *p = c; SKIP_WHITESPACE (); demand_empty_rest_of_line (); @@ -3570,7 +3917,7 @@ dot_personality (dummy) static void dot_proc (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { char *name, *p, c; symbolS *sym; @@ -3599,13 +3946,14 @@ dot_proc (dummy) demand_empty_rest_of_line (); ia64_do_align (16); + unwind.prologue_count = 0; unwind.list = unwind.tail = unwind.current_entry = NULL; unwind.personality_routine = 0; } static void dot_body (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { unwind.prologue = 0; unwind.prologue_mask = 0; @@ -3616,10 +3964,10 @@ dot_body (dummy) static void dot_prologue (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { unsigned char sep; - int mask = 0, grsave; + int mask = 0, grsave = 0; if (!is_it_end_of_statement ()) { @@ -3653,11 +4001,12 @@ dot_prologue (dummy) unwind.prologue = 1; unwind.prologue_mask = mask; + ++unwind.prologue_count; } static void dot_endp (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS e; unsigned char *ptr; @@ -3665,54 +4014,164 @@ dot_endp (dummy) long where; segT saved_seg; subsegT saved_subseg; + const char *sec_name, *text_name; + char *name, *p, c; + symbolS *sym; - saved_seg = now_seg; - saved_subseg = now_subseg; - - expression (&e); - demand_empty_rest_of_line (); + if (unwind.saved_text_seg) + { + saved_seg = unwind.saved_text_seg; + saved_subseg = unwind.saved_text_subseg; + unwind.saved_text_seg = NULL; + } + else + { + saved_seg = now_seg; + saved_subseg = now_subseg; + } + + /* + Use a slightly ugly scheme to derive the unwind section names from + the text section name: + + text sect. unwind table sect. + name: name: comments: + ---------- ----------------- -------------------------------- + .text .IA_64.unwind + .text.foo .IA_64.unwind.text.foo + .foo .IA_64.unwind.foo + .gnu.linkonce.t.foo + .gnu.linkonce.ia64unw.foo + _info .IA_64.unwind_info gas issues error message (ditto) + _infoFOO .IA_64.unwind_infoFOO gas issues error message (ditto) + + This mapping is done so that: + + (a) An object file with unwind info only in .text will use + unwind section names .IA_64.unwind and .IA_64.unwind_info. + This follows the letter of the ABI and also ensures backwards + compatibility with older toolchains. + + (b) An object file with unwind info in multiple text sections + will use separate unwind sections for each text section. + This allows us to properly set the "sh_info" and "sh_link" + fields in SHT_IA_64_UNWIND as required by the ABI and also + lets GNU ld support programs with multiple segments + containing unwind info (as might be the case for certain + embedded applications). + + (c) An error is issued if there would be a name clash. + */ + text_name = segment_name (saved_seg); + if (strncmp (text_name, "_info", 5) == 0) + { + as_bad ("Illegal section name `%s' (causes unwind section name clash)", + text_name); + ignore_rest_of_line (); + return; + } + if (strcmp (text_name, ".text") == 0) + text_name = ""; insn_group_break (1, 0, 0); - /* If there was a .handlerdata, we haven't generated an image yet. */ - if (unwind.info == 0) + /* If there wasn't a .handlerdata, we haven't generated an image yet. */ + if (!unwind.info) + generate_unwind_image (text_name); + + if (unwind.info || unwind.force_unwind_entry) { - generate_unwind_image (); - } + subseg_set (md.last_text_seg, 0); + unwind.proc_end = expr_build_dot (); - subseg_set (md.last_text_seg, 0); - unwind.proc_end = expr_build_dot (); + make_unw_section_name (SPECIAL_SECTION_UNWIND, text_name, sec_name); + set_section ((char *) sec_name); + bfd_set_section_flags (stdoutput, now_seg, + SEC_LOAD | SEC_ALLOC | SEC_READONLY); - set_section ((char *) special_section_name[SPECIAL_SECTION_UNWIND]); - ptr = frag_more (24); - where = frag_now_fix () - 24; - bytes_per_address = bfd_arch_bits_per_address (stdoutput) / 8; + /* Make sure that section has 4 byte alignment for ILP32 and + 8 byte alignment for LP64. */ + record_alignment (now_seg, md.pointer_size_shift); - /* Issue the values of a) Proc Begin, b) Proc End, c) Unwind Record. */ - e.X_op = O_pseudo_fixup; - e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym; - e.X_add_number = 0; - e.X_add_symbol = unwind.proc_start; - ia64_cons_fix_new (frag_now, where, bytes_per_address, &e); + /* Need space for 3 pointers for procedure start, procedure end, + and unwind info. */ + ptr = frag_more (3 * md.pointer_size); + where = frag_now_fix () - (3 * md.pointer_size); + bytes_per_address = bfd_arch_bits_per_address (stdoutput) / 8; - e.X_op = O_pseudo_fixup; - e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym; - e.X_add_number = 0; - e.X_add_symbol = unwind.proc_end; - ia64_cons_fix_new (frag_now, where + bytes_per_address, bytes_per_address, &e); + /* Issue the values of a) Proc Begin, b) Proc End, c) Unwind Record. */ + e.X_op = O_pseudo_fixup; + e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym; + e.X_add_number = 0; + e.X_add_symbol = unwind.proc_start; + ia64_cons_fix_new (frag_now, where, bytes_per_address, &e); - if (unwind.info != 0) - { e.X_op = O_pseudo_fixup; e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym; e.X_add_number = 0; - e.X_add_symbol = unwind.info; - ia64_cons_fix_new (frag_now, where + (bytes_per_address * 2), bytes_per_address, &e); - } - else - md_number_to_chars (ptr + (bytes_per_address * 2), 0, bytes_per_address); + e.X_add_symbol = unwind.proc_end; + ia64_cons_fix_new (frag_now, where + bytes_per_address, + bytes_per_address, &e); + + if (unwind.info) + { + e.X_op = O_pseudo_fixup; + e.X_op_symbol = pseudo_func[FUNC_SEG_RELATIVE].u.sym; + e.X_add_number = 0; + e.X_add_symbol = unwind.info; + ia64_cons_fix_new (frag_now, where + (bytes_per_address * 2), + bytes_per_address, &e); + } + else + md_number_to_chars (ptr + (bytes_per_address * 2), 0, + bytes_per_address); + } subseg_set (saved_seg, saved_subseg); + + /* Parse names of main and alternate entry points and set symbol sizes. */ + while (1) + { + SKIP_WHITESPACE (); + name = input_line_pointer; + c = get_symbol_end (); + p = input_line_pointer; + sym = symbol_find (name); + if (sym && unwind.proc_start + && (symbol_get_bfdsym (sym)->flags & BSF_FUNCTION) + && S_GET_SIZE (sym) == 0 && symbol_get_obj (sym)->size == NULL) + { + fragS *fr = symbol_get_frag (unwind.proc_start); + fragS *frag = symbol_get_frag (sym); + + /* Check whether the function label is at or beyond last + .proc directive. */ + while (fr && fr != frag) + fr = fr->fr_next; + if (fr) + { + if (frag == frag_now && SEG_NORMAL (now_seg)) + S_SET_SIZE (sym, frag_now_fix () - S_GET_VALUE (sym)); + else + { + symbol_get_obj (sym)->size = + (expressionS *) xmalloc (sizeof (expressionS)); + symbol_get_obj (sym)->size->X_op = O_subtract; + symbol_get_obj (sym)->size->X_add_symbol + = symbol_new (FAKE_LABEL_NAME, now_seg, + frag_now_fix (), frag_now); + symbol_get_obj (sym)->size->X_op_symbol = sym; + symbol_get_obj (sym)->size->X_add_number = 0; + } + } + } + *p = c; + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + break; + ++input_line_pointer; + } + demand_empty_rest_of_line (); unwind.proc_start = unwind.proc_end = unwind.info = 0; } @@ -3725,7 +4184,7 @@ dot_template (template) static void dot_regstk (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { int ins, locs, outs, rots; @@ -3871,12 +4330,37 @@ 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 dot_psr (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { char *option; int ch; @@ -3907,16 +4391,9 @@ dot_psr (dummy) demand_empty_rest_of_line (); } -static void -dot_alias (dummy) - int dummy; -{ - as_bad (".alias not implemented yet"); -} - static void dot_ln (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { new_logical_line (0, get_absolute_expression ()); demand_empty_rest_of_line (); @@ -3960,9 +4437,11 @@ dot_xdata (size) if (!name) return; + md.keep_pending_output = 1; set_section (name); cons (size); obj_elf_previous (0); + md.keep_pending_output = 0; } /* Why doesn't float_cons() call md_cons_align() the way cons() does? */ @@ -3971,19 +4450,25 @@ static void 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); } @@ -4006,9 +4491,11 @@ dot_xfloat_cons (kind) if (!name) return; + md.keep_pending_output = 1; set_section (name); stmt_float_cons (kind); obj_elf_previous (0); + md.keep_pending_output = 0; } static void @@ -4019,9 +4506,11 @@ dot_xstringer (zero) if (!name) return; + md.keep_pending_output = 1; set_section (name); stringer (zero); obj_elf_previous (0); + md.keep_pending_output = 0; } static void @@ -4033,11 +4522,13 @@ dot_xdata_ua (size) if (!name) return; + md.keep_pending_output = 1; set_section (name); md.auto_align = 0; cons (size); md.auto_align = saved_auto_align; obj_elf_previous (0); + md.keep_pending_output = 0; } static void @@ -4049,18 +4540,20 @@ dot_xfloat_cons_ua (kind) if (!name) return; + md.keep_pending_output = 1; set_section (name); md.auto_align = 0; stmt_float_cons (kind); md.auto_align = saved_auto_align; obj_elf_previous (0); + md.keep_pending_output = 0; } /* .reg.val ,value */ static void dot_reg_val (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { expressionS reg; @@ -4203,7 +4696,7 @@ dot_pred_rel (type) valueT bit = 1; int regno; - if (toupper (*input_line_pointer) != 'P' + if (TOUPPER (*input_line_pointer) != 'P' || (regno = atoi (++input_line_pointer)) < 0 || regno > 63) { @@ -4211,7 +4704,7 @@ dot_pred_rel (type) ignore_rest_of_line (); return; } - while (isdigit (*input_line_pointer)) + while (ISDIGIT (*input_line_pointer)) ++input_line_pointer; if (p1 == -1) p1 = regno; @@ -4228,7 +4721,7 @@ dot_pred_rel (type) valueT stop = 1; ++input_line_pointer; - if (toupper (*input_line_pointer) != 'P' + if (TOUPPER (*input_line_pointer) != 'P' || (regno = atoi (++input_line_pointer)) < 0 || regno > 63) { @@ -4236,7 +4729,7 @@ dot_pred_rel (type) ignore_rest_of_line (); return; } - while (isdigit (*input_line_pointer)) + while (ISDIGIT (*input_line_pointer)) ++input_line_pointer; stop <<= regno; if (bit >= stop) @@ -4313,7 +4806,7 @@ dot_pred_rel (type) static void dot_entry (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { const char *err; char *name; @@ -4352,7 +4845,7 @@ dot_entry (dummy) static void dot_mem_offset (dummy) - int dummy; + int dummy ATTRIBUTE_UNUSED; { md.mem_offset.hint = 1; md.mem_offset.offset = get_absolute_expression (); @@ -4379,43 +4872,43 @@ const pseudo_typeS md_pseudo_table[] = { "comment", dot_special_section, SPECIAL_SECTION_COMMENT }, { "ia_64.unwind", dot_special_section, SPECIAL_SECTION_UNWIND }, { "ia_64.unwind_info", dot_special_section, SPECIAL_SECTION_UNWIND_INFO }, + { "init_array", dot_special_section, SPECIAL_SECTION_INIT_ARRAY }, + { "fini_array", dot_special_section, SPECIAL_SECTION_FINI_ARRAY }, { "proc", dot_proc, 0 }, { "body", dot_body, 0 }, { "prologue", dot_prologue, 0 }, - { "endp", dot_endp }, - { "file", dwarf2_directive_file }, - { "loc", dwarf2_directive_loc }, - - { "fframe", dot_fframe }, - { "vframe", dot_vframe }, - { "vframesp", dot_vframesp }, - { "vframepsp", dot_vframepsp }, - { "save", dot_save }, - { "restore", dot_restore }, - { "restorereg", dot_restorereg }, - { "restorereg.p", dot_restorereg_p }, - { "handlerdata", dot_handlerdata }, - { "unwentry", dot_unwentry }, - { "altrp", dot_altrp }, + { "endp", dot_endp, 0 }, + + { "fframe", dot_fframe, 0 }, + { "vframe", dot_vframe, 0 }, + { "vframesp", dot_vframesp, 0 }, + { "vframepsp", dot_vframepsp, 0 }, + { "save", dot_save, 0 }, + { "restore", dot_restore, 0 }, + { "restorereg", dot_restorereg, 0 }, + { "restorereg.p", dot_restorereg_p, 0 }, + { "handlerdata", dot_handlerdata, 0 }, + { "unwentry", dot_unwentry, 0 }, + { "altrp", dot_altrp, 0 }, { "savesp", dot_savemem, 0 }, { "savepsp", dot_savemem, 1 }, - { "save.g", dot_saveg }, - { "save.f", dot_savef }, - { "save.b", dot_saveb }, - { "save.gf", dot_savegf }, - { "spill", dot_spill }, - { "spillreg", dot_spillreg }, + { "save.g", dot_saveg, 0 }, + { "save.f", dot_savef, 0 }, + { "save.b", dot_saveb, 0 }, + { "save.gf", dot_savegf, 0 }, + { "spill", dot_spill, 0 }, + { "spillreg", dot_spillreg, 0 }, { "spillsp", dot_spillmem, 0 }, { "spillpsp", dot_spillmem, 1 }, - { "spillreg.p", dot_spillreg_p }, + { "spillreg.p", dot_spillreg_p, 0 }, { "spillsp.p", dot_spillmem_p, 0 }, { "spillpsp.p", dot_spillmem_p, 1 }, - { "label_state", dot_label_state }, - { "copy_state", dot_copy_state }, - { "unwabi", dot_unwabi }, - { "personality", dot_personality }, + { "label_state", dot_label_state, 0 }, + { "copy_state", dot_copy_state, 0 }, + { "unwabi", dot_unwabi, 0 }, + { "personality", dot_personality, 0 }, #if 0 - { "estate", dot_estate }, + { "estate", dot_estate, 0 }, #endif { "mii", dot_template, 0x0 }, { "mli", dot_template, 0x2 }, /* old format, for compatibility */ @@ -4441,6 +4934,7 @@ const pseudo_typeS md_pseudo_table[] = { "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 }, @@ -4450,6 +4944,7 @@ const pseudo_typeS md_pseudo_table[] = { "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 }, @@ -4460,20 +4955,30 @@ const pseudo_typeS md_pseudo_table[] = { "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 }, - { "mem.offset", dot_mem_offset }, + { "mem.offset", dot_mem_offset, 0 }, { "pred.rel", dot_pred_rel, 0 }, { "pred.rel.clear", dot_pred_rel, 'c' }, { "pred.rel.imply", dot_pred_rel, 'i' }, { "pred.rel.mutex", dot_pred_rel, 'm' }, { "pred.safe_across_calls", dot_pred_rel, 's' }, - { "reg.val", dot_reg_val }, + { "reg.val", dot_reg_val, 0 }, { "auto", dot_dv_mode, 'a' }, { "explicit", dot_dv_mode, 'e' }, { "default", dot_dv_mode, 'd' }, + /* ??? These are needed to make gas/testsuite/gas/elf/ehopt.s work. + IA-64 aligns data allocation pseudo-ops by default, so we have to + tell it that these ones are supposed to be unaligned. Long term, + should rewrite so that only IA-64 specific data allocation pseudo-ops + are aligned by default. */ + {"2byte", stmt_cons_ua, 2}, + {"4byte", stmt_cons_ua, 4}, + {"8byte", stmt_cons_ua, 8}, + { NULL, 0, 0 } }; @@ -4490,9 +4995,11 @@ pseudo_opcode[] = { "data2", cons, 2 }, { "data4", cons, 4 }, { "data8", cons, 8 }, + { "data16", cons, 16 }, { "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 }, @@ -4500,9 +5007,11 @@ pseudo_opcode[] = { "data2.ua", stmt_cons_ua, 2 }, { "data4.ua", stmt_cons_ua, 4 }, { "data8.ua", stmt_cons_ua, 8 }, + { "data16.ua", stmt_cons_ua, 16 }, { "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 @@ -4557,7 +5066,7 @@ operand_width (opnd) return bits; } -static int +static enum operand_match_result operand_match (idesc, index, e) const struct ia64_opcode *idesc; int index; @@ -4574,62 +5083,82 @@ operand_match (idesc, index, e) case IA64_OPND_AR_CCV: if (e->X_op == O_register && e->X_add_number == REG_AR + 32) - return 1; + return OPERAND_MATCH; + break; + + case IA64_OPND_AR_CSD: + if (e->X_op == O_register && e->X_add_number == REG_AR + 25) + return OPERAND_MATCH; break; case IA64_OPND_AR_PFS: if (e->X_op == O_register && e->X_add_number == REG_AR + 64) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_GR0: if (e->X_op == O_register && e->X_add_number == REG_GR + 0) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_IP: if (e->X_op == O_register && e->X_add_number == REG_IP) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_PR: if (e->X_op == O_register && e->X_add_number == REG_PR) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_PR_ROT: if (e->X_op == O_register && e->X_add_number == REG_PR_ROT) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_PSR: if (e->X_op == O_register && e->X_add_number == REG_PSR) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_PSR_L: if (e->X_op == O_register && e->X_add_number == REG_PSR_L) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_PSR_UM: if (e->X_op == O_register && e->X_add_number == REG_PSR_UM) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_C1: - if (e->X_op == O_constant && e->X_add_number == 1) - return 1; + if (e->X_op == O_constant) + { + if (e->X_add_number == 1) + return OPERAND_MATCH; + else + return OPERAND_OUT_OF_RANGE; + } break; case IA64_OPND_C8: - if (e->X_op == O_constant && e->X_add_number == 8) - return 1; + if (e->X_op == O_constant) + { + if (e->X_add_number == 8) + return OPERAND_MATCH; + else + return OPERAND_OUT_OF_RANGE; + } break; case IA64_OPND_C16: - if (e->X_op == O_constant && e->X_add_number == 16) - return 1; + if (e->X_op == O_constant) + { + if (e->X_add_number == 16) + return OPERAND_MATCH; + else + return OPERAND_OUT_OF_RANGE; + } break; /* register operands: */ @@ -4637,20 +5166,20 @@ operand_match (idesc, index, e) case IA64_OPND_AR3: if (e->X_op == O_register && e->X_add_number >= REG_AR && e->X_add_number < REG_AR + 128) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_B1: case IA64_OPND_B2: if (e->X_op == O_register && e->X_add_number >= REG_BR && e->X_add_number < REG_BR + 8) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_CR3: if (e->X_op == O_register && e->X_add_number >= REG_CR && e->X_add_number < REG_CR + 128) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_F1: @@ -4659,14 +5188,14 @@ operand_match (idesc, index, e) case IA64_OPND_F4: if (e->X_op == O_register && e->X_add_number >= REG_FR && e->X_add_number < REG_FR + 128) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_P1: case IA64_OPND_P2: if (e->X_op == O_register && e->X_add_number >= REG_P && e->X_add_number < REG_P + 64) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_R1: @@ -4674,13 +5203,17 @@ operand_match (idesc, index, e) case IA64_OPND_R3: if (e->X_op == O_register && e->X_add_number >= REG_GR && e->X_add_number < REG_GR + 128) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_R3_2: - if (e->X_op == O_register && e->X_add_number >= REG_GR - && e->X_add_number < REG_GR + 4) - return 1; + if (e->X_op == O_register && e->X_add_number >= REG_GR) + { + if (e->X_add_number < REG_GR + 4) + return OPERAND_MATCH; + else if (e->X_add_number < REG_GR + 128) + return OPERAND_OUT_OF_RANGE; + } break; /* indirect operands: */ @@ -4697,12 +5230,12 @@ operand_match (idesc, index, e) if (e->X_op == O_index && e->X_op_symbol && (S_GET_VALUE (e->X_op_symbol) - IND_CPUID == opnd - IA64_OPND_CPUID_R3)) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_MR3: if (e->X_op == O_index && !e->X_op_symbol) - return 1; + return OPERAND_MATCH; break; /* immediate operands: */ @@ -4710,40 +5243,58 @@ operand_match (idesc, index, e) case IA64_OPND_LEN4: case IA64_OPND_LEN6: bits = operand_width (idesc->operands[index]); - if (e->X_op == O_constant - && (bfd_vma) (e->X_add_number - 1) < ((bfd_vma) 1 << bits)) - return 1; + if (e->X_op == O_constant) + { + if ((bfd_vma) (e->X_add_number - 1) < ((bfd_vma) 1 << bits)) + return OPERAND_MATCH; + else + return OPERAND_OUT_OF_RANGE; + } break; case IA64_OPND_CNT2b: - if (e->X_op == O_constant - && (bfd_vma) (e->X_add_number - 1) < 3) - return 1; + if (e->X_op == O_constant) + { + if ((bfd_vma) (e->X_add_number - 1) < 3) + return OPERAND_MATCH; + else + return OPERAND_OUT_OF_RANGE; + } break; case IA64_OPND_CNT2c: val = e->X_add_number; - if (e->X_op == O_constant - && (val == 0 || val == 7 || val == 15 || val == 16)) - return 1; + if (e->X_op == O_constant) + { + if ((val == 0 || val == 7 || val == 15 || val == 16)) + return OPERAND_MATCH; + else + return OPERAND_OUT_OF_RANGE; + } break; case IA64_OPND_SOR: /* SOR must be an integer multiple of 8 */ - if (e->X_add_number & 0x7) - break; + if (e->X_op == O_constant && e->X_add_number & 0x7) + return OPERAND_OUT_OF_RANGE; case IA64_OPND_SOF: case IA64_OPND_SOL: - if (e->X_op == O_constant && - (bfd_vma) e->X_add_number <= 96) - return 1; - break; - - case IA64_OPND_IMMU62: + if (e->X_op == O_constant) + { + if ((bfd_vma) e->X_add_number <= 96) + return OPERAND_MATCH; + else + return OPERAND_OUT_OF_RANGE; + } + break; + + case IA64_OPND_IMMU62: if (e->X_op == O_constant) { if ((bfd_vma) e->X_add_number < ((bfd_vma) 1 << 62)) - return 1; + return OPERAND_MATCH; + else + return OPERAND_OUT_OF_RANGE; } else { @@ -4769,10 +5320,10 @@ operand_match (idesc, index, e) fix->expr = *e; fix->is_pcrel = 0; ++CURR_SLOT.num_fixups; - return 1; + return OPERAND_MATCH; } else if (e->X_op == O_constant) - return 1; + return OPERAND_MATCH; break; case IA64_OPND_CCNT5: @@ -4790,59 +5341,78 @@ operand_match (idesc, index, e) case IA64_OPND_MHTYPE8: case IA64_OPND_POS6: bits = operand_width (idesc->operands[index]); - if (e->X_op == O_constant - && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << bits)) - return 1; + if (e->X_op == O_constant) + { + if ((bfd_vma) e->X_add_number < ((bfd_vma) 1 << bits)) + return OPERAND_MATCH; + else + return OPERAND_OUT_OF_RANGE; + } break; case IA64_OPND_IMMU9: bits = operand_width (idesc->operands[index]); - if (e->X_op == O_constant - && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << bits)) + if (e->X_op == O_constant) { - int lobits = e->X_add_number & 0x3; - if (((bfd_vma) e->X_add_number & 0x3C) != 0 && lobits == 0) - e->X_add_number |= (bfd_vma) 0x3; - return 1; + if ((bfd_vma) e->X_add_number < ((bfd_vma) 1 << bits)) + { + int lobits = e->X_add_number & 0x3; + if (((bfd_vma) e->X_add_number & 0x3C) != 0 && lobits == 0) + e->X_add_number |= (bfd_vma) 0x3; + return OPERAND_MATCH; + } + else + return OPERAND_OUT_OF_RANGE; } break; case IA64_OPND_IMM44: /* least 16 bits must be zero */ if ((e->X_add_number & 0xffff) != 0) + /* XXX technically, this is wrong: we should not be issuing warning + messages until we're sure this instruction pattern is going to + be used! */ as_warn (_("lower 16 bits of mask ignored")); - if (e->X_op == O_constant - && ((e->X_add_number >= 0 - && e->X_add_number < ((bfd_vma) 1 << 44)) - || (e->X_add_number < 0 - && -e->X_add_number <= ((bfd_vma) 1 << 44)))) + if (e->X_op == O_constant) { - /* sign-extend */ - if (e->X_add_number >= 0 - && (e->X_add_number & ((bfd_vma) 1 << 43)) != 0) + if (((e->X_add_number >= 0 + && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << 44)) + || (e->X_add_number < 0 + && (bfd_vma) -e->X_add_number <= ((bfd_vma) 1 << 44)))) { - e->X_add_number |= ~(((bfd_vma) 1 << 44) - 1); + /* sign-extend */ + if (e->X_add_number >= 0 + && (e->X_add_number & ((bfd_vma) 1 << 43)) != 0) + { + e->X_add_number |= ~(((bfd_vma) 1 << 44) - 1); + } + return OPERAND_MATCH; } - return 1; + else + return OPERAND_OUT_OF_RANGE; } break; case IA64_OPND_IMM17: /* bit 0 is a don't care (pr0 is hardwired to 1) */ - if (e->X_op == O_constant - && ((e->X_add_number >= 0 - && e->X_add_number < ((bfd_vma) 1 << 17)) - || (e->X_add_number < 0 - && -e->X_add_number <= ((bfd_vma) 1 << 17)))) - { - /* sign-extend */ - if (e->X_add_number >= 0 - && (e->X_add_number & ((bfd_vma) 1 << 16)) != 0) + if (e->X_op == O_constant) + { + if (((e->X_add_number >= 0 + && (bfd_vma) e->X_add_number < ((bfd_vma) 1 << 17)) + || (e->X_add_number < 0 + && (bfd_vma) -e->X_add_number <= ((bfd_vma) 1 << 17)))) { - e->X_add_number |= ~(((bfd_vma) 1 << 17) - 1); + /* sign-extend */ + if (e->X_add_number >= 0 + && (e->X_add_number & ((bfd_vma) 1 << 16)) != 0) + { + e->X_add_number |= ~(((bfd_vma) 1 << 17) - 1); + } + return OPERAND_MATCH; } - return 1; + else + return OPERAND_OUT_OF_RANGE; } break; @@ -4880,18 +5450,18 @@ operand_match (idesc, index, e) fix->expr = *e; fix->is_pcrel = 0; ++CURR_SLOT.num_fixups; - return 1; + return OPERAND_MATCH; } else if (e->X_op != O_constant && ! (e->X_op == O_big && opnd == IA64_OPND_IMM8M1U8)) - return 0; + return OPERAND_MISMATCH; if (opnd == IA64_OPND_IMM8M1U4) { /* Zero is not valid for unsigned compares that take an adjusted constant immediate range. */ if (e->X_add_number == 0) - return 0; + return OPERAND_OUT_OF_RANGE; /* Sign-extend 32-bit unsigned numbers, so that the following range checks will work. */ @@ -4903,7 +5473,7 @@ operand_match (idesc, index, e) /* Check for 0x100000000. This is valid because 0x100000000-1 is the same as ((uint32_t) -1). */ if (val == ((bfd_signed_vma) 1 << 32)) - return 1; + return OPERAND_MATCH; val = val - 1; } @@ -4912,7 +5482,7 @@ operand_match (idesc, index, e) /* Zero is not valid for unsigned compares that take an adjusted constant immediate range. */ if (e->X_add_number == 0) - return 0; + return OPERAND_OUT_OF_RANGE; /* Check for 0x10000000000000000. */ if (e->X_op == O_big) @@ -4922,9 +5492,9 @@ operand_match (idesc, index, e) && generic_bignum[2] == 0 && generic_bignum[3] == 0 && generic_bignum[4] == 1) - return 1; + return OPERAND_MATCH; else - return 0; + return OPERAND_OUT_OF_RANGE; } else val = e->X_add_number - 1; @@ -4943,19 +5513,24 @@ operand_match (idesc, index, e) else val = e->X_add_number; - if ((val >= 0 && val < ((bfd_vma) 1 << (bits - 1))) - || (val < 0 && -val <= ((bfd_vma) 1 << (bits - 1)))) - return 1; - break; + if ((val >= 0 && (bfd_vma) val < ((bfd_vma) 1 << (bits - 1))) + || (val < 0 && (bfd_vma) -val <= ((bfd_vma) 1 << (bits - 1)))) + return OPERAND_MATCH; + else + return OPERAND_OUT_OF_RANGE; case IA64_OPND_INC3: /* +/- 1, 4, 8, 16 */ val = e->X_add_number; if (val < 0) val = -val; - if (e->X_op == O_constant - && (val == 1 || val == 4 || val == 8 || val == 16)) - return 1; + if (e->X_op == O_constant) + { + if ((val == 1 || val == 4 || val == 8 || val == 16)) + return OPERAND_MATCH; + else + return OPERAND_OUT_OF_RANGE; + } break; case IA64_OPND_TGT25: @@ -4981,33 +5556,45 @@ operand_match (idesc, index, e) fix->expr = *e; fix->is_pcrel = 1; ++CURR_SLOT.num_fixups; - return 1; + return OPERAND_MATCH; } case IA64_OPND_TAG13: case IA64_OPND_TAG13b: switch (e->X_op) { case O_constant: - return 1; + return OPERAND_MATCH; case O_symbol: fix = CURR_SLOT.fixup + CURR_SLOT.num_fixups; - fix->code = ia64_gen_real_reloc_type (e->X_op_symbol, 0); + /* There are no external relocs for TAG13/TAG13b fields, so we + create a dummy reloc. This will not live past md_apply_fix3. */ + fix->code = BFD_RELOC_UNUSED; + fix->code = ia64_gen_real_reloc_type (e->X_op_symbol, fix->code); fix->opnd = idesc->operands[index]; fix->expr = *e; fix->is_pcrel = 1; ++CURR_SLOT.num_fixups; - return 1; + return OPERAND_MATCH; default: break; } break; + case IA64_OPND_LDXMOV: + fix = CURR_SLOT.fixup + CURR_SLOT.num_fixups; + fix->code = BFD_RELOC_IA64_LDXMOV; + fix->opnd = idesc->operands[index]; + fix->expr = *e; + fix->is_pcrel = 0; + ++CURR_SLOT.num_fixups; + return OPERAND_MATCH; + default: break; } - return 0; + return OPERAND_MISMATCH; } static int @@ -5055,8 +5642,9 @@ parse_operands (idesc) struct ia64_opcode *idesc; { int i = 0, highest_unmatched_operand, num_operands = 0, num_outputs = 0; - int sep = 0; + int error_pos, out_of_range_pos, curr_out_of_range_pos, sep = 0; enum ia64_opnd expected_operand = IA64_OPND_NIL; + enum operand_match_result result; char mnemonic[129]; char *first_arg = 0, *end, *saved_input_pointer; unsigned int sof; @@ -5138,6 +5726,8 @@ parse_operands (idesc) } highest_unmatched_operand = 0; + curr_out_of_range_pos = -1; + error_pos = 0; expected_operand = idesc->operands[0]; for (; idesc; idesc = get_next_opcode (idesc)) { @@ -5145,16 +5735,52 @@ parse_operands (idesc) continue; /* mismatch in # of outputs */ CURR_SLOT.num_fixups = 0; + + /* Try to match all operands. If we see an out-of-range operand, + then continue trying to match the rest of the operands, since if + the rest match, then this idesc will give the best error message. */ + + out_of_range_pos = -1; for (i = 0; i < num_operands && idesc->operands[i]; ++i) - if (!operand_match (idesc, i, CURR_SLOT.opnd + i)) - break; + { + result = operand_match (idesc, i, CURR_SLOT.opnd + i); + if (result != OPERAND_MATCH) + { + if (result != OPERAND_OUT_OF_RANGE) + break; + if (out_of_range_pos < 0) + /* remember position of the first out-of-range operand: */ + out_of_range_pos = i; + } + } + + /* If we did not match all operands, or if at least one operand was + out-of-range, then this idesc does not match. Keep track of which + idesc matched the most operands before failing. If we have two + idescs that failed at the same position, and one had an out-of-range + operand, then prefer the out-of-range operand. Thus if we have + "add r0=0x1000000,r1" we get an error saying the constant is out + of range instead of an error saying that the constant should have been + a register. */ - if (i != num_operands) + if (i != num_operands || out_of_range_pos >= 0) { - if (i > highest_unmatched_operand) + if (i > highest_unmatched_operand + || (i == highest_unmatched_operand + && out_of_range_pos > curr_out_of_range_pos)) { highest_unmatched_operand = i; - expected_operand = idesc->operands[i]; + if (out_of_range_pos >= 0) + { + expected_operand = idesc->operands[out_of_range_pos]; + error_pos = out_of_range_pos; + } + else + { + expected_operand = idesc->operands[i]; + error_pos = i; + } + curr_out_of_range_pos = out_of_range_pos; } continue; } @@ -5169,7 +5795,7 @@ parse_operands (idesc) { if (expected_operand) as_bad ("Operand %u of `%s' should be %s", - highest_unmatched_operand + 1, mnemonic, + error_pos + 1, mnemonic, elf64_ia64_operands[expected_operand].desc); else as_bad ("Operand mismatch"); @@ -5199,8 +5825,9 @@ errata_nop_necessary_p (slot, insn_unit) || idesc->operands[i] == IA64_OPND_P2) { int regno = slot->opnd[i].X_add_number - REG_P; + /* Ignore invalid operands; they generate errors elsewhere. */ if (regno >= 64) - abort (); + return 0; this_group->p_reg_set[regno] = 1; } } @@ -5215,8 +5842,9 @@ errata_nop_necessary_p (slot, insn_unit) || idesc->operands[i] == IA64_OPND_R3) { int regno = slot->opnd[i].X_add_number - REG_GR; + /* Ignore invalid operands; they generate errors elsewhere. */ if (regno >= 128) - abort (); + return 0; if (strncmp (idesc->name, "add", 3) != 0 && strncmp (idesc->name, "sub", 3) != 0 && strncmp (idesc->name, "shladd", 6) != 0 @@ -5246,8 +5874,9 @@ errata_nop_necessary_p (slot, insn_unit) || idesc->operands[i] == IA64_OPND_MR3) { int regno = slot->opnd[i].X_add_number - REG_GR; + /* Ignore invalid operands; they generate errors elsewhere. */ if (regno >= 128) - abort (); + return 0; if (idesc->operands[i] == IA64_OPND_R3) { if (strcmp (idesc->name, "fc") != 0 @@ -5258,9 +5887,9 @@ errata_nop_necessary_p (slot, insn_unit) && strncmp (idesc->name, "ptr", 3) != 0 && strncmp (idesc->name, "ptc", 3) != 0 && strncmp (idesc->name, "probe", 5) != 0) - return 0; + return 0; } - if (prev_group->g_reg_set_conditionally[regno]) + if (prev_group->g_reg_set_conditionally[regno]) return 1; } } @@ -5403,7 +6032,7 @@ emit_one_bundle () struct ia64_opcode *idesc; int end_of_insn_group = 0, user_template = -1; int n, i, j, first, curr; - unw_rec_list *ptr, *prev; + unw_rec_list *ptr; bfd_vma t0 = 0, t1 = 0; struct label_fix *lfix; struct insn_fix *ifix; @@ -5470,7 +6099,8 @@ emit_one_bundle () } if (idesc->flags & IA64_OPCODE_LAST) { - int required_slot, required_template; + int required_slot; + unsigned int required_template; /* If we need a stop bit after an M slot, our only choice is template 5 (M;;MI). If we need a stop bit after a B @@ -5585,10 +6215,11 @@ emit_one_bundle () } required_unit = ia64_templ_desc[template].exec_unit[i]; - /* resolve dynamic opcodes such as "break" and "nop": */ + /* resolve dynamic opcodes such as "break", "hint", and "nop": */ if (idesc->type == IA64_TYPE_DYN) { if ((strcmp (idesc->name, "nop") == 0) + || (strcmp (idesc->name, "hint") == 0) || (strcmp (idesc->name, "break") == 0)) insn_unit = required_unit; else if (strcmp (idesc->name, "chk.s") == 0) @@ -5649,12 +6280,12 @@ emit_one_bundle () continue; /* try next slot */ } - { - bfd_vma addr; + { + bfd_vma addr; - addr = frag_now->fr_address + frag_now_fix () - 16 + i; - dwarf2_gen_line_info (addr, &md.slot[curr].debug_line); - } + addr = frag_now->fr_address + frag_now_fix () - 16 + i; + dwarf2_gen_line_info (addr, &md.slot[curr].debug_line); + } if (errata_nop_necessary_p (md.slot + curr, insn_unit)) as_warn (_("Additional NOP may be necessary to workaround Itanium processor A/B step errata")); @@ -5751,6 +6382,7 @@ md_parse_option (c, arg) int c; char *arg; { + switch (c) { /* Switches from the Intel assembler. */ @@ -5806,12 +6438,13 @@ md_parse_option (c, arg) break; case 'a': - /* ??? Conflicts with gas' listing option. */ /* indirect= Assume unannotated indirect branches behavior according to -- exit: branch out from the current context (default) labels: all labels in context may be branch targets */ + if (strncmp (arg, "indirect=", 9) != 0) + return 0; break; case 'x': @@ -5868,6 +6501,11 @@ md_show_usage (stream) { fputs (_("\ IA-64 options:\n\ + --mconstant-gp mark output file as using the constant-GP model\n\ + (sets ELF header flag EF_IA_64_CONS_GP)\n\ + --mauto-pic mark output file as using the constant-GP model\n\ + without function descriptors (sets ELF header flag\n\ + EF_IA_64_NOFUNCDESC_CONS_GP)\n\ -milp32|-milp64|-mlp64|-mp64 select data model (default -mlp64)\n\ -mle | -mbe select little- or big-endian byte order (default -mle)\n\ -x | -xexplicit turn on dependency violation checking (default)\n\ @@ -5876,6 +6514,13 @@ IA-64 options:\n\ stream); } +void +ia64_after_parse_args () +{ + if (debug_type == DEBUG_STABS) + as_fatal (_("--gstabs is not supported for ia64")); +} + /* Return true if TYPE fits in TEMPL at SLOT. */ static int @@ -5929,7 +6574,23 @@ md_begin () bfd_set_section_alignment (stdoutput, text_section, 4); - target_big_endian = TARGET_BYTES_BIG_ENDIAN; + /* Make sure fucntion 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 (".", undefined_section, FUNC_DTP_MODULE, + &zero_address_frag); + + pseudo_func[FUNC_DTP_RELATIVE].u.sym = + symbol_new (".", undefined_section, FUNC_DTP_RELATIVE, + &zero_address_frag); + pseudo_func[FUNC_FPTR_RELATIVE].u.sym = symbol_new (".", undefined_section, FUNC_FPTR_RELATIVE, &zero_address_frag); @@ -5942,6 +6603,10 @@ md_begin () symbol_new (".", undefined_section, FUNC_LT_RELATIVE, &zero_address_frag); + pseudo_func[FUNC_LT_RELATIVE_X].u.sym = + symbol_new (".", undefined_section, FUNC_LT_RELATIVE_X, + &zero_address_frag); + pseudo_func[FUNC_PC_RELATIVE].u.sym = symbol_new (".", undefined_section, FUNC_PC_RELATIVE, &zero_address_frag); @@ -5958,6 +6623,10 @@ md_begin () symbol_new (".", undefined_section, FUNC_SEG_RELATIVE, &zero_address_frag); + pseudo_func[FUNC_TP_RELATIVE].u.sym = + symbol_new (".", undefined_section, FUNC_TP_RELATIVE, + &zero_address_frag); + pseudo_func[FUNC_LTV_RELATIVE].u.sym = symbol_new (".", undefined_section, FUNC_LTV_RELATIVE, &zero_address_frag); @@ -5966,6 +6635,22 @@ md_begin () symbol_new (".", undefined_section, FUNC_LT_FPTR_RELATIVE, &zero_address_frag); + pseudo_func[FUNC_LT_DTP_MODULE].u.sym = + symbol_new (".", undefined_section, FUNC_LT_DTP_MODULE, + &zero_address_frag); + + pseudo_func[FUNC_LT_DTP_RELATIVE].u.sym = + symbol_new (".", undefined_section, FUNC_LT_DTP_RELATIVE, + &zero_address_frag); + + pseudo_func[FUNC_LT_TP_RELATIVE].u.sym = + symbol_new (".", undefined_section, FUNC_LT_TP_RELATIVE, + &zero_address_frag); + + pseudo_func[FUNC_IPLT_RELOC].u.sym = + symbol_new (".", undefined_section, FUNC_IPLT_RELOC, + &zero_address_frag); + /* Compute the table of best templates. We compute goodness as a base 4 value, in which each match counts for 3, each F counts for 2, each B counts for 1. This should maximize the number of @@ -6138,6 +6823,19 @@ md_begin () if (! ok) as_warn (_("Could not set architecture and machine")); + /* Set the pointer size and pointer shift size depending on md.flags */ + + if (md.flags & EF_IA_64_ABI64) + { + md.pointer_size = 8; /* pointers are 8 bytes */ + md.pointer_size_shift = 3; /* alignment is 8 bytes = 2^2 */ + } + else + { + md.pointer_size = 4; /* pointers are 4 bytes */ + md.pointer_size_shift = 2; /* alignment is 4 bytes = 2^2 */ + } + md.mem_offset.hint = 0; md.path = 0; md.maxpaths = 0; @@ -6151,12 +6849,10 @@ md_begin () void ia64_init (argc, argv) - int argc; - char **argv; + int argc ATTRIBUTE_UNUSED; + char **argv ATTRIBUTE_UNUSED; { - md.flags = EF_IA_64_ABI64; - if (TARGET_BYTES_BIG_ENDIAN) - md.flags |= EF_IA_64_BE; + md.flags = MD_FLAGS_DEFAULT; } /* Return a string for the target object file format. */ @@ -6169,16 +6865,36 @@ ia64_target_format () if (md.flags & EF_IA_64_BE) { if (md.flags & EF_IA_64_ABI64) +#if defined(TE_AIX50) + return "elf64-ia64-aix-big"; +#elif defined(TE_HPUX) + return "elf64-ia64-hpux-big"; +#else return "elf64-ia64-big"; +#endif else +#if defined(TE_AIX50) + return "elf32-ia64-aix-big"; +#elif defined(TE_HPUX) + return "elf32-ia64-hpux-big"; +#else return "elf32-ia64-big"; +#endif } else { if (md.flags & EF_IA_64_ABI64) +#ifdef TE_AIX50 + return "elf64-ia64-aix-little"; +#else return "elf64-ia64-little"; +#endif else +#ifdef TE_AIX50 + return "elf32-ia64-aix-little"; +#else return "elf32-ia64-little"; +#endif } } else @@ -6297,14 +7013,36 @@ ia64_unrecognized_line (ch) char *s; char c; symbolS *tag; + int temp; if (md.qp.X_op == O_register) { as_bad ("Tag must come before qualifying predicate."); return 0; } - s = input_line_pointer; - c = get_symbol_end (); + + /* This implements just enough of read_a_source_file in read.c to + recognize labels. */ + if (is_name_beginner (*input_line_pointer)) + { + s = input_line_pointer; + c = get_symbol_end (); + } + else if (LOCAL_LABELS_FB + && ISDIGIT (*input_line_pointer)) + { + temp = 0; + while (ISDIGIT (*input_line_pointer)) + temp = (temp * 10) + *input_line_pointer++ - '0'; + fb_label_instance_inc (temp); + s = fb_label_name (temp, 0); + c = *input_line_pointer; + } + else + { + s = NULL; + c = '\0'; + } if (c != ':') { /* Put ':' back for error messages' sake. */ @@ -6312,6 +7050,7 @@ ia64_unrecognized_line (ch) as_bad ("Expected ':'"); return 0; } + defining_tag = 1; tag = colon (s); defining_tag = 0; @@ -6379,7 +7118,8 @@ ia64_frob_label (sym) void ia64_flush_pending_output () { - if (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) + if (!md.keep_pending_output + && bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) { /* ??? This causes many unnecessary stop bits to be emitted. Unfortunately, it isn't clear if it is safe to remove this. */ @@ -6465,7 +7205,7 @@ ia64_parse_name (name, e) switch (name[0]) { case 'i': - if (name[1] == 'n' && isdigit (name[2])) + if (name[1] == 'n' && ISDIGIT (name[2])) { dr = &md.in; name += 2; @@ -6473,7 +7213,7 @@ ia64_parse_name (name, e) break; case 'l': - if (name[1] == 'o' && name[2] == 'c' && isdigit (name[3])) + if (name[1] == 'o' && name[2] == 'c' && ISDIGIT (name[3])) { dr = &md.loc; name += 3; @@ -6481,7 +7221,7 @@ ia64_parse_name (name, e) break; case 'o': - if (name[1] == 'u' && name[2] == 't' && isdigit (name[3])) + if (name[1] == 'u' && name[2] == 't' && ISDIGIT (name[3])) { dr = &md.out; name += 3; @@ -6538,19 +7278,29 @@ ia64_canonicalize_symbol_name (name) return name; } +/* Return true if idesc is a conditional branch instruction. This excludes + the modulo scheduled branches, and br.ia. Mod-sched branches are excluded + because they always read/write resources regardless of the value of the + qualifying predicate. br.ia must always use p0, and hence is always + taken. Thus this function returns true for branches which can fall + through, and which use no resources if they do fall through. */ + static int is_conditional_branch (idesc) struct ia64_opcode *idesc; { - return (strncmp (idesc->name, "br", 2) == 0 - && (strcmp (idesc->name, "br") == 0 - || strncmp (idesc->name, "br.cond", 7) == 0 - || strncmp (idesc->name, "br.call", 7) == 0 - || strncmp (idesc->name, "br.ret", 6) == 0 - || strcmp (idesc->name, "brl") == 0 - || strncmp (idesc->name, "brl.cond", 7) == 0 - || strncmp (idesc->name, "brl.call", 7) == 0 - || strncmp (idesc->name, "brl.ret", 6) == 0)); + /* br is a conditional branch. Everything that starts with br. except + br.ia, br.c{loop,top,exit}, and br.w{top,exit} is a conditional branch. + Everything that starts with brl is a conditional branch. */ + return (idesc->name[0] == 'b' && idesc->name[1] == 'r' + && (idesc->name[2] == '\0' + || (idesc->name[2] == '.' && idesc->name[3] != 'i' + && idesc->name[3] != 'c' && idesc->name[3] != 'w') + || idesc->name[2] == 'l' + /* br.cond, br.call, br.clr */ + || (idesc->name[2] == '.' && idesc->name[3] == 'c' + && (idesc->name[4] == 'a' || idesc->name[4] == 'o' + || (idesc->name[4] == 'l' && idesc->name[5] == 'r'))))); } /* Return whether the given opcode is a taken branch. If there's any doubt, @@ -6826,7 +7576,7 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note) } else { - for (i = idesc->num_outputs;i < NELEMS (idesc->operands); i++) + for (i = idesc->num_outputs; i < NELEMS (idesc->operands); i++) if (idesc->operands[i] == IA64_OPND_B1 || idesc->operands[i] == IA64_OPND_B2) { @@ -6950,7 +7700,7 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note) for (i = 0; i < NELEMS (gr_values); i++) { /* Uses all registers *except* the one in R3. */ - if (i != (gr_values[regno].value & 0xFF)) + if ((unsigned)i != (gr_values[regno].value & 0xFF)) { specs[count] = tmpl; specs[count++].index = i; @@ -7284,8 +8034,8 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note) { int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P; int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P; - int or_andcm = strstr(idesc->name, "or.andcm") != NULL; - int and_orcm = strstr(idesc->name, "and.orcm") != NULL; + int or_andcm = strstr (idesc->name, "or.andcm") != NULL; + int and_orcm = strstr (idesc->name, "and.orcm") != NULL; if ((idesc->operands[0] == IA64_OPND_P1 || idesc->operands[0] == IA64_OPND_P2) @@ -7348,7 +8098,7 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note) && idesc->operands[0] == IA64_OPND_PR) { mask = CURR_SLOT.opnd[2].X_add_number; - if (mask & ((valueT) 1<<16)) + if (mask & ((valueT) 1 << 16)) for (i = 16; i < 63; i++) { specs[count] = tmpl; @@ -7406,8 +8156,8 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note) { int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P; int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P; - int or_andcm = strstr(idesc->name, "or.andcm") != NULL; - int and_orcm = strstr(idesc->name, "and.orcm") != NULL; + int or_andcm = strstr (idesc->name, "or.andcm") != NULL; + int and_orcm = strstr (idesc->name, "and.orcm") != NULL; if ((idesc->operands[0] == IA64_OPND_P1 || idesc->operands[0] == IA64_OPND_P2) @@ -7839,13 +8589,13 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note) specs[count++] = tmpl; } else if (note == 7) - { - valueT mask = 0; - if (idesc->operands[2] == IA64_OPND_IMM17) - mask = CURR_SLOT.opnd[2].X_add_number; - if (mask & ((valueT) 1 << 63)) + { + valueT mask = 0; + if (idesc->operands[2] == IA64_OPND_IMM17) + mask = CURR_SLOT.opnd[2].X_add_number; + if (mask & ((valueT) 1 << 63)) specs[count++] = tmpl; - } + } else if (note == 11) { if ((idesc->operands[0] == IA64_OPND_P1 @@ -7867,16 +8617,16 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note) { if (rsrc_write) { - int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P; - int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P; - int or_andcm = strstr(idesc->name, "or.andcm") != NULL; - int and_orcm = strstr(idesc->name, "and.orcm") != NULL; + int p1 = CURR_SLOT.opnd[0].X_add_number - REG_P; + int p2 = CURR_SLOT.opnd[1].X_add_number - REG_P; + int or_andcm = strstr (idesc->name, "or.andcm") != NULL; + int and_orcm = strstr (idesc->name, "and.orcm") != NULL; if (p1 == 63 && (idesc->operands[0] == IA64_OPND_P1 || idesc->operands[0] == IA64_OPND_P2)) { - specs[count] = tmpl; + specs[count] = tmpl; specs[count++].cmp_type = (or_andcm ? CMP_OR : (and_orcm ? CMP_AND : CMP_NONE)); } @@ -7884,7 +8634,7 @@ dep->name, idesc->name, (rsrc_write?"write":"read"), note) && (idesc->operands[1] == IA64_OPND_P1 || idesc->operands[1] == IA64_OPND_P2)) { - specs[count] = tmpl; + specs[count] = tmpl; specs[count++].cmp_type = (or_andcm ? CMP_AND : (and_orcm ? CMP_OR : CMP_NONE)); } @@ -8121,6 +8871,19 @@ add_qp_mutex (mask) qp_mutexes[qp_mutexeslen++].prmask = mask; } +static int +has_suffix_p (name, suffix) + const char *name; + const char *suffix; +{ + size_t namelen = strlen (name); + size_t sufflen = strlen (suffix); + + if (namelen <= sufflen) + return 0; + return strcmp (name + namelen - sufflen, suffix) == 0; +} + static void clear_register_values () { @@ -8177,7 +8940,7 @@ note_register_values (idesc) 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; @@ -8238,21 +9001,19 @@ note_register_values (idesc) } /* In general, clear mutexes and implies which include P1 or P2, with the following exceptions. */ - else if (strstr (idesc->name, ".or.andcm") != NULL) + 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 (strstr (idesc->name, ".and.orcm") != NULL) - { - add_qp_mutex (p1mask | p2mask); - clear_qp_implies (p1mask, p2mask); - } - else if (strstr (idesc->name, ".and") != NULL) + else if (has_suffix_p (idesc->name, ".andcm") + || has_suffix_p (idesc->name, ".and")) { clear_qp_implies (0, p1mask | p2mask); } - else if (strstr (idesc->name, ".or") != NULL) + else if (has_suffix_p (idesc->name, ".orcm") + || has_suffix_p (idesc->name, ".or")) { clear_qp_mutex (p1mask | p2mask); clear_qp_implies (p1mask | p2mask, 0); @@ -8260,7 +9021,7 @@ note_register_values (idesc) else { clear_qp_implies (p1mask | p2mask, p1mask | p2mask); - if (strstr (idesc->name, ".unc") != NULL) + if (has_suffix_p (idesc->name, ".unc")) { add_qp_mutex (p1mask | p2mask); if (CURR_SLOT.qp_regno != 0) @@ -8295,8 +9056,11 @@ note_register_values (idesc) gr_values[regno].value = CURR_SLOT.opnd[1].X_add_number; gr_values[regno].path = md.path; if (md.debug_dv) - fprintf (stderr, " Know gr%d = 0x%llx\n", - regno, gr_values[regno].value); + { + fprintf (stderr, " Know gr%d = ", regno); + fprintf_vma (stderr, gr_values[regno].value); + fputs ("\n", stderr); + } } } else @@ -8488,8 +9252,8 @@ insn_group_break (insert_stop, qp_regno, save_current) static void mark_resource (idesc, dep, spec, depind, path) - struct ia64_opcode *idesc; - const struct ia64_dependency *dep; + struct ia64_opcode *idesc ATTRIBUTE_UNUSED; + const struct ia64_dependency *dep ATTRIBUTE_UNUSED; struct rsrc *spec; int depind; int path; @@ -8499,7 +9263,7 @@ mark_resource (idesc, dep, spec, depind, path) regdepstotlen += 20; regdeps = (struct rsrc *) xrealloc ((void *) regdeps, - regdepstotlen * sizeof(struct rsrc)); + regdepstotlen * sizeof (struct rsrc)); } regdeps[regdepslen] = *spec; @@ -8526,9 +9290,12 @@ print_dependency (action, depind) if (regdeps[depind].specific && regdeps[depind].index != 0) fprintf (stderr, " (%d)", regdeps[depind].index); if (regdeps[depind].mem_offset.hint) - fprintf (stderr, " 0x%llx+0x%llx", - regdeps[depind].mem_offset.base, - regdeps[depind].mem_offset.offset); + { + fputs (" ", stderr); + fprintf_vma (stderr, regdeps[depind].mem_offset.base); + fputs ("+", stderr); + fprintf_vma (stderr, regdeps[depind].mem_offset.offset); + } fprintf (stderr, "\n"); } } @@ -9115,7 +9882,7 @@ md_assemble (str) symbolS * md_undefined_symbol (name) - char *name; + char *name ATTRIBUTE_UNUSED; { return 0; } @@ -9176,14 +9943,25 @@ md_operand (e) as_bad ("Not a symbolic expression"); goto err; } - if (S_GET_VALUE (e->X_op_symbol) == FUNC_FPTR_RELATIVE - && i == FUNC_LT_RELATIVE) - i = FUNC_LT_FPTR_RELATIVE; - else + if (i != FUNC_LT_RELATIVE) { as_bad ("Illegal combination of relocation functions"); goto err; } + switch (S_GET_VALUE (e->X_op_symbol)) + { + case FUNC_FPTR_RELATIVE: + i = FUNC_LT_FPTR_RELATIVE; break; + case FUNC_DTP_MODULE: + i = FUNC_LT_DTP_MODULE; break; + case FUNC_DTP_RELATIVE: + i = FUNC_LT_DTP_RELATIVE; break; + case FUNC_TP_RELATIVE: + i = FUNC_LT_TP_RELATIVE; break; + default: + as_bad ("Illegal combination of relocation functions"); + goto err; + } } /* Make sure gas doesn't get rid of local symbols that are used in relocs. */ @@ -9285,12 +10063,16 @@ ia64_force_relocation (fix) case BFD_RELOC_IA64_PLTOFF64I: case BFD_RELOC_IA64_PLTOFF64MSB: case BFD_RELOC_IA64_PLTOFF64LSB: + + case BFD_RELOC_IA64_LTOFF22X: + case BFD_RELOC_IA64_LDXMOV: return 1; default: - return 0; + break; } - return 0; + + return generic_force_reloc (fix); } /* Decide from what point a pc-relative relocation is relative to, @@ -9308,6 +10090,20 @@ ia64_pcrel_from_section (fix, sec) 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. */ @@ -9336,27 +10132,59 @@ ia64_cons_fix_new (f, where, nbytes, exp) break; case 8: - if (target_big_endian) - code = BFD_RELOC_IA64_DIR64MSB; + /* In 32-bit mode, data8 could mean function descriptors too. */ + if (exp->X_op == O_pseudo_fixup + && exp->X_op_symbol + && S_GET_VALUE (exp->X_op_symbol) == FUNC_IPLT_RELOC + && !(md.flags & EF_IA_64_ABI64)) + { + if (target_big_endian) + code = BFD_RELOC_IA64_IPLTMSB; + else + code = BFD_RELOC_IA64_IPLTLSB; + exp->X_op = O_symbol; + break; + } else - code = BFD_RELOC_IA64_DIR64LSB; - break; + { + if (target_big_endian) + code = BFD_RELOC_IA64_DIR64MSB; + else + code = BFD_RELOC_IA64_DIR64LSB; + break; + } + + case 16: + if (exp->X_op == O_pseudo_fixup + && exp->X_op_symbol + && S_GET_VALUE (exp->X_op_symbol) == FUNC_IPLT_RELOC) + { + if (target_big_endian) + code = BFD_RELOC_IA64_IPLTMSB; + else + code = BFD_RELOC_IA64_IPLTLSB; + exp->X_op = O_symbol; + break; + } + /* FALLTHRU */ default: as_bad ("Unsupported fixup size %d", nbytes); 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); /* We need to store the byte order in effect in case we're going to fix an 8 or 16 bit relocation (for which there no real - relocs available). See md_apply_fix(). */ + relocs available). See md_apply_fix3(). */ fix->tc_fix_data.bigendian = target_big_endian; } @@ -9412,6 +10240,14 @@ ia64_gen_real_reloc_type (sym, r_type) } break; + case FUNC_LT_RELATIVE_X: + switch (r_type) + { + case BFD_RELOC_IA64_IMM22: new = BFD_RELOC_IA64_LTOFF22X; break; + default: break; + } + break; + case FUNC_PC_RELATIVE: switch (r_type) { @@ -9480,9 +10316,76 @@ ia64_gen_real_reloc_type (sym, r_type) break; } break; + + case FUNC_TP_RELATIVE: + switch (r_type) + { + case BFD_RELOC_IA64_IMM14: + new = BFD_RELOC_IA64_TPREL14; break; + case BFD_RELOC_IA64_IMM22: + new = BFD_RELOC_IA64_TPREL22; break; + case BFD_RELOC_IA64_IMM64: + new = BFD_RELOC_IA64_TPREL64I; break; + default: + break; + } + break; + + case FUNC_LT_TP_RELATIVE: + switch (r_type) + { + case BFD_RELOC_IA64_IMM22: + new = BFD_RELOC_IA64_LTOFF_TPREL22; break; + default: + break; + } + break; + + case FUNC_LT_DTP_MODULE: + switch (r_type) + { + case BFD_RELOC_IA64_IMM22: + new = BFD_RELOC_IA64_LTOFF_DTPMOD22; break; + default: + break; + } + break; + + 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: + new = BFD_RELOC_IA64_DTPREL22; break; + case BFD_RELOC_IA64_IMM64: + new = BFD_RELOC_IA64_DTPREL64I; break; + default: + break; + } + break; + + case FUNC_LT_DTP_RELATIVE: + switch (r_type) + { + case BFD_RELOC_IA64_IMM22: + new = BFD_RELOC_IA64_LTOFF_DTPREL22; break; + default: + break; + } + break; + + case FUNC_IPLT_RELOC: + break; + default: abort (); } + /* Hmmmm. Should this ever occur? */ if (new) return new; @@ -9578,15 +10481,15 @@ fix_insn (fix, odesc, value) If fixp->fx_addsy is non-NULL, we'll have to generate a reloc entry (if possible). */ -int -md_apply_fix3 (fix, valuep, seg) + +void +md_apply_fix3 (fix, valP, seg) fixS *fix; - valueT *valuep; - segT seg; + valueT *valP; + segT seg ATTRIBUTE_UNUSED; { char *fixpos; - valueT value = *valuep; - int adjust = 0; + valueT value = *valP; fixpos = fix->fx_frag->fr_literal + fix->fx_where; @@ -9596,22 +10499,18 @@ md_apply_fix3 (fix, valuep, seg) { case BFD_RELOC_IA64_DIR32MSB: fix->fx_r_type = BFD_RELOC_IA64_PCREL32MSB; - adjust = 1; break; case BFD_RELOC_IA64_DIR32LSB: fix->fx_r_type = BFD_RELOC_IA64_PCREL32LSB; - adjust = 1; break; case BFD_RELOC_IA64_DIR64MSB: fix->fx_r_type = BFD_RELOC_IA64_PCREL64MSB; - adjust = 1; break; case BFD_RELOC_IA64_DIR64LSB: fix->fx_r_type = BFD_RELOC_IA64_PCREL64LSB; - adjust = 1; break; default: @@ -9622,20 +10521,30 @@ md_apply_fix3 (fix, valuep, seg) { switch (fix->fx_r_type) { - case 0: + case BFD_RELOC_UNUSED: + /* This must be a TAG13 or TAG13b operand. There are no external + relocs defined for them, so we must give an error. */ as_bad_where (fix->fx_file, fix->fx_line, "%s must have a constant value", elf64_ia64_operands[fix->tc_fix_data.opnd].desc); + fix->fx_done = 1; + return; + + case BFD_RELOC_IA64_TPREL14: + case BFD_RELOC_IA64_TPREL22: + case BFD_RELOC_IA64_TPREL64I: + case BFD_RELOC_IA64_LTOFF_TPREL22: + case BFD_RELOC_IA64_LTOFF_DTPMOD22: + case BFD_RELOC_IA64_DTPREL14: + case BFD_RELOC_IA64_DTPREL22: + case BFD_RELOC_IA64_DTPREL64I: + case BFD_RELOC_IA64_LTOFF_DTPREL22: + S_SET_THREAD_LOCAL (fix->fx_addsy); break; default: break; } - - /* ??? This is a hack copied from tc-i386.c to make PCREL relocs - work. There should be a better way to handle this. */ - if (adjust) - fix->fx_offset += fix->fx_where + fix->fx_frag->fr_address; } else if (fix->tc_fix_data.opnd == IA64_OPND_NIL) { @@ -9644,15 +10553,12 @@ md_apply_fix3 (fix, valuep, seg) else number_to_chars_littleendian (fixpos, value, fix->fx_size); fix->fx_done = 1; - return 1; } else { fix_insn (fix, elf64_ia64_operands + fix->tc_fix_data.opnd, value); fix->fx_done = 1; - return 1; } - return 1; } /* Generate the BFD reloc to be stuck in the object file from the @@ -9660,7 +10566,7 @@ md_apply_fix3 (fix, valuep, seg) arelent * tc_gen_reloc (sec, fixp) - asection *sec; + asection *sec ATTRIBUTE_UNUSED; fixS *fixp; { arelent *reloc; @@ -9695,7 +10601,6 @@ md_atof (type, lit, size) int *size; { LITTLENUM_TYPE words[MAX_LITTLENUMS]; - LITTLENUM_TYPE *word; char *t; int prec; @@ -9730,66 +10635,276 @@ md_atof (type, lit, size) 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); } + else + *size = prec * 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; +/* Handle ia64 specific semantics of the align directive. */ + +void +ia64_md_do_align (n, fill, len, max) + int n ATTRIBUTE_UNUSED; + const char *fill ATTRIBUTE_UNUSED; + int len ATTRIBUTE_UNUSED; + int max ATTRIBUTE_UNUSED; { - int align = bfd_get_section_alignment (stdoutput, seg); - valueT mask = ((valueT) 1 << align) - 1; + if (subseg_text_p (now_seg)) + ia64_flush_insns (); +} + +/* This is called from HANDLE_ALIGN in write.c. Fill in the contents + of an rs_align_code fragment. */ + +void +ia64_handle_align (fragp) + 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}; + + int bytes; + char *p; - return (size + mask) & ~mask; + if (fragp->fr_type != rs_align_code) + return; + + bytes = fragp->fr_next->fr_address - fragp->fr_address - fragp->fr_fix; + p = fragp->fr_literal + fragp->fr_fix; + + /* Make sure we are on a 16-byte boundary, in case someone has been + putting data into a text section. */ + if (bytes & 15) + { + int fix = bytes & 15; + memset (p, 0, fix); + p += fix; + bytes -= fix; + fragp->fr_fix += fix; + } + + memcpy (p, (target_big_endian ? be_nop : le_nop), 16); + fragp->fr_var = 16; } -/* Handle ia64 specific semantics of the align directive. */ +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); + } +} -int -ia64_md_do_align (n, fill, len, max) - int n; - const char *fill; - int len; - int max; +static void +ia64_float_to_chars_littleendian (char *lit, LITTLENUM_TYPE *words, + int prec) { - /* Fill any pending bundle with nops. */ - if (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) - ia64_flush_insns (); + while (prec--) + { + number_to_chars_littleendian (lit, (long) (words[prec]), + sizeof (LITTLENUM_TYPE)); + lit += sizeof (LITTLENUM_TYPE); + } +} - /* When we align code in a text section, emit a bundle of 3 nops instead of - zero bytes. We can only do this if a multiple of 16 bytes was requested. - N is log base 2 of the requested alignment. */ - if (fill == NULL - && bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE - && n > 4) - { - /* 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}; - - /* Make sure we are on a 16-byte boundary, in case someone has been - putting data into a text section. */ - frag_align (4, 0, 0); +void +ia64_elf_section_change_hook (void) +{ + dot_byteorder (-1); +} - if (target_big_endian) - frag_align_pattern (n, be_nop, 16, max); - else - frag_align_pattern (n, le_nop, 16, max); - return 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++; } +} - return 0; +/* 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); }