X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-ppc.c;h=c18a274793d94084e96f363a1d8ca8c9c476009b;hb=f589a541dc723a8cf7104bdce5ac8413e594f8fe;hp=e2f4ccc753cfe0c557453d26553cc245ccfedcbb;hpb=1eeb357e6a549344090d3a9da973d338ae65ef23;p=deliverable%2Fbinutils-gdb.git diff --git a/gas/config/tc-ppc.c b/gas/config/tc-ppc.c index e2f4ccc753..c18a274793 100644 --- a/gas/config/tc-ppc.c +++ b/gas/config/tc-ppc.c @@ -1,5 +1,5 @@ /* tc-ppc.c -- Assemble for the PowerPC or POWER (RS/6000) - Copyright (C) 1994 Free Software Foundation, Inc. + Copyright (C) 1994, 95, 96, 97, 98, 1999 Free Software Foundation, Inc. Written by Ian Lance Taylor, Cygnus Support. This file is part of GAS, the GNU Assembler. @@ -15,8 +15,9 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with GAS; see the file COPYING. If not, write to - the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ + along with GAS; see the file COPYING. If not, write to the Free + Software Foundation, 59 Temple Place - Suite 330, Boston, MA + 02111-1307, USA. */ #include #include @@ -25,11 +26,34 @@ #include "opcode/ppc.h" +#ifdef OBJ_ELF +#include "elf/ppc.h" +#endif + +#ifdef TE_PE +#include "coff/pe.h" +#endif + /* This is the assembler for the PowerPC or POWER (RS/6000) chips. */ -/* FIXME: This should be handled in a different way. */ +/* Tell the main code what the endianness is. */ extern int target_big_endian; +/* Whether or not, we've set target_big_endian. */ +static int set_target_endian = 0; + +/* Whether to use user friendly register names. */ +#ifndef TARGET_REG_NAMES_P +#ifdef TE_PE +#define TARGET_REG_NAMES_P true +#else +#define TARGET_REG_NAMES_P false +#endif +#endif + +static boolean reg_names_p = TARGET_REG_NAMES_P; + +static boolean register_name PARAMS ((expressionS *)); static void ppc_set_cpu PARAMS ((void)); static unsigned long ppc_insert_operand PARAMS ((unsigned long insn, const struct powerpc_operand *operand, @@ -38,29 +62,74 @@ static void ppc_macro PARAMS ((char *str, const struct powerpc_macro *macro)); static void ppc_byte PARAMS ((int)); static int ppc_is_toc_sym PARAMS ((symbolS *sym)); static void ppc_tc PARAMS ((int)); -#ifdef OBJ_COFF + +#ifdef OBJ_XCOFF static void ppc_comm PARAMS ((int)); static void ppc_bb PARAMS ((int)); +static void ppc_bc PARAMS ((int)); static void ppc_bf PARAMS ((int)); static void ppc_biei PARAMS ((int)); static void ppc_bs PARAMS ((int)); static void ppc_eb PARAMS ((int)); +static void ppc_ec PARAMS ((int)); static void ppc_ef PARAMS ((int)); static void ppc_es PARAMS ((int)); static void ppc_csect PARAMS ((int)); +static void ppc_change_csect PARAMS ((symbolS *)); static void ppc_function PARAMS ((int)); static void ppc_extern PARAMS ((int)); static void ppc_lglobl PARAMS ((int)); +static void ppc_section PARAMS ((int)); +static void ppc_named_section PARAMS ((int)); static void ppc_stabx PARAMS ((int)); static void ppc_rename PARAMS ((int)); static void ppc_toc PARAMS ((int)); +static void ppc_xcoff_cons PARAMS ((int)); +static void ppc_vbyte PARAMS ((int)); +#endif + +#ifdef OBJ_ELF +static bfd_reloc_code_real_type ppc_elf_suffix PARAMS ((char **, expressionS *)); +static void ppc_elf_cons PARAMS ((int)); +static void ppc_elf_rdata PARAMS ((int)); +static void ppc_elf_lcomm PARAMS ((int)); +static void ppc_elf_validate_fix PARAMS ((fixS *, segT)); +#endif + +#ifdef TE_PE +static void ppc_set_current_section PARAMS ((segT)); +static void ppc_previous PARAMS ((int)); +static void ppc_pdata PARAMS ((int)); +static void ppc_ydata PARAMS ((int)); +static void ppc_reldata PARAMS ((int)); +static void ppc_rdata PARAMS ((int)); +static void ppc_ualong PARAMS ((int)); +static void ppc_znop PARAMS ((int)); +static void ppc_pe_comm PARAMS ((int)); +static void ppc_pe_section PARAMS ((int)); +static void ppc_pe_function PARAMS ((int)); +static void ppc_pe_tocd PARAMS ((int)); #endif /* Generic assembler global variables which must be defined by all targets. */ -/* Characters which always start a comment. */ +#ifdef OBJ_ELF +/* This string holds the chars that always start a comment. If the + pre-processor is disabled, these aren't very useful. The macro + tc_comment_chars points to this. We use this, rather than the + usual comment_chars, so that we can switch for Solaris conventions. */ +static const char ppc_solaris_comment_chars[] = "#!"; +static const char ppc_eabi_comment_chars[] = "#"; + +#ifdef TARGET_SOLARIS_COMMENT +const char *ppc_comment_chars = ppc_solaris_comment_chars; +#else +const char *ppc_comment_chars = ppc_eabi_comment_chars; +#endif +#else const char comment_chars[] = "#"; +#endif /* Characters which start a comment at the beginning of a line. */ const char line_comment_chars[] = "#"; @@ -84,7 +153,7 @@ const pseudo_typeS md_pseudo_table[] = /* Pseudo-ops which must be overridden. */ { "byte", ppc_byte, 0 }, -#ifdef OBJ_COFF +#ifdef OBJ_XCOFF /* Pseudo-ops specific to the RS/6000 XCOFF format. Some of these legitimately belong in the obj-*.c file. However, XCOFF is based on COFF, and is only implemented for the RS/6000. We just use @@ -92,11 +161,14 @@ const pseudo_typeS md_pseudo_table[] = { "comm", ppc_comm, 0 }, { "lcomm", ppc_comm, 1 }, { "bb", ppc_bb, 0 }, + { "bc", ppc_bc, 0 }, { "bf", ppc_bf, 0 }, { "bi", ppc_biei, 0 }, { "bs", ppc_bs, 0 }, { "csect", ppc_csect, 0 }, + { "data", ppc_section, 'd' }, { "eb", ppc_eb, 0 }, + { "ec", ppc_ec, 0 }, { "ef", ppc_ef, 0 }, { "ei", ppc_biei, 1 }, { "es", ppc_es, 0 }, @@ -104,8 +176,39 @@ const pseudo_typeS md_pseudo_table[] = { "function", ppc_function, 0 }, { "lglobl", ppc_lglobl, 0 }, { "rename", ppc_rename, 0 }, + { "section", ppc_named_section, 0 }, { "stabx", ppc_stabx, 0 }, + { "text", ppc_section, 't' }, { "toc", ppc_toc, 0 }, + { "long", ppc_xcoff_cons, 2 }, + { "word", ppc_xcoff_cons, 1 }, + { "short", ppc_xcoff_cons, 1 }, + { "vbyte", ppc_vbyte, 0 }, +#endif + +#ifdef OBJ_ELF + { "long", ppc_elf_cons, 4 }, + { "word", ppc_elf_cons, 2 }, + { "short", ppc_elf_cons, 2 }, + { "rdata", ppc_elf_rdata, 0 }, + { "rodata", ppc_elf_rdata, 0 }, + { "lcomm", ppc_elf_lcomm, 0 }, +#endif + +#ifdef TE_PE + /* Pseudo-ops specific to the Windows NT PowerPC PE (coff) format */ + { "previous", ppc_previous, 0 }, + { "pdata", ppc_pdata, 0 }, + { "ydata", ppc_ydata, 0 }, + { "reldata", ppc_reldata, 0 }, + { "rdata", ppc_rdata, 0 }, + { "ualong", ppc_ualong, 0 }, + { "znop", ppc_znop, 0 }, + { "comm", ppc_pe_comm, 0 }, + { "lcomm", ppc_pe_comm, 1 }, + { "section", ppc_pe_section, 0 }, + { "function", ppc_pe_function,0 }, + { "tocd", ppc_pe_tocd, 0 }, #endif /* This pseudo-op is used even when not generating XCOFF output. */ @@ -113,6 +216,369 @@ const pseudo_typeS md_pseudo_table[] = { NULL, NULL, 0 } }; + + +/* Predefined register names if -mregnames (or default for Windows NT). */ +/* In general, there are lots of them, in an attempt to be compatible */ +/* with a number of other Windows NT assemblers. */ + +/* Structure to hold information about predefined registers. */ +struct pd_reg + { + char *name; + int value; + }; + +/* List of registers that are pre-defined: + + Each general register has predefined names of the form: + 1. r which has the value . + 2. r. which has the value . + + + Each floating point register has predefined names of the form: + 1. f which has the value . + 2. f. which has the value . + + Each condition register has predefined names of the form: + 1. cr which has the value . + 2. cr. which has the value . + + There are individual registers as well: + sp or r.sp has the value 1 + rtoc or r.toc has the value 2 + fpscr has the value 0 + xer has the value 1 + lr has the value 8 + ctr has the value 9 + pmr has the value 0 + dar has the value 19 + dsisr has the value 18 + dec has the value 22 + sdr1 has the value 25 + srr0 has the value 26 + srr1 has the value 27 + + The table is sorted. Suitable for searching by a binary search. */ + +static const struct pd_reg pre_defined_registers[] = +{ + { "cr.0", 0 }, /* Condition Registers */ + { "cr.1", 1 }, + { "cr.2", 2 }, + { "cr.3", 3 }, + { "cr.4", 4 }, + { "cr.5", 5 }, + { "cr.6", 6 }, + { "cr.7", 7 }, + + { "cr0", 0 }, + { "cr1", 1 }, + { "cr2", 2 }, + { "cr3", 3 }, + { "cr4", 4 }, + { "cr5", 5 }, + { "cr6", 6 }, + { "cr7", 7 }, + + { "ctr", 9 }, + + { "dar", 19 }, /* Data Access Register */ + { "dec", 22 }, /* Decrementer */ + { "dsisr", 18 }, /* Data Storage Interrupt Status Register */ + + { "f.0", 0 }, /* Floating point registers */ + { "f.1", 1 }, + { "f.10", 10 }, + { "f.11", 11 }, + { "f.12", 12 }, + { "f.13", 13 }, + { "f.14", 14 }, + { "f.15", 15 }, + { "f.16", 16 }, + { "f.17", 17 }, + { "f.18", 18 }, + { "f.19", 19 }, + { "f.2", 2 }, + { "f.20", 20 }, + { "f.21", 21 }, + { "f.22", 22 }, + { "f.23", 23 }, + { "f.24", 24 }, + { "f.25", 25 }, + { "f.26", 26 }, + { "f.27", 27 }, + { "f.28", 28 }, + { "f.29", 29 }, + { "f.3", 3 }, + { "f.30", 30 }, + { "f.31", 31 }, + { "f.4", 4 }, + { "f.5", 5 }, + { "f.6", 6 }, + { "f.7", 7 }, + { "f.8", 8 }, + { "f.9", 9 }, + + { "f0", 0 }, + { "f1", 1 }, + { "f10", 10 }, + { "f11", 11 }, + { "f12", 12 }, + { "f13", 13 }, + { "f14", 14 }, + { "f15", 15 }, + { "f16", 16 }, + { "f17", 17 }, + { "f18", 18 }, + { "f19", 19 }, + { "f2", 2 }, + { "f20", 20 }, + { "f21", 21 }, + { "f22", 22 }, + { "f23", 23 }, + { "f24", 24 }, + { "f25", 25 }, + { "f26", 26 }, + { "f27", 27 }, + { "f28", 28 }, + { "f29", 29 }, + { "f3", 3 }, + { "f30", 30 }, + { "f31", 31 }, + { "f4", 4 }, + { "f5", 5 }, + { "f6", 6 }, + { "f7", 7 }, + { "f8", 8 }, + { "f9", 9 }, + + { "fpscr", 0 }, + + { "lr", 8 }, /* Link Register */ + + { "pmr", 0 }, + + { "r.0", 0 }, /* General Purpose Registers */ + { "r.1", 1 }, + { "r.10", 10 }, + { "r.11", 11 }, + { "r.12", 12 }, + { "r.13", 13 }, + { "r.14", 14 }, + { "r.15", 15 }, + { "r.16", 16 }, + { "r.17", 17 }, + { "r.18", 18 }, + { "r.19", 19 }, + { "r.2", 2 }, + { "r.20", 20 }, + { "r.21", 21 }, + { "r.22", 22 }, + { "r.23", 23 }, + { "r.24", 24 }, + { "r.25", 25 }, + { "r.26", 26 }, + { "r.27", 27 }, + { "r.28", 28 }, + { "r.29", 29 }, + { "r.3", 3 }, + { "r.30", 30 }, + { "r.31", 31 }, + { "r.4", 4 }, + { "r.5", 5 }, + { "r.6", 6 }, + { "r.7", 7 }, + { "r.8", 8 }, + { "r.9", 9 }, + + { "r.sp", 1 }, /* Stack Pointer */ + + { "r.toc", 2 }, /* Pointer to the table of contents */ + + { "r0", 0 }, /* More general purpose registers */ + { "r1", 1 }, + { "r10", 10 }, + { "r11", 11 }, + { "r12", 12 }, + { "r13", 13 }, + { "r14", 14 }, + { "r15", 15 }, + { "r16", 16 }, + { "r17", 17 }, + { "r18", 18 }, + { "r19", 19 }, + { "r2", 2 }, + { "r20", 20 }, + { "r21", 21 }, + { "r22", 22 }, + { "r23", 23 }, + { "r24", 24 }, + { "r25", 25 }, + { "r26", 26 }, + { "r27", 27 }, + { "r28", 28 }, + { "r29", 29 }, + { "r3", 3 }, + { "r30", 30 }, + { "r31", 31 }, + { "r4", 4 }, + { "r5", 5 }, + { "r6", 6 }, + { "r7", 7 }, + { "r8", 8 }, + { "r9", 9 }, + + { "rtoc", 2 }, /* Table of contents */ + + { "sdr1", 25 }, /* Storage Description Register 1 */ + + { "sp", 1 }, + + { "srr0", 26 }, /* Machine Status Save/Restore Register 0 */ + { "srr1", 27 }, /* Machine Status Save/Restore Register 1 */ + + { "xer", 1 }, + +}; + +#define REG_NAME_CNT (sizeof(pre_defined_registers) / sizeof(struct pd_reg)) + +/* Given NAME, find the register number associated with that name, return + the integer value associated with the given name or -1 on failure. */ + +static int reg_name_search + PARAMS ((const struct pd_reg *, int, const char * name)); + +static int +reg_name_search (regs, regcount, name) + const struct pd_reg *regs; + int regcount; + const char *name; +{ + int middle, low, high; + int cmp; + + low = 0; + high = regcount - 1; + + do + { + middle = (low + high) / 2; + cmp = strcasecmp (name, regs[middle].name); + if (cmp < 0) + high = middle - 1; + else if (cmp > 0) + low = middle + 1; + else + return regs[middle].value; + } + while (low <= high); + + return -1; +} + +/* + * Summary of register_name(). + * + * in: Input_line_pointer points to 1st char of operand. + * + * out: A expressionS. + * The operand may have been a register: in this case, X_op == O_register, + * X_add_number is set to the register number, and truth is returned. + * Input_line_pointer->(next non-blank) char after operand, or is in its + * original state. + */ + +static boolean +register_name (expressionP) + expressionS *expressionP; +{ + int reg_number; + char *name; + char *start; + char c; + + /* Find the spelling of the operand */ + start = name = input_line_pointer; + if (name[0] == '%' && isalpha (name[1])) + name = ++input_line_pointer; + + else if (!reg_names_p || !isalpha (name[0])) + return false; + + c = get_symbol_end (); + reg_number = reg_name_search (pre_defined_registers, REG_NAME_CNT, name); + + /* look to see if it's in the register table */ + if (reg_number >= 0) + { + expressionP->X_op = O_register; + expressionP->X_add_number = reg_number; + + /* make the rest nice */ + expressionP->X_add_symbol = NULL; + expressionP->X_op_symbol = NULL; + *input_line_pointer = c; /* put back the delimiting char */ + return true; + } + else + { + /* reset the line as if we had not done anything */ + *input_line_pointer = c; /* put back the delimiting char */ + input_line_pointer = start; /* reset input_line pointer */ + return false; + } +} + +/* This function is called for each symbol seen in an expression. It + handles the special parsing which PowerPC assemblers are supposed + to use for condition codes. */ + +/* Whether to do the special parsing. */ +static boolean cr_operand; + +/* Names to recognize in a condition code. This table is sorted. */ +static const struct pd_reg cr_names[] = +{ + { "cr0", 0 }, + { "cr1", 1 }, + { "cr2", 2 }, + { "cr3", 3 }, + { "cr4", 4 }, + { "cr5", 5 }, + { "cr6", 6 }, + { "cr7", 7 }, + { "eq", 2 }, + { "gt", 1 }, + { "lt", 0 }, + { "so", 3 }, + { "un", 3 } +}; + +/* Parsing function. This returns non-zero if it recognized an + expression. */ + +int +ppc_parse_name (name, expr) + const char *name; + expressionS *expr; +{ + int val; + + if (! cr_operand) + return 0; + + val = reg_name_search (cr_names, sizeof cr_names / sizeof cr_names[0], + name); + if (val < 0) + return 0; + + expr->X_op = O_constant; + expr->X_add_number = val; + + return 1; +} /* Local variables. */ @@ -124,16 +590,30 @@ static int ppc_cpu = 0; PPC_OPCODE_32 or PPC_OPCODE_64. */ static int ppc_size = PPC_OPCODE_32; -/* The endianness we are using. */ -static int ppc_big_endian = PPC_BIG_ENDIAN; - /* Opcode hash table. */ static struct hash_control *ppc_hash; /* Macro hash table. */ static struct hash_control *ppc_macro_hash; -#ifdef OBJ_COFF +#ifdef OBJ_ELF +/* What type of shared library support to use */ +static enum { SHLIB_NONE, SHLIB_PIC, SHILB_MRELOCATABLE } shlib = SHLIB_NONE; + +/* Flags to set in the elf header */ +static flagword ppc_flags = 0; + +/* Whether this is Solaris or not. */ +#ifdef TARGET_SOLARIS_COMMENT +#define SOLARIS_P true +#else +#define SOLARIS_P false +#endif + +static boolean msolaris = SOLARIS_P; +#endif + +#ifdef OBJ_XCOFF /* The RS/6000 assembler uses the .csect pseudo-op to generate code using a bunch of different sections. These assembler sections, @@ -178,13 +658,29 @@ static symbolS *ppc_current_block; cause BFD to set the section number of a symbol to N_DEBUG. */ static asection *ppc_coff_debug_section; -/* The size of the .debug section. */ -static bfd_size_type ppc_debug_name_section_size; +#endif /* OBJ_XCOFF */ + +#ifdef TE_PE + +/* Various sections that we need for PE coff support. */ +static segT ydata_section; +static segT pdata_section; +static segT reldata_section; +static segT rdata_section; +static segT tocdata_section; + +/* The current section and the previous section. See ppc_previous. */ +static segT ppc_previous_section; +static segT ppc_current_section; -#endif /* OBJ_COFF */ +#endif /* TE_PE */ + +#ifdef OBJ_ELF +symbolS *GOT_symbol; /* Pre-defined "_GLOBAL_OFFSET_TABLE" */ +#endif /* OBJ_ELF */ #ifdef OBJ_ELF -CONST char *md_shortopts = "um:VQ:"; +CONST char *md_shortopts = "b:l:usm:K:VQ:"; #else CONST char *md_shortopts = "um:"; #endif @@ -205,6 +701,44 @@ md_parse_option (c, arg) external, which is the default for gas anyhow. */ break; +#ifdef OBJ_ELF + case 'l': + /* Solaris as takes -le (presumably for little endian). For completeness + sake, recognize -be also. */ + if (strcmp (arg, "e") == 0) + { + target_big_endian = 0; + set_target_endian = 1; + } + else + return 0; + + break; + + case 'b': + if (strcmp (arg, "e") == 0) + { + target_big_endian = 1; + set_target_endian = 1; + } + else + return 0; + + break; + + case 'K': + /* Recognize -K PIC */ + if (strcmp (arg, "PIC") == 0 || strcmp (arg, "pic") == 0) + { + shlib = SHLIB_PIC; + ppc_flags |= EF_PPC_RELOCATABLE_LIB; + } + else + return 0; + + break; +#endif + case 'm': /* -mpwrx and -mpwr2 mean to assemble for the IBM POWER/2 (RIOS2). */ @@ -213,16 +747,15 @@ md_parse_option (c, arg) /* -mpwr means to assemble for the IBM POWER (RIOS1). */ else if (strcmp (arg, "pwr") == 0) ppc_cpu = PPC_OPCODE_POWER; - /* -m601 means to assemble for the Motorola PowerPC 601. FIXME: We - ignore the option for now, but we should really use it to permit - instructions defined on the 601 that are not part of the standard - PowerPC architecture (mostly holdovers from the POWER). */ + /* -m601 means to assemble for the Motorola PowerPC 601, which includes + instructions that are holdovers from the Power. */ else if (strcmp (arg, "601") == 0) ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_601; /* -mppc, -mppc32, -m603, and -m604 mean to assemble for the Motorola PowerPC 603/604. */ else if (strcmp (arg, "ppc") == 0 || strcmp (arg, "ppc32") == 0 + || strcmp (arg, "403") == 0 || strcmp (arg, "603") == 0 || strcmp (arg, "604") == 0) ppc_cpu = PPC_OPCODE_PPC; @@ -233,12 +766,72 @@ md_parse_option (c, arg) ppc_cpu = PPC_OPCODE_PPC; ppc_size = PPC_OPCODE_64; } + else if (strcmp (arg, "ppc64bridge") == 0) + { + ppc_cpu = PPC_OPCODE_PPC | PPC_OPCODE_64_BRIDGE; + ppc_size = PPC_OPCODE_64; + } + /* -mcom means assemble for the common intersection between Power + and PowerPC. At present, we just allow the union, rather + than the intersection. */ + else if (strcmp (arg, "com") == 0) + ppc_cpu = PPC_OPCODE_COMMON; /* -many means to assemble for any architecture (PWR/PWRX/PPC). */ else if (strcmp (arg, "any") == 0) - ppc_cpu = PPC_OPCODE_POWER | PPC_OPCODE_POWER2 | PPC_OPCODE_PPC; + ppc_cpu = PPC_OPCODE_ANY; + + else if (strcmp (arg, "regnames") == 0) + reg_names_p = true; + + else if (strcmp (arg, "no-regnames") == 0) + reg_names_p = false; + +#ifdef OBJ_ELF + /* -mrelocatable/-mrelocatable-lib -- warn about initializations that require relocation */ + else if (strcmp (arg, "relocatable") == 0) + { + shlib = SHILB_MRELOCATABLE; + ppc_flags |= EF_PPC_RELOCATABLE; + } + + else if (strcmp (arg, "relocatable-lib") == 0) + { + shlib = SHILB_MRELOCATABLE; + ppc_flags |= EF_PPC_RELOCATABLE_LIB; + } + + /* -memb, set embedded bit */ + else if (strcmp (arg, "emb") == 0) + ppc_flags |= EF_PPC_EMB; + + /* -mlittle/-mbig set the endianess */ + else if (strcmp (arg, "little") == 0 || strcmp (arg, "little-endian") == 0) + { + target_big_endian = 0; + set_target_endian = 1; + } + + else if (strcmp (arg, "big") == 0 || strcmp (arg, "big-endian") == 0) + { + target_big_endian = 1; + set_target_endian = 1; + } + + else if (strcmp (arg, "solaris") == 0) + { + msolaris = true; + ppc_comment_chars = ppc_solaris_comment_chars; + } + + else if (strcmp (arg, "no-solaris") == 0) + { + msolaris = false; + ppc_comment_chars = ppc_eabi_comment_chars; + } +#endif else { - as_bad ("invalid architecture -m%s", arg); + as_bad (_("invalid switch -m%s"), arg); return 0; } break; @@ -253,6 +846,15 @@ md_parse_option (c, arg) should be emitted or not. FIXME: Not implemented. */ case 'Q': break; + + /* Solaris takes -s to specify that .stabs go in a .stabs section, + rather than .stabs.excl, which is ignored by the linker. + FIXME: Not implemented. */ + case 's': + if (arg) + return 0; + + break; #endif default: @@ -266,18 +868,32 @@ void md_show_usage (stream) FILE *stream; { - fprintf(stream, "\ + fprintf(stream, _("\ PowerPC options:\n\ -u ignored\n\ --mpwrx generate code for IBM POWER/2 (RIOS2)\n\ +-mpwrx, -mpwr2 generate code for IBM POWER/2 (RIOS2)\n\ -mpwr generate code for IBM POWER (RIOS1)\n\ -m601 generate code for Motorola PowerPC 601\n\ --mppc generate code for Motorola PowerPC 603/604\n\ --many generate code for any architecture (PWR/PWRX/PPC)\n"); +-mppc, -mppc32, -m403, -m603, -m604\n\ + generate code for Motorola PowerPC 603/604\n\ +-mppc64, -m620 generate code for Motorola PowerPC 620\n\ +-mppc64bridge generate code for PowerPC 64, including bridge insns\n\ +-mcom generate code Power/PowerPC common instructions\n\ +-many generate code for any architecture (PWR/PWRX/PPC)\n\ +-mregnames Allow symbolic names for registers\n\ +-mno-regnames Do not allow symbolic names for registers\n")); #ifdef OBJ_ELF - fprintf(stream, "\ + fprintf(stream, _("\ +-mrelocatable support for GCC's -mrelocatble option\n\ +-mrelocatable-lib support for GCC's -mrelocatble-lib option\n\ +-memb set PPC_EMB bit in ELF flags\n\ +-mlittle, -mlittle-endian\n\ + generate code for a little endian machine\n\ +-mbig, -mbig-endian generate code for a big endian machine\n\ +-msolaris generate code for Solaris\n\ +-mno-solaris do not generate code for Solaris\n\ -V print assembler version number\n\ --Qy, -Qn ignored\n"); +-Qy, -Qn ignored\n")); #endif } @@ -286,16 +902,23 @@ PowerPC options:\n\ static void ppc_set_cpu () { + const char *default_os = TARGET_OS; const char *default_cpu = TARGET_CPU; if (ppc_cpu == 0) { - if (strcmp (default_cpu, "rs6000") == 0) + if (strncmp (default_os, "aix", 3) == 0 + && default_os[3] >= '4' && default_os[3] <= '9') + ppc_cpu = PPC_OPCODE_COMMON; + else if (strncmp (default_os, "aix3", 4) == 0) ppc_cpu = PPC_OPCODE_POWER; - else if (strcmp (default_cpu, "powerpc") == 0) + else if (strcmp (default_cpu, "rs6000") == 0) + ppc_cpu = PPC_OPCODE_POWER; + else if (strcmp (default_cpu, "powerpc") == 0 + || strcmp (default_cpu, "powerpcle") == 0) ppc_cpu = PPC_OPCODE_PPC; else - abort (); + as_fatal (_("Unknown default cpu = %s, os = %s"), default_cpu, default_os); } } @@ -304,14 +927,24 @@ ppc_set_cpu () enum bfd_architecture ppc_arch () { + const char *default_cpu = TARGET_CPU; ppc_set_cpu (); if ((ppc_cpu & PPC_OPCODE_PPC) != 0) return bfd_arch_powerpc; else if ((ppc_cpu & PPC_OPCODE_POWER) != 0) return bfd_arch_rs6000; - else - abort (); + else if ((ppc_cpu & (PPC_OPCODE_COMMON | PPC_OPCODE_ANY)) != 0) + { + if (strcmp (default_cpu, "rs6000") == 0) + return bfd_arch_rs6000; + else if (strcmp (default_cpu, "powerpc") == 0 + || strcmp (default_cpu, "powerpcle") == 0) + return bfd_arch_powerpc; + } + + as_fatal (_("Neither Power nor PowerPC opcodes were selected.")); + return bfd_arch_unknown; } /* This function is called when the assembler starts up. It is called @@ -325,9 +958,16 @@ md_begin () const struct powerpc_opcode *op_end; const struct powerpc_macro *macro; const struct powerpc_macro *macro_end; + boolean dup_insn = false; ppc_set_cpu (); +#ifdef OBJ_ELF + /* Set the ELF flags if desired. */ + if (ppc_flags && !msolaris) + bfd_set_private_flags (stdoutput, ppc_flags); +#endif + /* Insert the opcodes into a hash table. */ ppc_hash = hash_new (); @@ -338,23 +978,21 @@ md_begin () if ((op->flags & ppc_cpu) != 0 && ((op->flags & (PPC_OPCODE_32 | PPC_OPCODE_64)) == 0 - || (op->flags & (PPC_OPCODE_32 | PPC_OPCODE_64)) == ppc_size)) + || (op->flags & (PPC_OPCODE_32 | PPC_OPCODE_64)) == ppc_size + || (ppc_cpu & PPC_OPCODE_64_BRIDGE) != 0)) { const char *retval; retval = hash_insert (ppc_hash, op->name, (PTR) op); if (retval != (const char *) NULL) { - /* When using -many, the comparison instructions are a - harmless special case. */ - if (ppc_cpu != (PPC_OPCODE_POWER - | PPC_OPCODE_POWER2 - | PPC_OPCODE_PPC) - || (strcmp (op->name, "cmpli") != 0 - && strcmp (op->name, "cmpi") != 0 - && strcmp (op->name, "cmp") != 0 - && strcmp (op->name, "cmpl") != 0)) - abort (); + /* Ignore Power duplicates for -m601 */ + if ((ppc_cpu & PPC_OPCODE_601) != 0 + && (op->flags & PPC_OPCODE_POWER) != 0) + continue; + + as_bad (_("Internal assembler error for instruction %s"), op->name); + dup_insn = true; } } } @@ -371,23 +1009,40 @@ md_begin () retval = hash_insert (ppc_macro_hash, macro->name, (PTR) macro); if (retval != (const char *) NULL) - abort (); + { + as_bad (_("Internal assembler error for macro %s"), macro->name); + dup_insn = true; + } } } - /* Tell the main code what the endianness is. */ - target_big_endian = ppc_big_endian; + if (dup_insn) + abort (); + + /* Tell the main code what the endianness is if it is not overidden by the user. */ + if (!set_target_endian) + { + set_target_endian = 1; + target_big_endian = PPC_BIG_ENDIAN; + } -#ifdef OBJ_COFF +#ifdef OBJ_XCOFF ppc_coff_debug_section = coff_section_from_bfd_index (stdoutput, N_DEBUG); /* Create dummy symbols to serve as initial csects. This forces the text csects to precede the data csects. These symbols will not be output. */ ppc_text_csects = symbol_make ("dummy\001"); - ppc_text_csects->sy_tc.within = ppc_text_csects; + symbol_get_tc (ppc_text_csects)->within = ppc_text_csects; ppc_data_csects = symbol_make ("dummy\001"); - ppc_data_csects->sy_tc.within = ppc_data_csects; + symbol_get_tc (ppc_data_csects)->within = ppc_data_csects; +#endif + +#ifdef TE_PE + + ppc_current_section = text_section; + ppc_previous_section = 0; + #endif } @@ -408,12 +1063,26 @@ ppc_insert_operand (insn, operand, val, file, line) if ((operand->flags & PPC_OPERAND_SIGNED) != 0) { - if ((operand->flags & PPC_OPERAND_SIGNOPT) != 0 - && ppc_size == PPC_OPCODE_32) + if ((operand->flags & PPC_OPERAND_SIGNOPT) != 0) max = (1 << operand->bits) - 1; else max = (1 << (operand->bits - 1)) - 1; min = - (1 << (operand->bits - 1)); + + if (ppc_size == PPC_OPCODE_32) + { + /* Some people write 32 bit hex constants with the sign + extension done by hand. This shouldn't really be + valid, but, to permit this code to assemble on a 64 + bit host, we sign extend the 32 bit value. */ + if (val > 0 + && (val & 0x80000000) != 0 + && (val & 0xffffffff) == val) + { + val -= 0x80000000; + val -= 0x80000000; + } + } } else { @@ -429,14 +1098,14 @@ ppc_insert_operand (insn, operand, val, file, line) if (test < (offsetT) min || test > (offsetT) max) { const char *err = - "operand out of range (%s not between %ld and %ld)"; + _("operand out of range (%s not between %ld and %ld)"); char buf[100]; sprint_value (buf, test); if (file == (char *) NULL) - as_warn (err, buf, min, max); + as_bad (err, buf, min, max); else - as_warn_where (file, line, err, buf, min, max); + as_bad_where (file, line, err, buf, min, max); } } @@ -447,7 +1116,7 @@ ppc_insert_operand (insn, operand, val, file, line) errmsg = NULL; insn = (*operand->insert) (insn, (long) val, &errmsg); if (errmsg != (const char *) NULL) - as_warn (errmsg); + as_bad (errmsg); } else insn |= (((long) val & ((1 << operand->bits) - 1)) @@ -456,90 +1125,531 @@ ppc_insert_operand (insn, operand, val, file, line) return insn; } -/* We need to keep a list of fixups. We can't simply generate them as - we go, because that would require us to first create the frag, and - that would screw up references to ``.''. */ - -struct ppc_fixup + +#ifdef OBJ_ELF +/* Parse @got, etc. and return the desired relocation. */ +static bfd_reloc_code_real_type +ppc_elf_suffix (str_p, exp_p) + char **str_p; + expressionS *exp_p; { - expressionS exp; - int opindex; -}; - -#define MAX_INSN_FIXUPS (5) + struct map_bfd { + char *string; + int length; + bfd_reloc_code_real_type reloc; + }; + + char ident[20]; + char *str = *str_p; + char *str2; + int ch; + int len; + struct map_bfd *ptr; + +#define MAP(str,reloc) { str, sizeof(str)-1, reloc } + + static struct map_bfd mapping[] = { + MAP ("l", BFD_RELOC_LO16), + MAP ("h", BFD_RELOC_HI16), + MAP ("ha", BFD_RELOC_HI16_S), + MAP ("brtaken", BFD_RELOC_PPC_B16_BRTAKEN), + MAP ("brntaken", BFD_RELOC_PPC_B16_BRNTAKEN), + MAP ("got", BFD_RELOC_16_GOTOFF), + MAP ("got@l", BFD_RELOC_LO16_GOTOFF), + MAP ("got@h", BFD_RELOC_HI16_GOTOFF), + MAP ("got@ha", BFD_RELOC_HI16_S_GOTOFF), + MAP ("fixup", BFD_RELOC_CTOR), /* warnings with -mrelocatable */ + MAP ("plt", BFD_RELOC_24_PLT_PCREL), + MAP ("pltrel24", BFD_RELOC_24_PLT_PCREL), + MAP ("copy", BFD_RELOC_PPC_COPY), + MAP ("globdat", BFD_RELOC_PPC_GLOB_DAT), + MAP ("local24pc", BFD_RELOC_PPC_LOCAL24PC), + MAP ("local", BFD_RELOC_PPC_LOCAL24PC), + MAP ("pltrel", BFD_RELOC_32_PLT_PCREL), + MAP ("plt@l", BFD_RELOC_LO16_PLTOFF), + MAP ("plt@h", BFD_RELOC_HI16_PLTOFF), + MAP ("plt@ha", BFD_RELOC_HI16_S_PLTOFF), + MAP ("sdarel", BFD_RELOC_GPREL16), + MAP ("sectoff", BFD_RELOC_32_BASEREL), + MAP ("sectoff@l", BFD_RELOC_LO16_BASEREL), + MAP ("sectoff@h", BFD_RELOC_HI16_BASEREL), + MAP ("sectoff@ha", BFD_RELOC_HI16_S_BASEREL), + MAP ("naddr", BFD_RELOC_PPC_EMB_NADDR32), + MAP ("naddr16", BFD_RELOC_PPC_EMB_NADDR16), + MAP ("naddr@l", BFD_RELOC_PPC_EMB_NADDR16_LO), + MAP ("naddr@h", BFD_RELOC_PPC_EMB_NADDR16_HI), + MAP ("naddr@ha", BFD_RELOC_PPC_EMB_NADDR16_HA), + MAP ("sdai16", BFD_RELOC_PPC_EMB_SDAI16), + MAP ("sda2rel", BFD_RELOC_PPC_EMB_SDA2REL), + MAP ("sda2i16", BFD_RELOC_PPC_EMB_SDA2I16), + MAP ("sda21", BFD_RELOC_PPC_EMB_SDA21), + MAP ("mrkref", BFD_RELOC_PPC_EMB_MRKREF), + MAP ("relsect", BFD_RELOC_PPC_EMB_RELSEC16), + MAP ("relsect@l", BFD_RELOC_PPC_EMB_RELST_LO), + MAP ("relsect@h", BFD_RELOC_PPC_EMB_RELST_HI), + MAP ("relsect@ha", BFD_RELOC_PPC_EMB_RELST_HA), + MAP ("bitfld", BFD_RELOC_PPC_EMB_BIT_FLD), + MAP ("relsda", BFD_RELOC_PPC_EMB_RELSDA), + MAP ("xgot", BFD_RELOC_PPC_TOC16), + + { (char *)0, 0, BFD_RELOC_UNUSED } + }; + + if (*str++ != '@') + return BFD_RELOC_UNUSED; + + for (ch = *str, str2 = ident; + (str2 < ident + sizeof (ident) - 1 + && (isalnum (ch) || ch == '@')); + ch = *++str) + { + *str2++ = (islower (ch)) ? ch : tolower (ch); + } -/* This routine is called for each instruction to be assembled. */ + *str2 = '\0'; + len = str2 - ident; -void -md_assemble (str) - char *str; -{ - char *s; - const struct powerpc_opcode *opcode; - unsigned long insn; - const unsigned char *opindex_ptr; - int skip_optional; - int need_paren; - int next_opindex; - struct ppc_fixup fixups[MAX_INSN_FIXUPS]; - int fc; - char *f; - int i; + ch = ident[0]; + for (ptr = &mapping[0]; ptr->length > 0; ptr++) + if (ch == ptr->string[0] + && len == ptr->length + && memcmp (ident, ptr->string, ptr->length) == 0) + { + if (exp_p->X_add_number != 0 + && (ptr->reloc == BFD_RELOC_16_GOTOFF + || ptr->reloc == BFD_RELOC_LO16_GOTOFF + || ptr->reloc == BFD_RELOC_HI16_GOTOFF + || ptr->reloc == BFD_RELOC_HI16_S_GOTOFF)) + as_warn (_("identifier+constant@got means identifier@got+constant")); + + /* Now check for identifier@suffix+constant */ + if (*str == '-' || *str == '+') + { + char *orig_line = input_line_pointer; + expressionS new_exp; + + input_line_pointer = str; + expression (&new_exp); + if (new_exp.X_op == O_constant) + { + exp_p->X_add_number += new_exp.X_add_number; + str = input_line_pointer; + } + + if (&input_line_pointer != str_p) + input_line_pointer = orig_line; + } - /* Get the opcode. */ - for (s = str; *s != '\0' && ! isspace (*s); s++) - ; - if (*s != '\0') - *s++ = '\0'; + *str_p = str; + return ptr->reloc; + } - /* Look up the opcode in the hash table. */ - opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, str); - if (opcode == (const struct powerpc_opcode *) NULL) - { - const struct powerpc_macro *macro; + return BFD_RELOC_UNUSED; +} - macro = (const struct powerpc_macro *) hash_find (ppc_macro_hash, str); - if (macro == (const struct powerpc_macro *) NULL) - as_bad ("Unrecognized opcode: `%s'", str); - else - ppc_macro (s, macro); +/* Like normal .long/.short/.word, except support @got, etc. */ +/* clobbers input_line_pointer, checks */ +/* end-of-line. */ +static void +ppc_elf_cons (nbytes) + register int nbytes; /* 1=.byte, 2=.word, 4=.long */ +{ + expressionS exp; + bfd_reloc_code_real_type reloc; + if (is_it_end_of_statement ()) + { + demand_empty_rest_of_line (); return; } - insn = opcode->opcode; - - str = s; - while (isspace (*str)) - ++str; - - /* PowerPC operands are just expressions. The only real issue is - that a few operand types are optional. All cases which might use - an optional operand separate the operands only with commas (in - some cases parentheses are used, as in ``lwz 1,0(1)'' but such - cases never have optional operands). There is never more than - one optional operand for an instruction. So, before we start - seriously parsing the operands, we check to see if we have an - optional operand, and, if we do, we count the number of commas to - see whether the operand should be omitted. */ - skip_optional = 0; - for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++) + do { - const struct powerpc_operand *operand; - - operand = &powerpc_operands[*opindex_ptr]; - if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0) + expression (&exp); + if (exp.X_op == O_symbol + && *input_line_pointer == '@' + && (reloc = ppc_elf_suffix (&input_line_pointer, &exp)) != BFD_RELOC_UNUSED) { - unsigned int opcount; + reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput, reloc); + int size = bfd_get_reloc_size (reloc_howto); + + if (size > nbytes) + as_bad (_("%s relocations do not fit in %d bytes\n"), reloc_howto->name, nbytes); - /* There is an optional operand. Count the number of - commas in the input line. */ - if (*str == '\0') - opcount = 0; else { - opcount = 1; - s = str; + register char *p = frag_more ((int) nbytes); + int offset = nbytes - size; + + fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size, &exp, 0, reloc); + } + } + else + emit_expr (&exp, (unsigned int) nbytes); + } + while (*input_line_pointer++ == ','); + + input_line_pointer--; /* Put terminator back into stream. */ + demand_empty_rest_of_line (); +} + +/* Solaris pseduo op to change to the .rodata section. */ +static void +ppc_elf_rdata (xxx) + int xxx; +{ + char *save_line = input_line_pointer; + static char section[] = ".rodata\n"; + + /* Just pretend this is .section .rodata */ + input_line_pointer = section; + obj_elf_section (xxx); + + input_line_pointer = save_line; +} + +/* Pseudo op to make file scope bss items */ +static void +ppc_elf_lcomm(xxx) + int xxx; +{ + register char *name; + register char c; + register char *p; + offsetT size; + register symbolS *symbolP; + offsetT align; + segT old_sec; + int old_subsec; + char *pfrag; + int align2; + + name = input_line_pointer; + c = get_symbol_end (); + + /* just after name is now '\0' */ + p = input_line_pointer; + *p = c; + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + { + as_bad (_("Expected comma after symbol-name: rest of line ignored.")); + ignore_rest_of_line (); + return; + } + + input_line_pointer++; /* skip ',' */ + if ((size = get_absolute_expression ()) < 0) + { + as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) size); + ignore_rest_of_line (); + return; + } + + /* The third argument to .lcomm is the alignment. */ + if (*input_line_pointer != ',') + align = 8; + else + { + ++input_line_pointer; + align = get_absolute_expression (); + if (align <= 0) + { + as_warn (_("ignoring bad alignment")); + align = 8; + } + } + + *p = 0; + symbolP = symbol_find_or_make (name); + *p = c; + + if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP)) + { + as_bad (_("Ignoring attempt to re-define symbol `%s'."), + S_GET_NAME (symbolP)); + ignore_rest_of_line (); + return; + } + + if (S_GET_VALUE (symbolP) && S_GET_VALUE (symbolP) != (valueT) size) + { + as_bad (_("Length of .lcomm \"%s\" is already %ld. Not changed to %ld."), + S_GET_NAME (symbolP), + (long) S_GET_VALUE (symbolP), + (long) size); + + ignore_rest_of_line (); + return; + } + + /* allocate_bss: */ + old_sec = now_seg; + old_subsec = now_subseg; + if (align) + { + /* convert to a power of 2 alignment */ + for (align2 = 0; (align & 1) == 0; align >>= 1, ++align2); + if (align != 1) + { + as_bad (_("Common alignment not a power of 2")); + ignore_rest_of_line (); + return; + } + } + else + align2 = 0; + + record_alignment (bss_section, align2); + subseg_set (bss_section, 0); + if (align2) + frag_align (align2, 0, 0); + if (S_GET_SEGMENT (symbolP) == bss_section) + symbol_get_frag (symbolP)->fr_symbol = 0; + symbol_set_frag (symbolP, frag_now); + pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, symbolP, size, + (char *) 0); + *pfrag = 0; + S_SET_SIZE (symbolP, size); + S_SET_SEGMENT (symbolP, bss_section); + subseg_set (old_sec, old_subsec); + demand_empty_rest_of_line (); +} + +/* Validate any relocations emitted for -mrelocatable, possibly adding + fixups for word relocations in writable segments, so we can adjust + them at runtime. */ +static void +ppc_elf_validate_fix (fixp, seg) + fixS *fixp; + segT seg; +{ + if (fixp->fx_done || fixp->fx_pcrel) + return; + + switch (shlib) + { + case SHLIB_NONE: + case SHLIB_PIC: + return; + + case SHILB_MRELOCATABLE: + if (fixp->fx_r_type <= BFD_RELOC_UNUSED + && fixp->fx_r_type != BFD_RELOC_16_GOTOFF + && fixp->fx_r_type != BFD_RELOC_HI16_GOTOFF + && fixp->fx_r_type != BFD_RELOC_LO16_GOTOFF + && fixp->fx_r_type != BFD_RELOC_HI16_S_GOTOFF + && fixp->fx_r_type != BFD_RELOC_32_BASEREL + && fixp->fx_r_type != BFD_RELOC_LO16_BASEREL + && fixp->fx_r_type != BFD_RELOC_HI16_BASEREL + && fixp->fx_r_type != BFD_RELOC_HI16_S_BASEREL + && strcmp (segment_name (seg), ".got2") != 0 + && strcmp (segment_name (seg), ".dtors") != 0 + && strcmp (segment_name (seg), ".ctors") != 0 + && strcmp (segment_name (seg), ".fixup") != 0 + && strcmp (segment_name (seg), ".stab") != 0 + && strcmp (segment_name (seg), ".gcc_except_table") != 0 + && strcmp (segment_name (seg), ".eh_frame") != 0 + && strcmp (segment_name (seg), ".ex_shared") != 0) + { + if ((seg->flags & (SEC_READONLY | SEC_CODE)) != 0 + || fixp->fx_r_type != BFD_RELOC_CTOR) + { + as_bad_where (fixp->fx_file, fixp->fx_line, + _("Relocation cannot be done when using -mrelocatable")); + } + } + return; + } +} +#endif /* OBJ_ELF */ + +#ifdef TE_PE + +/* + * Summary of parse_toc_entry(). + * + * in: Input_line_pointer points to the '[' in one of: + * + * [toc] [tocv] [toc32] [toc64] + * + * Anything else is an error of one kind or another. + * + * out: + * return value: success or failure + * toc_kind: kind of toc reference + * input_line_pointer: + * success: first char after the ']' + * failure: unchanged + * + * settings: + * + * [toc] - rv == success, toc_kind = default_toc + * [tocv] - rv == success, toc_kind = data_in_toc + * [toc32] - rv == success, toc_kind = must_be_32 + * [toc64] - rv == success, toc_kind = must_be_64 + * + */ + +enum toc_size_qualifier +{ + default_toc, /* The toc cell constructed should be the system default size */ + data_in_toc, /* This is a direct reference to a toc cell */ + must_be_32, /* The toc cell constructed must be 32 bits wide */ + must_be_64 /* The toc cell constructed must be 64 bits wide */ +}; + +static int +parse_toc_entry(toc_kind) + enum toc_size_qualifier *toc_kind; +{ + char *start; + char *toc_spec; + char c; + enum toc_size_qualifier t; + + /* save the input_line_pointer */ + start = input_line_pointer; + + /* skip over the '[' , and whitespace */ + ++input_line_pointer; + SKIP_WHITESPACE (); + + /* find the spelling of the operand */ + toc_spec = input_line_pointer; + c = get_symbol_end (); + + if (strcmp(toc_spec, "toc") == 0) + { + t = default_toc; + } + else if (strcmp(toc_spec, "tocv") == 0) + { + t = data_in_toc; + } + else if (strcmp(toc_spec, "toc32") == 0) + { + t = must_be_32; + } + else if (strcmp(toc_spec, "toc64") == 0) + { + t = must_be_64; + } + else + { + as_bad (_("syntax error: invalid toc specifier `%s'"), toc_spec); + *input_line_pointer = c; /* put back the delimiting char */ + input_line_pointer = start; /* reset input_line pointer */ + return 0; + } + + /* now find the ']' */ + *input_line_pointer = c; /* put back the delimiting char */ + + SKIP_WHITESPACE (); /* leading whitespace could be there. */ + c = *input_line_pointer++; /* input_line_pointer->past char in c. */ + + if (c != ']') + { + as_bad (_("syntax error: expected `]', found `%c'"), c); + input_line_pointer = start; /* reset input_line pointer */ + return 0; + } + + *toc_kind = t; /* set return value */ + return 1; +} +#endif + + +/* We need to keep a list of fixups. We can't simply generate them as + we go, because that would require us to first create the frag, and + that would screw up references to ``.''. */ + +struct ppc_fixup +{ + expressionS exp; + int opindex; + bfd_reloc_code_real_type reloc; +}; + +#define MAX_INSN_FIXUPS (5) + +/* This routine is called for each instruction to be assembled. */ + +void +md_assemble (str) + char *str; +{ + char *s; + const struct powerpc_opcode *opcode; + unsigned long insn; + const unsigned char *opindex_ptr; + int skip_optional; + int need_paren; + int next_opindex; + struct ppc_fixup fixups[MAX_INSN_FIXUPS]; + int fc; + char *f; + int i; +#ifdef OBJ_ELF + bfd_reloc_code_real_type reloc; +#endif + + /* Get the opcode. */ + for (s = str; *s != '\0' && ! isspace (*s); s++) + ; + if (*s != '\0') + *s++ = '\0'; + + /* Look up the opcode in the hash table. */ + opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, str); + if (opcode == (const struct powerpc_opcode *) NULL) + { + const struct powerpc_macro *macro; + + macro = (const struct powerpc_macro *) hash_find (ppc_macro_hash, str); + if (macro == (const struct powerpc_macro *) NULL) + as_bad (_("Unrecognized opcode: `%s'"), str); + else + ppc_macro (s, macro); + + return; + } + + insn = opcode->opcode; + + str = s; + while (isspace (*str)) + ++str; + + /* PowerPC operands are just expressions. The only real issue is + that a few operand types are optional. All cases which might use + an optional operand separate the operands only with commas (in + some cases parentheses are used, as in ``lwz 1,0(1)'' but such + cases never have optional operands). There is never more than + one optional operand for an instruction. So, before we start + seriously parsing the operands, we check to see if we have an + optional operand, and, if we do, we count the number of commas to + see whether the operand should be omitted. */ + skip_optional = 0; + for (opindex_ptr = opcode->operands; *opindex_ptr != 0; opindex_ptr++) + { + const struct powerpc_operand *operand; + + operand = &powerpc_operands[*opindex_ptr]; + if ((operand->flags & PPC_OPERAND_OPTIONAL) != 0) + { + unsigned int opcount; + + /* There is an optional operand. Count the number of + commas in the input line. */ + if (*str == '\0') + opcount = 0; + else + { + opcount = 1; + s = str; while ((s = strchr (s, ',')) != (char *) NULL) { ++opcount; @@ -585,7 +1695,7 @@ md_assemble (str) { insn = (*operand->insert) (insn, 0L, &errmsg); if (errmsg != (const char *) NULL) - as_warn (errmsg); + as_bad (errmsg); continue; } @@ -598,7 +1708,7 @@ md_assemble (str) { insn = (*operand->insert) (insn, 0L, &errmsg); if (errmsg != (const char *) NULL) - as_warn (errmsg); + as_bad (errmsg); } if ((operand->flags & PPC_OPERAND_NEXT) != 0) next_opindex = *opindex_ptr + 1; @@ -608,24 +1718,226 @@ md_assemble (str) /* Gather the operand. */ hold = input_line_pointer; input_line_pointer = str; - expression (&ex); + +#ifdef TE_PE + if (*input_line_pointer == '[') + { + /* We are expecting something like the second argument here: + + lwz r4,[toc].GS.0.static_int(rtoc) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + The argument following the `]' must be a symbol name, and the + register must be the toc register: 'rtoc' or '2' + + The effect is to 0 as the displacement field + in the instruction, and issue an IMAGE_REL_PPC_TOCREL16 (or + the appropriate variation) reloc against it based on the symbol. + The linker will build the toc, and insert the resolved toc offset. + + Note: + o The size of the toc entry is currently assumed to be + 32 bits. This should not be assumed to be a hard coded + number. + o In an effort to cope with a change from 32 to 64 bits, + there are also toc entries that are specified to be + either 32 or 64 bits: + lwz r4,[toc32].GS.0.static_int(rtoc) + lwz r4,[toc64].GS.0.static_int(rtoc) + These demand toc entries of the specified size, and the + instruction probably requires it. + */ + + int valid_toc; + enum toc_size_qualifier toc_kind; + bfd_reloc_code_real_type toc_reloc; + + /* go parse off the [tocXX] part */ + valid_toc = parse_toc_entry(&toc_kind); + + if (!valid_toc) + { + /* Note: message has already been issued. */ + /* FIXME: what sort of recovery should we do? */ + /* demand_rest_of_line(); return; ? */ + } + + /* Now get the symbol following the ']' */ + expression(&ex); + + switch (toc_kind) + { + case default_toc: + /* In this case, we may not have seen the symbol yet, since */ + /* it is allowed to appear on a .extern or .globl or just be */ + /* a label in the .data section. */ + toc_reloc = BFD_RELOC_PPC_TOC16; + break; + case data_in_toc: + /* 1. The symbol must be defined and either in the toc */ + /* section, or a global. */ + /* 2. The reloc generated must have the TOCDEFN flag set in */ + /* upper bit mess of the reloc type. */ + /* FIXME: It's a little confusing what the tocv qualifier can */ + /* be used for. At the very least, I've seen three */ + /* uses, only one of which I'm sure I can explain. */ + if (ex.X_op == O_symbol) + { + assert (ex.X_add_symbol != NULL); + if (symbol_get_bfdsym (ex.X_add_symbol)->section + != tocdata_section) + { + as_bad(_("[tocv] symbol is not a toc symbol")); + } + } + + toc_reloc = BFD_RELOC_PPC_TOC16; + break; + case must_be_32: + /* FIXME: these next two specifically specify 32/64 bit toc */ + /* entries. We don't support them today. Is this the */ + /* right way to say that? */ + toc_reloc = BFD_RELOC_UNUSED; + as_bad (_("Unimplemented toc32 expression modifier")); + break; + case must_be_64: + /* FIXME: see above */ + toc_reloc = BFD_RELOC_UNUSED; + as_bad (_("Unimplemented toc64 expression modifier")); + break; + default: + fprintf(stderr, + _("Unexpected return value [%d] from parse_toc_entry!\n"), + toc_kind); + abort(); + break; + } + + /* We need to generate a fixup for this expression. */ + if (fc >= MAX_INSN_FIXUPS) + as_fatal (_("too many fixups")); + + fixups[fc].reloc = toc_reloc; + fixups[fc].exp = ex; + fixups[fc].opindex = *opindex_ptr; + ++fc; + + /* Ok. We've set up the fixup for the instruction. Now make it + look like the constant 0 was found here */ + ex.X_unsigned = 1; + ex.X_op = O_constant; + ex.X_add_number = 0; + ex.X_add_symbol = NULL; + ex.X_op_symbol = NULL; + } + + else +#endif /* TE_PE */ + { + if (! register_name (&ex)) + { + if ((operand->flags & PPC_OPERAND_CR) != 0) + cr_operand = true; + expression (&ex); + cr_operand = false; + } + } + str = input_line_pointer; input_line_pointer = hold; if (ex.X_op == O_illegal) - as_bad ("illegal operand"); + as_bad (_("illegal operand")); else if (ex.X_op == O_absent) - as_bad ("missing operand"); + as_bad (_("missing operand")); + else if (ex.X_op == O_register) + { + insn = ppc_insert_operand (insn, operand, ex.X_add_number, + (char *) NULL, 0); + } else if (ex.X_op == O_constant) - insn = ppc_insert_operand (insn, operand, ex.X_add_number, - (char *) NULL, 0); + { +#ifdef OBJ_ELF + /* Allow @HA, @L, @H on constants. */ + char *orig_str = str; + + if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED) + switch (reloc) + { + default: + str = orig_str; + break; + + case BFD_RELOC_LO16: + /* X_unsigned is the default, so if the user has done + something which cleared it, we always produce a + signed value. */ + if (ex.X_unsigned + && (operand->flags & PPC_OPERAND_SIGNED) == 0) + ex.X_add_number &= 0xffff; + else + ex.X_add_number = (((ex.X_add_number & 0xffff) + ^ 0x8000) + - 0x8000); + break; + + case BFD_RELOC_HI16: + ex.X_add_number = (ex.X_add_number >> 16) & 0xffff; + break; + + case BFD_RELOC_HI16_S: + ex.X_add_number = ((((ex.X_add_number >> 16) & 0xffff) + + ((ex.X_add_number >> 15) & 1)) + & 0xffff); + break; + } +#endif + insn = ppc_insert_operand (insn, operand, ex.X_add_number, + (char *) NULL, 0); + } +#ifdef OBJ_ELF + else if ((reloc = ppc_elf_suffix (&str, &ex)) != BFD_RELOC_UNUSED) + { + /* For the absoulte forms of branchs, convert the PC relative form back into + the absolute. */ + if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0) + { + switch (reloc) + { + case BFD_RELOC_PPC_B26: + reloc = BFD_RELOC_PPC_BA26; + break; + case BFD_RELOC_PPC_B16: + reloc = BFD_RELOC_PPC_BA16; + break; + case BFD_RELOC_PPC_B16_BRTAKEN: + reloc = BFD_RELOC_PPC_BA16_BRTAKEN; + break; + case BFD_RELOC_PPC_B16_BRNTAKEN: + reloc = BFD_RELOC_PPC_BA16_BRNTAKEN; + break; + default: + break; + } + } + + /* We need to generate a fixup for this expression. */ + if (fc >= MAX_INSN_FIXUPS) + as_fatal (_("too many fixups")); + fixups[fc].exp = ex; + fixups[fc].opindex = 0; + fixups[fc].reloc = reloc; + ++fc; + } +#endif /* OBJ_ELF */ + else { /* We need to generate a fixup for this expression. */ if (fc >= MAX_INSN_FIXUPS) - as_fatal ("too many fixups"); + as_fatal (_("too many fixups")); fixups[fc].exp = ex; fixups[fc].opindex = *opindex_ptr; + fixups[fc].reloc = BFD_RELOC_UNUSED; ++fc; } @@ -647,7 +1959,7 @@ md_assemble (str) if (*str != endc && (endc != ',' || *str != '\0')) { - as_bad ("syntax error; found `%c' but expected `%c'", *str, endc); + as_bad (_("syntax error; found `%c' but expected `%c'"), *str, endc); break; } @@ -659,27 +1971,64 @@ md_assemble (str) ++str; if (*str != '\0') - as_bad ("junk at end of line: `%s'", str); + as_bad (_("junk at end of line: `%s'"), str); /* Write out the instruction. */ f = frag_more (4); md_number_to_chars (f, insn, 4); /* Create any fixups. At this point we do not use a - bfd_reloc_code_real_type, but instead just use the operand index. - This lets us easily handle fixups for any operand type, although - that is admittedly not a very exciting feature. We pick a BFD - reloc type in md_apply_fix. */ + bfd_reloc_code_real_type, but instead just use the + BFD_RELOC_UNUSED plus the operand index. This lets us easily + handle fixups for any operand type, although that is admittedly + not a very exciting feature. We pick a BFD reloc type in + md_apply_fix. */ for (i = 0; i < fc; i++) { const struct powerpc_operand *operand; operand = &powerpc_operands[fixups[i].opindex]; - fix_new_exp (frag_now, f - frag_now->fr_literal, 4, - &fixups[i].exp, - (operand->flags & PPC_OPERAND_RELATIVE) != 0, - ((bfd_reloc_code_real_type) - (fixups[i].opindex + (int) BFD_RELOC_UNUSED))); + if (fixups[i].reloc != BFD_RELOC_UNUSED) + { + reloc_howto_type *reloc_howto = bfd_reloc_type_lookup (stdoutput, fixups[i].reloc); + int size; + int offset; + fixS *fixP; + + if (!reloc_howto) + abort (); + + size = bfd_get_reloc_size (reloc_howto); + offset = target_big_endian ? (4 - size) : 0; + + if (size < 1 || size > 4) + abort(); + + fixP = fix_new_exp (frag_now, f - frag_now->fr_literal + offset, size, + &fixups[i].exp, reloc_howto->pc_relative, + fixups[i].reloc); + + /* Turn off complaints that the addend is too large for things like + foo+100000@ha. */ + switch (fixups[i].reloc) + { + case BFD_RELOC_16_GOTOFF: + case BFD_RELOC_PPC_TOC16: + case BFD_RELOC_LO16: + case BFD_RELOC_HI16: + case BFD_RELOC_HI16_S: + fixP->fx_no_overflow = 1; + break; + default: + break; + } + } + else + fix_new_exp (frag_now, f - frag_now->fr_literal, 4, + &fixups[i].exp, + (operand->flags & PPC_OPERAND_RELATIVE) != 0, + ((bfd_reloc_code_real_type) + (fixups[i].opindex + (int) BFD_RELOC_UNUSED))); } } @@ -694,7 +2043,7 @@ ppc_macro (str, macro) const struct powerpc_macro *macro; { char *operands[10]; - int count; + unsigned int count; char *s; unsigned int len; const char *format; @@ -718,7 +2067,7 @@ ppc_macro (str, macro) if (count != macro->operands) { - as_bad ("wrong number of operands"); + as_bad (_("wrong number of operands")); return; } @@ -763,7 +2112,61 @@ ppc_macro (str, macro) md_assemble (complete); } -/* Pseudo-op handling. */ +#ifdef OBJ_ELF +/* For ELF, add support for SHF_EXCLUDE and SHT_ORDERED */ + +int +ppc_section_letter (letter, ptr_msg) + int letter; + char **ptr_msg; +{ + if (letter == 'e') + return SHF_EXCLUDE; + + *ptr_msg = _("Bad .section directive: want a,w,x,e in string"); + return 0; +} + +int +ppc_section_word (str, len) + char *str; + size_t len; +{ + if (len == 7 && strncmp (str, "exclude", 7) == 0) + return SHF_EXCLUDE; + + return -1; +} + +int +ppc_section_type (str, len) + char *str; + size_t len; +{ + if (len == 7 && strncmp (str, "ordered", 7) == 0) + return SHT_ORDERED; + + return -1; +} + +int +ppc_section_flags (flags, attr, type) + int flags; + int attr; + int type; +{ + if (type == SHT_ORDERED) + flags |= SEC_ALLOC | SEC_LOAD | SEC_SORT_ENTRIES; + + if (attr & SHF_EXCLUDE) + flags |= SEC_EXCLUDE; + + return flags; +} +#endif /* OBJ_ELF */ + + +/* Pseudo-op handling. */ /* The .byte pseudo-op. This is similar to the normal .byte pseudo-op, but it can also take a single ASCII string. */ @@ -800,10 +2203,14 @@ ppc_byte (ignore) demand_empty_rest_of_line (); } -#ifdef OBJ_COFF +#ifdef OBJ_XCOFF /* XCOFF specific pseudo-op handling. */ +/* This is set if we are creating a .stabx symbol, since we don't want + to handle symbol suffixes for such symbols. */ +static boolean ppc_stab_symbol; + /* The .comm and .lcomm pseudo-ops for XCOFF. XCOFF puts common symbols in the .bss segment as though they were local common symbols, and uses a different smclas. */ @@ -830,7 +2237,7 @@ ppc_comm (lcomm) if (*input_line_pointer != ',') { - as_bad ("missing size"); + as_bad (_("missing size")); ignore_rest_of_line (); return; } @@ -839,7 +2246,7 @@ ppc_comm (lcomm) size = get_absolute_expression (); if (size < 0) { - as_bad ("negative size"); + as_bad (_("negative size")); ignore_rest_of_line (); return; } @@ -855,7 +2262,7 @@ ppc_comm (lcomm) align = get_absolute_expression (); if (align <= 0) { - as_warn ("ignoring bad alignment"); + as_warn (_("ignoring bad alignment")); align = 3; } } @@ -880,7 +2287,7 @@ ppc_comm (lcomm) argument. */ if (*input_line_pointer != ',') { - as_bad ("missing real symbol name"); + as_bad (_("missing real symbol name")); ignore_rest_of_line (); return; } @@ -901,7 +2308,7 @@ ppc_comm (lcomm) if (S_IS_DEFINED (sym) || S_GET_VALUE (sym) != 0) { - as_bad ("attempt to redefine symbol"); + as_bad (_("attempt to redefine symbol")); ignore_rest_of_line (); return; } @@ -922,38 +2329,38 @@ ppc_comm (lcomm) } else { - lcomm_sym->sy_tc.output = 1; + symbol_get_tc (lcomm_sym)->output = 1; def_sym = lcomm_sym; def_size = 0; } subseg_set (bss_section, 1); - frag_align (align, 0); + frag_align (align, 0, 0); - def_sym->sy_frag = frag_now; + symbol_set_frag (def_sym, frag_now); pfrag = frag_var (rs_org, 1, 1, (relax_substateT) 0, def_sym, def_size, (char *) NULL); *pfrag = 0; S_SET_SEGMENT (def_sym, bss_section); - def_sym->sy_tc.align = align; + symbol_get_tc (def_sym)->align = align; } else if (lcomm) { /* Align the size of lcomm_sym. */ - lcomm_sym->sy_frag->fr_offset = - ((lcomm_sym->sy_frag->fr_offset + (1 << align) - 1) + symbol_get_frag (lcomm_sym)->fr_offset = + ((symbol_get_frag (lcomm_sym)->fr_offset + (1 << align) - 1) &~ ((1 << align) - 1)); - if (align > lcomm_sym->sy_tc.align) - lcomm_sym->sy_tc.align = align; + if (align > symbol_get_tc (lcomm_sym)->align) + symbol_get_tc (lcomm_sym)->align = align; } if (lcomm) { /* Make sym an offset from lcomm_sym. */ S_SET_SEGMENT (sym, bss_section); - sym->sy_frag = lcomm_sym->sy_frag; - S_SET_VALUE (sym, lcomm_sym->sy_frag->fr_offset); - lcomm_sym->sy_frag->fr_offset += size; + symbol_set_frag (sym, symbol_get_frag (lcomm_sym)); + S_SET_VALUE (sym, symbol_get_frag (lcomm_sym)->fr_offset); + symbol_get_frag (lcomm_sym)->fr_offset += size; } subseg_set (current_seg, current_subseg); @@ -982,19 +2389,43 @@ ppc_csect (ignore) *input_line_pointer = endc; + if (S_GET_NAME (sym)[0] == '\0') + { + /* An unnamed csect is assumed to be [PR]. */ + symbol_get_tc (sym)->class = XMC_PR; + } + + ppc_change_csect (sym); + + if (*input_line_pointer == ',') + { + ++input_line_pointer; + symbol_get_tc (sym)->align = get_absolute_expression (); + } + + demand_empty_rest_of_line (); +} + +/* Change to a different csect. */ + +static void +ppc_change_csect (sym) + symbolS *sym; +{ if (S_IS_DEFINED (sym)) - subseg_set (S_GET_SEGMENT (sym), sym->sy_tc.subseg); + subseg_set (S_GET_SEGMENT (sym), symbol_get_tc (sym)->subseg); else { symbolS **list_ptr; int after_toc; + int hold_chunksize; symbolS *list; /* This is a new csect. We need to look at the symbol class to figure out whether it should go in the text section or the data section. */ after_toc = 0; - switch (sym->sy_tc.class) + switch (symbol_get_tc (sym)->class) { case XMC_PR: case XMC_RO: @@ -1005,7 +2436,7 @@ ppc_csect (ignore) case XMC_TI: case XMC_TB: S_SET_SEGMENT (sym, text_section); - sym->sy_tc.subseg = ppc_text_subsegment; + symbol_get_tc (sym)->subseg = ppc_text_subsegment; ++ppc_text_subsegment; list_ptr = &ppc_text_csects; break; @@ -1016,10 +2447,12 @@ ppc_csect (ignore) case XMC_UA: case XMC_BS: case XMC_UC: - if (ppc_toc_csect->sy_tc.subseg + 1 == ppc_data_subsegment) + if (ppc_toc_csect != NULL + && (symbol_get_tc (ppc_toc_csect)->subseg + 1 + == ppc_data_subsegment)) after_toc = 1; S_SET_SEGMENT (sym, data_section); - sym->sy_tc.subseg = ppc_data_subsegment; + symbol_get_tc (sym)->subseg = ppc_data_subsegment; ++ppc_data_subsegment; list_ptr = &ppc_data_csects; break; @@ -1027,34 +2460,99 @@ ppc_csect (ignore) abort (); } - subseg_new (segment_name (S_GET_SEGMENT (sym)), sym->sy_tc.subseg); + /* We set the obstack chunk size to a small value before + changing subsegments, so that we don't use a lot of memory + space for what may be a small section. */ + hold_chunksize = chunksize; + chunksize = 64; + + subseg_new (segment_name (S_GET_SEGMENT (sym)), + symbol_get_tc (sym)->subseg); + + chunksize = hold_chunksize; + if (after_toc) ppc_after_toc_frag = frag_now; - sym->sy_frag = frag_now; + symbol_set_frag (sym, frag_now); S_SET_VALUE (sym, (valueT) frag_now_fix ()); - sym->sy_tc.align = 2; - sym->sy_tc.output = 1; - sym->sy_tc.within = sym; + symbol_get_tc (sym)->align = 2; + symbol_get_tc (sym)->output = 1; + symbol_get_tc (sym)->within = sym; for (list = *list_ptr; - list->sy_tc.next != (symbolS *) NULL; - list = list->sy_tc.next) + symbol_get_tc (list)->next != (symbolS *) NULL; + list = symbol_get_tc (list)->next) ; - list->sy_tc.next = sym; + symbol_get_tc (list)->next = sym; symbol_remove (sym, &symbol_rootP, &symbol_lastP); - symbol_append (sym, list->sy_tc.within, &symbol_rootP, &symbol_lastP); + symbol_append (sym, symbol_get_tc (list)->within, &symbol_rootP, + &symbol_lastP); } - if (*input_line_pointer == ',') + ppc_current_csect = sym; +} + +/* This function handles the .text and .data pseudo-ops. These + pseudo-ops aren't really used by XCOFF; we implement them for the + convenience of people who aren't used to XCOFF. */ + +static void +ppc_section (type) + int type; +{ + const char *name; + symbolS *sym; + + if (type == 't') + name = ".text[PR]"; + else if (type == 'd') + name = ".data[RW]"; + else + abort (); + + sym = symbol_find_or_make (name); + + ppc_change_csect (sym); + + demand_empty_rest_of_line (); +} + +/* This function handles the .section pseudo-op. This is mostly to + give an error, since XCOFF only supports .text, .data and .bss, but + we do permit the user to name the text or data section. */ + +static void +ppc_named_section (ignore) + int ignore; +{ + char *user_name; + const char *real_name; + char c; + symbolS *sym; + + user_name = input_line_pointer; + c = get_symbol_end (); + + if (strcmp (user_name, ".text") == 0) + real_name = ".text[PR]"; + else if (strcmp (user_name, ".data") == 0) + real_name = ".data[RW]"; + else { - ++input_line_pointer; - sym->sy_tc.align = get_absolute_expression (); + as_bad (_("The XCOFF file format does not support arbitrary sections")); + *input_line_pointer = c; + ignore_rest_of_line (); + return; } - ppc_current_csect = sym; + *input_line_pointer = c; + + sym = symbol_find_or_make (real_name); + + ppc_change_csect (sym); demand_empty_rest_of_line (); } @@ -1078,15 +2576,26 @@ ppc_extern (ignore) demand_empty_rest_of_line (); } -/* The .lglobl pseudo-op. I think the RS/6000 assembler only needs - this because it can't handle undefined symbols. I think we can - just ignore it. */ +/* The .lglobl pseudo-op. Keep the symbol in the symbol table. */ static void ppc_lglobl (ignore) int ignore; { - s_ignore (0); + char *name; + char endc; + symbolS *sym; + + name = input_line_pointer; + endc = get_symbol_end (); + + sym = symbol_find_or_make (name); + + *input_line_pointer = endc; + + symbol_get_tc (sym)->output = 1; + + demand_empty_rest_of_line (); } /* The .rename pseudo-op. The RS/6000 assembler can rename symbols, @@ -1110,13 +2619,13 @@ ppc_rename (ignore) if (*input_line_pointer != ',') { - as_bad ("missing rename string"); + as_bad (_("missing rename string")); ignore_rest_of_line (); return; } ++input_line_pointer; - sym->sy_tc.real_name = demand_copy_C_string (&len); + symbol_get_tc (sym)->real_name = demand_copy_C_string (&len); demand_empty_rest_of_line (); } @@ -1141,12 +2650,16 @@ ppc_stabx (ignore) if (*input_line_pointer != ',') { - as_bad ("missing value"); + as_bad (_("missing value")); return; } ++input_line_pointer; + ppc_stab_symbol = true; sym = symbol_make (name); + ppc_stab_symbol = false; + + symbol_get_tc (sym)->real_name = name; (void) expression (&exp); @@ -1155,22 +2668,22 @@ ppc_stabx (ignore) case O_illegal: case O_absent: case O_big: - as_bad ("illegal .stabx expression; zero assumed"); + as_bad (_("illegal .stabx expression; zero assumed")); exp.X_add_number = 0; /* Fall through. */ case O_constant: S_SET_VALUE (sym, (valueT) exp.X_add_number); - sym->sy_frag = &zero_address_frag; + symbol_set_frag (sym, &zero_address_frag); break; case O_symbol: if (S_GET_SEGMENT (exp.X_add_symbol) == undefined_section) - sym->sy_value = exp; + symbol_set_value_expression (sym, &exp); else { S_SET_VALUE (sym, exp.X_add_number + S_GET_VALUE (exp.X_add_symbol)); - sym->sy_frag = exp.X_add_symbol->sy_frag; + symbol_set_frag (sym, symbol_get_frag (exp.X_add_symbol)); } break; @@ -1178,16 +2691,16 @@ ppc_stabx (ignore) /* The value is some complex expression. This will probably fail at some later point, but this is probably the right thing to do here. */ - sym->sy_value = exp; + symbol_set_value_expression (sym, &exp); break; } S_SET_SEGMENT (sym, ppc_coff_debug_section); - sym->bsym->flags |= BSF_DEBUGGING; + symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING; if (*input_line_pointer != ',') { - as_bad ("missing class"); + as_bad (_("missing class")); return; } ++input_line_pointer; @@ -1196,17 +2709,17 @@ ppc_stabx (ignore) if (*input_line_pointer != ',') { - as_bad ("missing type"); + as_bad (_("missing type")); return; } ++input_line_pointer; S_SET_DATA_TYPE (sym, get_absolute_expression ()); - sym->sy_tc.output = 1; + symbol_get_tc (sym)->output = 1; if (S_GET_STORAGE_CLASS (sym) == C_STSYM) - sym->sy_tc.within = ppc_current_block; + symbol_get_tc (sym)->within = ppc_current_block; if (exp.X_op != O_symbol || ! S_IS_EXTERNAL (exp.X_add_symbol) @@ -1216,15 +2729,8 @@ ppc_stabx (ignore) { symbol_remove (sym, &symbol_rootP, &symbol_lastP); symbol_append (sym, exp.X_add_symbol, &symbol_rootP, &symbol_lastP); - if (ppc_current_csect->sy_tc.within == exp.X_add_symbol) - ppc_current_csect->sy_tc.within = sym; - } - - if (strlen (name) > SYMNMLEN) - { - /* For some reason, each name is preceded by a two byte length - and followed by a null byte. */ - ppc_debug_name_section_size += strlen (name) + 3; + if (symbol_get_tc (ppc_current_csect)->within == exp.X_add_symbol) + symbol_get_tc (ppc_current_csect)->within = sym; } demand_empty_rest_of_line (); @@ -1264,7 +2770,7 @@ ppc_function (ignore) if (*input_line_pointer != ',') { - as_bad ("missing symbol name"); + as_bad (_("missing symbol name")); ignore_rest_of_line (); return; } @@ -1279,15 +2785,19 @@ ppc_function (ignore) if (ext_sym != lab_sym) { - ext_sym->sy_value.X_op = O_symbol; - ext_sym->sy_value.X_add_symbol = lab_sym; - ext_sym->sy_value.X_op_symbol = NULL; - ext_sym->sy_value.X_add_number = 0; + expressionS exp; + + exp.X_op = O_symbol; + exp.X_add_symbol = lab_sym; + exp.X_op_symbol = NULL; + exp.X_add_number = 0; + exp.X_unsigned = 0; + symbol_set_value_expression (ext_sym, &exp); } - if (ext_sym->sy_tc.class == -1) - ext_sym->sy_tc.class = XMC_PR; - ext_sym->sy_tc.output = 1; + if (symbol_get_tc (ext_sym)->class == -1) + symbol_get_tc (ext_sym)->class = XMC_PR; + symbol_get_tc (ext_sym)->output = 1; if (*input_line_pointer == ',') { @@ -1305,11 +2815,11 @@ ppc_function (ignore) { /* The fifth argument is the function size. */ ++input_line_pointer; - ext_sym->sy_tc.size = symbol_new ("L0\001", - absolute_section, - (valueT) 0, - &zero_address_frag); - pseudo_set (ext_sym->sy_tc.size); + symbol_get_tc (ext_sym)->size = symbol_new ("L0\001", + absolute_section, + (valueT) 0, + &zero_address_frag); + pseudo_set (symbol_get_tc (ext_sym)->size); } } } @@ -1333,7 +2843,7 @@ ppc_bf (ignore) sym = symbol_make (".bf"); S_SET_SEGMENT (sym, text_section); - sym->sy_frag = frag_now; + symbol_set_frag (sym, frag_now); S_SET_VALUE (sym, frag_now_fix ()); S_SET_STORAGE_CLASS (sym, C_FCN); @@ -1342,7 +2852,7 @@ ppc_bf (ignore) S_SET_NUMBER_AUXILIARY (sym, 1); SA_SET_SYM_LNNO (sym, coff_line_base); - sym->sy_tc.output = 1; + symbol_get_tc (sym)->output = 1; ppc_frob_label (sym); @@ -1361,12 +2871,12 @@ ppc_ef (ignore) sym = symbol_make (".ef"); S_SET_SEGMENT (sym, text_section); - sym->sy_frag = frag_now; + symbol_set_frag (sym, frag_now); S_SET_VALUE (sym, frag_now_fix ()); S_SET_STORAGE_CLASS (sym, C_FCN); S_SET_NUMBER_AUXILIARY (sym, 1); SA_SET_SYM_LNNO (sym, get_absolute_expression ()); - sym->sy_tc.output = 1; + symbol_get_tc (sym)->output = 1; ppc_frob_label (sym); @@ -1381,6 +2891,8 @@ static void ppc_biei (ei) int ei; { + static symbolS *last_biei; + char *name; int len; symbolS *sym; @@ -1388,23 +2900,22 @@ ppc_biei (ei) name = demand_copy_C_string (&len); - sym = symbol_make (name); - S_SET_SEGMENT (sym, ppc_coff_debug_section); - sym->bsym->flags |= BSF_DEBUGGING; + /* The value of these symbols is actually file offset. Here we set + the value to the index into the line number entries. In + ppc_frob_symbols we set the fix_line field, which will cause BFD + to do the right thing. */ - /* FIXME: The value of the .bi or .ei symbol is supposed to be the - offset in the file to the line number entry to use. That is - quite difficult to implement using BFD, so I'm just not doing it. - Sorry. Please add it if you can figure out how. Note that this - approach is the only way to support multiple files in COFF, since - line numbers are associated with function symbols. Note further - that it still doesn't work, since the line numbers are stored as - offsets from a base line number. */ + sym = symbol_make (name); + /* obj-coff.c currently only handles line numbers correctly in the + .text section. */ + S_SET_SEGMENT (sym, text_section); + S_SET_VALUE (sym, coff_n_line_nos); + symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING; S_SET_STORAGE_CLASS (sym, ei ? C_EINCL : C_BINCL); - sym->sy_tc.output = 1; + symbol_get_tc (sym)->output = 1; - for (look = symbol_rootP; + for (look = last_biei ? last_biei : symbol_rootP; (look != (symbolS *) NULL && (S_GET_STORAGE_CLASS (look) == C_FILE || S_GET_STORAGE_CLASS (look) == C_BINCL @@ -1415,6 +2926,7 @@ ppc_biei (ei) { symbol_remove (sym, &symbol_rootP, &symbol_lastP); symbol_insert (sym, look, &symbol_rootP, &symbol_lastP); + last_biei = sym; } demand_empty_rest_of_line (); @@ -1434,7 +2946,7 @@ ppc_bs (ignore) symbolS *sym; if (ppc_current_block != NULL) - as_bad ("nested .bs blocks"); + as_bad (_("nested .bs blocks")); name = input_line_pointer; endc = get_symbol_end (); @@ -1446,10 +2958,10 @@ ppc_bs (ignore) sym = symbol_make (".bs"); S_SET_SEGMENT (sym, now_seg); S_SET_STORAGE_CLASS (sym, C_BSTAT); - sym->bsym->flags |= BSF_DEBUGGING; - sym->sy_tc.output = 1; + symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING; + symbol_get_tc (sym)->output = 1; - sym->sy_tc.within = csect; + symbol_get_tc (sym)->within = csect; ppc_frob_label (sym); @@ -1467,13 +2979,13 @@ ppc_es (ignore) symbolS *sym; if (ppc_current_block == NULL) - as_bad (".es without preceding .bs"); + as_bad (_(".es without preceding .bs")); sym = symbol_make (".es"); S_SET_SEGMENT (sym, now_seg); S_SET_STORAGE_CLASS (sym, C_ESTAT); - sym->bsym->flags |= BSF_DEBUGGING; - sym->sy_tc.output = 1; + symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING; + symbol_get_tc (sym)->output = 1; ppc_frob_label (sym); @@ -1493,14 +3005,16 @@ ppc_bb (ignore) sym = symbol_make (".bb"); S_SET_SEGMENT (sym, text_section); - sym->sy_frag = frag_now; + symbol_set_frag (sym, frag_now); S_SET_VALUE (sym, frag_now_fix ()); S_SET_STORAGE_CLASS (sym, C_BLOCK); S_SET_NUMBER_AUXILIARY (sym, 1); SA_SET_SYM_LNNO (sym, get_absolute_expression ()); - sym->sy_tc.output = 1; + symbol_get_tc (sym)->output = 1; + + SF_SET_PROCESS (sym); ppc_frob_label (sym); @@ -1518,12 +3032,58 @@ ppc_eb (ignore) sym = symbol_make (".eb"); S_SET_SEGMENT (sym, text_section); - sym->sy_frag = frag_now; + symbol_set_frag (sym, frag_now); S_SET_VALUE (sym, frag_now_fix ()); - S_SET_STORAGE_CLASS (sym, C_FCN); + S_SET_STORAGE_CLASS (sym, C_BLOCK); S_SET_NUMBER_AUXILIARY (sym, 1); SA_SET_SYM_LNNO (sym, get_absolute_expression ()); - sym->sy_tc.output = 1; + symbol_get_tc (sym)->output = 1; + + SF_SET_PROCESS (sym); + + ppc_frob_label (sym); + + demand_empty_rest_of_line (); +} + +/* The .bc pseudo-op. This just creates a C_BCOMM symbol with a + specified name. */ + +static void +ppc_bc (ignore) + int ignore; +{ + char *name; + int len; + symbolS *sym; + + name = demand_copy_C_string (&len); + sym = symbol_make (name); + S_SET_SEGMENT (sym, ppc_coff_debug_section); + symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING; + S_SET_STORAGE_CLASS (sym, C_BCOMM); + S_SET_VALUE (sym, 0); + symbol_get_tc (sym)->output = 1; + + ppc_frob_label (sym); + + demand_empty_rest_of_line (); +} + +/* The .ec pseudo-op. This just creates a C_ECOMM symbol. */ + +static void +ppc_ec (ignore) + int ignore; +{ + symbolS *sym; + + sym = symbol_make (".ec"); + S_SET_SEGMENT (sym, ppc_coff_debug_section); + symbol_get_bfdsym (sym)->flags |= BSF_DEBUGGING; + S_SET_STORAGE_CLASS (sym, C_ECOMM); + S_SET_VALUE (sym, 0); + symbol_get_tc (sym)->output = 1; ppc_frob_label (sym); @@ -1537,7 +3097,7 @@ ppc_toc (ignore) int ignore; { if (ppc_toc_csect != (symbolS *) NULL) - subseg_set (data_section, ppc_toc_csect->sy_tc.subseg); + subseg_set (data_section, symbol_get_tc (ppc_toc_csect)->subseg); else { subsegT subseg; @@ -1551,128 +3111,768 @@ ppc_toc (ignore) ppc_toc_frag = frag_now; sym = symbol_find_or_make ("TOC[TC0]"); - sym->sy_frag = frag_now; + symbol_set_frag (sym, frag_now); S_SET_SEGMENT (sym, data_section); S_SET_VALUE (sym, (valueT) frag_now_fix ()); - sym->sy_tc.subseg = subseg; - sym->sy_tc.output = 1; - sym->sy_tc.within = sym; + symbol_get_tc (sym)->subseg = subseg; + symbol_get_tc (sym)->output = 1; + symbol_get_tc (sym)->within = sym; ppc_toc_csect = sym; for (list = ppc_data_csects; - list->sy_tc.next != (symbolS *) NULL; - list = list->sy_tc.next) + symbol_get_tc (list)->next != (symbolS *) NULL; + list = symbol_get_tc (list)->next) ; - list->sy_tc.next = sym; + symbol_get_tc (list)->next = sym; symbol_remove (sym, &symbol_rootP, &symbol_lastP); - symbol_append (sym, list->sy_tc.within, &symbol_rootP, &symbol_lastP); + symbol_append (sym, symbol_get_tc (list)->within, &symbol_rootP, + &symbol_lastP); + } + + ppc_current_csect = ppc_toc_csect; + + demand_empty_rest_of_line (); +} + +/* The AIX assembler automatically aligns the operands of a .long or + .short pseudo-op, and we want to be compatible. */ + +static void +ppc_xcoff_cons (log_size) + int log_size; +{ + frag_align (log_size, 0, 0); + record_alignment (now_seg, log_size); + cons (1 << log_size); +} + +static void +ppc_vbyte (dummy) + int dummy; +{ + expressionS exp; + int byte_count; + + (void) expression (&exp); + + if (exp.X_op != O_constant) + { + as_bad (_("non-constant byte count")); + return; + } + + byte_count = exp.X_add_number; + + if (*input_line_pointer != ',') + { + as_bad (_("missing value")); + return; + } + + ++input_line_pointer; + cons (byte_count); +} + +#endif /* OBJ_XCOFF */ + +/* The .tc pseudo-op. This is used when generating either XCOFF or + ELF. This takes two or more arguments. + + When generating XCOFF output, the first argument is the name to + give to this location in the toc; this will be a symbol with class + TC. The rest of the arguments are 4 byte values to actually put at + this location in the TOC; often there is just one more argument, a + relocateable symbol reference. + + When not generating XCOFF output, the arguments are the same, but + the first argument is simply ignored. */ + +static void +ppc_tc (ignore) + int ignore; +{ +#ifdef OBJ_XCOFF + + /* Define the TOC symbol name. */ + { + char *name; + char endc; + symbolS *sym; + + if (ppc_toc_csect == (symbolS *) NULL + || ppc_toc_csect != ppc_current_csect) + { + as_bad (_(".tc not in .toc section")); + ignore_rest_of_line (); + return; + } + + name = input_line_pointer; + endc = get_symbol_end (); + + sym = symbol_find_or_make (name); + + *input_line_pointer = endc; + + if (S_IS_DEFINED (sym)) + { + symbolS *label; + + label = symbol_get_tc (ppc_current_csect)->within; + if (symbol_get_tc (label)->class != XMC_TC0) + { + as_bad (_(".tc with no label")); + ignore_rest_of_line (); + return; + } + + S_SET_SEGMENT (label, S_GET_SEGMENT (sym)); + symbol_set_frag (label, symbol_get_frag (sym)); + S_SET_VALUE (label, S_GET_VALUE (sym)); + + while (! is_end_of_line[(unsigned char) *input_line_pointer]) + ++input_line_pointer; + + return; + } + + S_SET_SEGMENT (sym, now_seg); + symbol_set_frag (sym, frag_now); + S_SET_VALUE (sym, (valueT) frag_now_fix ()); + symbol_get_tc (sym)->class = XMC_TC; + symbol_get_tc (sym)->output = 1; + + ppc_frob_label (sym); + } + +#else /* ! defined (OBJ_XCOFF) */ + + /* Skip the TOC symbol name. */ + while (is_part_of_name (*input_line_pointer) + || *input_line_pointer == '[' + || *input_line_pointer == ']' + || *input_line_pointer == '{' + || *input_line_pointer == '}') + ++input_line_pointer; + + /* Align to a four byte boundary. */ + frag_align (2, 0, 0); + record_alignment (now_seg, 2); + +#endif /* ! defined (OBJ_XCOFF) */ + + if (*input_line_pointer != ',') + demand_empty_rest_of_line (); + else + { + ++input_line_pointer; + cons (4); + } +} + +#ifdef TE_PE + +/* Pseudo-ops specific to the Windows NT PowerPC PE (coff) format */ + +/* Set the current section. */ +static void +ppc_set_current_section (new) + segT new; +{ + ppc_previous_section = ppc_current_section; + ppc_current_section = new; +} + +/* pseudo-op: .previous + behaviour: toggles the current section with the previous section. + errors: None + warnings: "No previous section" +*/ +static void +ppc_previous(ignore) + int ignore; +{ + symbolS *tmp; + + if (ppc_previous_section == NULL) + { + as_warn(_("No previous section to return to. Directive ignored.")); + return; + } + + subseg_set(ppc_previous_section, 0); + + ppc_set_current_section(ppc_previous_section); +} + +/* pseudo-op: .pdata + behaviour: predefined read only data section + double word aligned + errors: None + warnings: None + initial: .section .pdata "adr3" + a - don't know -- maybe a misprint + d - initialized data + r - readable + 3 - double word aligned (that would be 4 byte boundary) + + commentary: + Tag index tables (also known as the function table) for exception + handling, debugging, etc. + +*/ +static void +ppc_pdata(ignore) + int ignore; +{ + if (pdata_section == 0) + { + pdata_section = subseg_new (".pdata", 0); + + bfd_set_section_flags (stdoutput, pdata_section, + (SEC_ALLOC | SEC_LOAD | SEC_RELOC + | SEC_READONLY | SEC_DATA )); + + bfd_set_section_alignment (stdoutput, pdata_section, 2); + } + else + { + pdata_section = subseg_new(".pdata", 0); + } + ppc_set_current_section(pdata_section); +} + +/* pseudo-op: .ydata + behaviour: predefined read only data section + double word aligned + errors: None + warnings: None + initial: .section .ydata "drw3" + a - don't know -- maybe a misprint + d - initialized data + r - readable + 3 - double word aligned (that would be 4 byte boundary) + commentary: + Tag tables (also known as the scope table) for exception handling, + debugging, etc. +*/ +static void +ppc_ydata(ignore) + int ignore; +{ + if (ydata_section == 0) + { + ydata_section = subseg_new (".ydata", 0); + bfd_set_section_flags (stdoutput, ydata_section, + (SEC_ALLOC | SEC_LOAD | SEC_RELOC + | SEC_READONLY | SEC_DATA )); + + bfd_set_section_alignment (stdoutput, ydata_section, 3); + } + else + { + ydata_section = subseg_new (".ydata", 0); + } + ppc_set_current_section(ydata_section); +} + +/* pseudo-op: .reldata + behaviour: predefined read write data section + double word aligned (4-byte) + FIXME: relocation is applied to it + FIXME: what's the difference between this and .data? + errors: None + warnings: None + initial: .section .reldata "drw3" + d - initialized data + r - readable + w - writeable + 3 - double word aligned (that would be 8 byte boundary) + + commentary: + Like .data, but intended to hold data subject to relocation, such as + function descriptors, etc. +*/ +static void +ppc_reldata(ignore) + int ignore; +{ + if (reldata_section == 0) + { + reldata_section = subseg_new (".reldata", 0); + + bfd_set_section_flags (stdoutput, reldata_section, + ( SEC_ALLOC | SEC_LOAD | SEC_RELOC + | SEC_DATA )); + + bfd_set_section_alignment (stdoutput, reldata_section, 2); + } + else + { + reldata_section = subseg_new (".reldata", 0); + } + ppc_set_current_section(reldata_section); +} + +/* pseudo-op: .rdata + behaviour: predefined read only data section + double word aligned + errors: None + warnings: None + initial: .section .rdata "dr3" + d - initialized data + r - readable + 3 - double word aligned (that would be 4 byte boundary) +*/ +static void +ppc_rdata(ignore) + int ignore; +{ + if (rdata_section == 0) + { + rdata_section = subseg_new (".rdata", 0); + bfd_set_section_flags (stdoutput, rdata_section, + (SEC_ALLOC | SEC_LOAD | SEC_RELOC + | SEC_READONLY | SEC_DATA )); + + bfd_set_section_alignment (stdoutput, rdata_section, 2); + } + else + { + rdata_section = subseg_new (".rdata", 0); + } + ppc_set_current_section(rdata_section); +} + +/* pseudo-op: .ualong + behaviour: much like .int, with the exception that no alignment is + performed. + FIXME: test the alignment statement + errors: None + warnings: None +*/ +static void +ppc_ualong(ignore) + int ignore; +{ + /* try for long */ + cons ( 4 ); +} + +/* pseudo-op: .znop + behaviour: Issue a nop instruction + Issue a IMAGE_REL_PPC_IFGLUE relocation against it, using + the supplied symbol name. + errors: None + warnings: Missing symbol name +*/ +static void +ppc_znop(ignore) + int ignore; +{ + unsigned long insn; + const struct powerpc_opcode *opcode; + expressionS ex; + char *f; + + symbolS *sym; + + /* Strip out the symbol name */ + char *symbol_name; + char c; + char *name; + unsigned int exp; + flagword flags; + asection *sec; + + symbol_name = input_line_pointer; + c = get_symbol_end (); + + name = xmalloc (input_line_pointer - symbol_name + 1); + strcpy (name, symbol_name); + + sym = symbol_find_or_make (name); + + *input_line_pointer = c; + + SKIP_WHITESPACE (); + + /* Look up the opcode in the hash table. */ + opcode = (const struct powerpc_opcode *) hash_find (ppc_hash, "nop"); + + /* stick in the nop */ + insn = opcode->opcode; + + /* Write out the instruction. */ + f = frag_more (4); + md_number_to_chars (f, insn, 4); + fix_new (frag_now, + f - frag_now->fr_literal, + 4, + sym, + 0, + 0, + BFD_RELOC_16_GOT_PCREL); + +} + +/* pseudo-op: + behaviour: + errors: + warnings: +*/ +static void +ppc_pe_comm(lcomm) + int lcomm; +{ + register char *name; + register char c; + register char *p; + offsetT temp; + register symbolS *symbolP; + offsetT align; + + name = input_line_pointer; + c = get_symbol_end (); + + /* just after name is now '\0' */ + p = input_line_pointer; + *p = c; + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + { + as_bad (_("Expected comma after symbol-name: rest of line ignored.")); + ignore_rest_of_line (); + return; + } + + input_line_pointer++; /* skip ',' */ + if ((temp = get_absolute_expression ()) < 0) + { + as_warn (_(".COMMon length (%ld.) <0! Ignored."), (long) temp); + ignore_rest_of_line (); + return; + } + + if (! lcomm) + { + /* The third argument to .comm is the alignment. */ + if (*input_line_pointer != ',') + align = 3; + else + { + ++input_line_pointer; + align = get_absolute_expression (); + if (align <= 0) + { + as_warn (_("ignoring bad alignment")); + align = 3; + } + } + } + + *p = 0; + symbolP = symbol_find_or_make (name); + + *p = c; + if (S_IS_DEFINED (symbolP) && ! S_IS_COMMON (symbolP)) + { + as_bad (_("Ignoring attempt to re-define symbol `%s'."), + S_GET_NAME (symbolP)); + ignore_rest_of_line (); + return; } - ppc_current_csect = ppc_toc_csect; + if (S_GET_VALUE (symbolP)) + { + if (S_GET_VALUE (symbolP) != (valueT) temp) + as_bad (_("Length of .comm \"%s\" is already %ld. Not changed to %ld."), + S_GET_NAME (symbolP), + (long) S_GET_VALUE (symbolP), + (long) temp); + } + else + { + S_SET_VALUE (symbolP, (valueT) temp); + S_SET_EXTERNAL (symbolP); + } demand_empty_rest_of_line (); } -#endif /* OBJ_COFF */ - -/* The .tc pseudo-op. This is used when generating either XCOFF or - ELF. This takes two or more arguments. - - When generating XCOFF output, the first argument is the name to - give to this location in the toc; this will be a symbol with class - TC. The rest of the arguments are 4 byte values to actually put at - this location in the TOC; often there is just one more argument, a - relocateable symbol reference. - - When not generating XCOFF output, the arguments are the same, but - the first argument is simply ignored. */ +/* + * implement the .section pseudo op: + * .section name {, "flags"} + * ^ ^ + * | +--- optional flags: 'b' for bss + * | 'i' for info + * +-- section name 'l' for lib + * 'n' for noload + * 'o' for over + * 'w' for data + * 'd' (apparently m88k for data) + * 'x' for text + * But if the argument is not a quoted string, treat it as a + * subsegment number. + * + * FIXME: this is a copy of the section processing from obj-coff.c, with + * additions/changes for the moto-pas assembler support. There are three + * categories: + * + * FIXME: I just noticed this. This doesn't work at all really. It it + * setting bits that bfd probably neither understands or uses. The + * correct approach (?) will have to incorporate extra fields attached + * to the section to hold the system specific stuff. (krk) + * + * Section Contents: + * 'a' - unknown - referred to in documentation, but no definition supplied + * 'c' - section has code + * 'd' - section has initialized data + * 'u' - section has uninitialized data + * 'i' - section contains directives (info) + * 'n' - section can be discarded + * 'R' - remove section at link time + * + * Section Protection: + * 'r' - section is readable + * 'w' - section is writeable + * 'x' - section is executable + * 's' - section is sharable + * + * Section Alignment: + * '0' - align to byte boundary + * '1' - align to halfword undary + * '2' - align to word boundary + * '3' - align to doubleword boundary + * '4' - align to quadword boundary + * '5' - align to 32 byte boundary + * '6' - align to 64 byte boundary + * + */ -static void -ppc_tc (ignore) +void +ppc_pe_section (ignore) int ignore; { -#ifdef OBJ_COFF + /* Strip out the section name */ + char *section_name; + char c; + char *name; + unsigned int exp; + flagword flags; + segT sec; + int align; - /* Define the TOC symbol name. */ - { - char *name; - char endc; - symbolS *sym; + section_name = input_line_pointer; + c = get_symbol_end (); - if (ppc_toc_csect == (symbolS *) NULL - || ppc_toc_csect != ppc_current_csect) - { - as_bad (".tc not in .toc section"); - ignore_rest_of_line (); - return; - } + name = xmalloc (input_line_pointer - section_name + 1); + strcpy (name, section_name); - name = input_line_pointer; - endc = get_symbol_end (); + *input_line_pointer = c; - sym = symbol_find_or_make (name); + SKIP_WHITESPACE (); - *input_line_pointer = endc; + exp = 0; + flags = SEC_NO_FLAGS; - if (S_IS_DEFINED (sym)) - { - symbolS *label; + if (strcmp (name, ".idata$2") == 0) + { + align = 0; + } + else if (strcmp (name, ".idata$3") == 0) + { + align = 0; + } + else if (strcmp (name, ".idata$4") == 0) + { + align = 2; + } + else if (strcmp (name, ".idata$5") == 0) + { + align = 2; + } + else if (strcmp (name, ".idata$6") == 0) + { + align = 1; + } + else + align = 4; /* default alignment to 16 byte boundary */ - label = ppc_current_csect->sy_tc.within; - if (label->sy_tc.class != XMC_TC0) - { - as_warn (".tc with no label"); - ignore_rest_of_line (); - return; - } + if (*input_line_pointer == ',') + { + ++input_line_pointer; + SKIP_WHITESPACE (); + if (*input_line_pointer != '"') + exp = get_absolute_expression (); + else + { + ++input_line_pointer; + while (*input_line_pointer != '"' + && ! is_end_of_line[(unsigned char) *input_line_pointer]) + { + switch (*input_line_pointer) + { + /* Section Contents */ + case 'a': /* unknown */ + as_bad (_("Unsupported section attribute -- 'a'")); + break; + case 'c': /* code section */ + flags |= SEC_CODE; + break; + case 'd': /* section has initialized data */ + flags |= SEC_DATA; + break; + case 'u': /* section has uninitialized data */ + /* FIXME: This is IMAGE_SCN_CNT_UNINITIALIZED_DATA + in winnt.h */ + flags |= SEC_ROM; + break; + case 'i': /* section contains directives (info) */ + /* FIXME: This is IMAGE_SCN_LNK_INFO + in winnt.h */ + flags |= SEC_HAS_CONTENTS; + break; + case 'n': /* section can be discarded */ + flags &=~ SEC_LOAD; + break; + case 'R': /* Remove section at link time */ + flags |= SEC_NEVER_LOAD; + break; + + /* Section Protection */ + case 'r': /* section is readable */ + flags |= IMAGE_SCN_MEM_READ; + break; + case 'w': /* section is writeable */ + flags |= IMAGE_SCN_MEM_WRITE; + break; + case 'x': /* section is executable */ + flags |= IMAGE_SCN_MEM_EXECUTE; + break; + case 's': /* section is sharable */ + flags |= IMAGE_SCN_MEM_SHARED; + break; + + /* Section Alignment */ + case '0': /* align to byte boundary */ + flags |= IMAGE_SCN_ALIGN_1BYTES; + align = 0; + break; + case '1': /* align to halfword boundary */ + flags |= IMAGE_SCN_ALIGN_2BYTES; + align = 1; + break; + case '2': /* align to word boundary */ + flags |= IMAGE_SCN_ALIGN_4BYTES; + align = 2; + break; + case '3': /* align to doubleword boundary */ + flags |= IMAGE_SCN_ALIGN_8BYTES; + align = 3; + break; + case '4': /* align to quadword boundary */ + flags |= IMAGE_SCN_ALIGN_16BYTES; + align = 4; + break; + case '5': /* align to 32 byte boundary */ + flags |= IMAGE_SCN_ALIGN_32BYTES; + align = 5; + break; + case '6': /* align to 64 byte boundary */ + flags |= IMAGE_SCN_ALIGN_64BYTES; + align = 6; + break; + + default: + as_bad(_("unknown section attribute '%c'"), + *input_line_pointer); + break; + } + ++input_line_pointer; + } + if (*input_line_pointer == '"') + ++input_line_pointer; + } + } - S_SET_SEGMENT (label, S_GET_SEGMENT (sym)); - label->sy_frag = sym->sy_frag; - S_SET_VALUE (label, S_GET_VALUE (sym)); + sec = subseg_new (name, (subsegT) exp); - while (! is_end_of_line[(unsigned char) *input_line_pointer]) - ++input_line_pointer; + ppc_set_current_section(sec); - return; - } + if (flags != SEC_NO_FLAGS) + { + if (! bfd_set_section_flags (stdoutput, sec, flags)) + as_bad (_("error setting flags for \"%s\": %s"), + bfd_section_name (stdoutput, sec), + bfd_errmsg (bfd_get_error ())); + } - S_SET_SEGMENT (sym, now_seg); - sym->sy_frag = frag_now; - S_SET_VALUE (sym, (valueT) frag_now_fix ()); - sym->sy_tc.class = XMC_TC; - sym->sy_tc.output = 1; + bfd_set_section_alignment(stdoutput, sec, align); - ppc_frob_label (sym); - } +} -#else /* ! defined (OBJ_COFF) */ +static void +ppc_pe_function (ignore) + int ignore; +{ + char *name; + char endc; + symbolS *ext_sym; - /* Skip the TOC symbol name. */ - while (is_part_of_name (*input_line_pointer) - || *input_line_pointer == '[' - || *input_line_pointer == ']' - || *input_line_pointer == '{' - || *input_line_pointer == '}') - ++input_line_pointer; + name = input_line_pointer; + endc = get_symbol_end (); - /* Align to a four byte boundary. */ - frag_align (2, 0); - record_alignment (now_seg, 2); + ext_sym = symbol_find_or_make (name); -#endif /* ! defined (OBJ_COFF) */ + *input_line_pointer = endc; - if (*input_line_pointer != ',') - demand_empty_rest_of_line (); + S_SET_DATA_TYPE (ext_sym, DT_FCN << N_BTSHFT); + SF_SET_FUNCTION (ext_sym); + SF_SET_PROCESS (ext_sym); + coff_add_linesym (ext_sym); + + demand_empty_rest_of_line (); +} + +static void +ppc_pe_tocd (ignore) + int ignore; +{ + if (tocdata_section == 0) + { + tocdata_section = subseg_new (".tocd", 0); + /* FIXME: section flags won't work */ + bfd_set_section_flags (stdoutput, tocdata_section, + (SEC_ALLOC | SEC_LOAD | SEC_RELOC + | SEC_READONLY | SEC_DATA )); + + bfd_set_section_alignment (stdoutput, tocdata_section, 2); + } else { - ++input_line_pointer; - cons (4); + rdata_section = subseg_new (".tocd", 0); } + + ppc_set_current_section(tocdata_section); + + demand_empty_rest_of_line (); +} + +/* Don't adjust TOC relocs to use the section symbol. */ + +int +ppc_pe_fix_adjustable (fix) + fixS *fix; +{ + return fix->fx_r_type != BFD_RELOC_PPC_TOC16; } + +#endif -#ifdef OBJ_COFF +#ifdef OBJ_XCOFF /* XCOFF specific symbol and file handling. */ @@ -1685,6 +3885,9 @@ ppc_canonicalize_symbol_name (name) { char *s; + if (ppc_stab_symbol) + return name; + for (s = name; *s != '\0' && *s != '{' && *s != '['; s++) ; if (*s != '\0') @@ -1704,7 +3907,7 @@ ppc_canonicalize_symbol_name (name) *s = toupper (*s); if (*s == '\0' || s[1] != '\0') - as_bad ("bad symbol suffix"); + as_bad (_("bad symbol suffix")); *s = ']'; } @@ -1719,16 +3922,21 @@ void ppc_symbol_new_hook (sym) symbolS *sym; { + struct ppc_tc_sy *tc; const char *s; - sym->sy_tc.next = NULL; - sym->sy_tc.output = 0; - sym->sy_tc.class = -1; - sym->sy_tc.real_name = NULL; - sym->sy_tc.subseg = 0; - sym->sy_tc.align = 0; - sym->sy_tc.size = NULL; - sym->sy_tc.within = NULL; + tc = symbol_get_tc (sym); + tc->next = NULL; + tc->output = 0; + tc->class = -1; + tc->real_name = NULL; + tc->subseg = 0; + tc->align = 0; + tc->size = NULL; + tc->within = NULL; + + if (ppc_stab_symbol) + return; s = strchr (S_GET_NAME (sym), '['); if (s == (const char *) NULL) @@ -1743,56 +3951,56 @@ ppc_symbol_new_hook (sym) { case 'B': if (strcmp (s, "BS]") == 0) - sym->sy_tc.class = XMC_BS; + tc->class = XMC_BS; break; case 'D': if (strcmp (s, "DB]") == 0) - sym->sy_tc.class = XMC_DB; + tc->class = XMC_DB; else if (strcmp (s, "DS]") == 0) - sym->sy_tc.class = XMC_DS; + tc->class = XMC_DS; break; case 'G': if (strcmp (s, "GL]") == 0) - sym->sy_tc.class = XMC_GL; + tc->class = XMC_GL; break; case 'P': if (strcmp (s, "PR]") == 0) - sym->sy_tc.class = XMC_PR; + tc->class = XMC_PR; break; case 'R': if (strcmp (s, "RO]") == 0) - sym->sy_tc.class = XMC_RO; + tc->class = XMC_RO; else if (strcmp (s, "RW]") == 0) - sym->sy_tc.class = XMC_RW; + tc->class = XMC_RW; break; case 'S': if (strcmp (s, "SV]") == 0) - sym->sy_tc.class = XMC_SV; + tc->class = XMC_SV; break; case 'T': if (strcmp (s, "TC]") == 0) - sym->sy_tc.class = XMC_TC; + tc->class = XMC_TC; else if (strcmp (s, "TI]") == 0) - sym->sy_tc.class = XMC_TI; + tc->class = XMC_TI; else if (strcmp (s, "TB]") == 0) - sym->sy_tc.class = XMC_TB; - else if (strcmp (s, "TC0]") == 0 || strcm (s, "T0]") == 0) - sym->sy_tc.class = XMC_TC0; + tc->class = XMC_TB; + else if (strcmp (s, "TC0]") == 0 || strcmp (s, "T0]") == 0) + tc->class = XMC_TC0; break; case 'U': if (strcmp (s, "UA]") == 0) - sym->sy_tc.class = XMC_UA; + tc->class = XMC_UA; else if (strcmp (s, "UC]") == 0) - sym->sy_tc.class = XMC_UC; + tc->class = XMC_UC; break; case 'X': if (strcmp (s, "XO]") == 0) - sym->sy_tc.class = XMC_XO; + tc->class = XMC_XO; break; } - if (sym->sy_tc.class == -1) - as_bad ("Unrecognized symbol suffix"); + if (tc->class == -1) + as_bad (_("Unrecognized symbol suffix")); } /* Set the class of a label based on where it is defined. This @@ -1805,16 +4013,22 @@ ppc_frob_label (sym) { if (ppc_current_csect != (symbolS *) NULL) { - if (sym->sy_tc.class == -1) - sym->sy_tc.class = ppc_current_csect->sy_tc.class; + if (symbol_get_tc (sym)->class == -1) + symbol_get_tc (sym)->class = symbol_get_tc (ppc_current_csect)->class; symbol_remove (sym, &symbol_rootP, &symbol_lastP); - symbol_append (sym, ppc_current_csect->sy_tc.within, &symbol_rootP, - &symbol_lastP); - ppc_current_csect->sy_tc.within = sym; + symbol_append (sym, symbol_get_tc (ppc_current_csect)->within, + &symbol_rootP, &symbol_lastP); + symbol_get_tc (ppc_current_csect)->within = sym; } } +/* This variable is set by ppc_frob_symbol if any absolute symbols are + seen. It tells ppc_adjust_symtab whether it needs to look through + the symbols. */ + +static boolean ppc_saw_abs; + /* Change the name of a symbol just before writing it out. Set the real name if the .rename pseudo-op was used. Otherwise, remove any class suffix. Return 1 if the symbol should not be included in the @@ -1829,15 +4043,15 @@ ppc_frob_symbol (sym) /* Discard symbols that should not be included in the output symbol table. */ - if (! sym->sy_used_in_reloc - && ((sym->bsym->flags & BSF_SECTION_SYM) != 0 + if (! symbol_used_in_reloc_p (sym) + && ((symbol_get_bfdsym (sym)->flags & BSF_SECTION_SYM) != 0 || (! S_IS_EXTERNAL (sym) - && ! sym->sy_tc.output + && ! symbol_get_tc (sym)->output && S_GET_STORAGE_CLASS (sym) != C_FILE))) return 1; - if (sym->sy_tc.real_name != (char *) NULL) - S_SET_NAME (sym, sym->sy_tc.real_name); + if (symbol_get_tc (sym)->real_name != (char *) NULL) + S_SET_NAME (sym, symbol_get_tc (sym)->real_name); else { const char *name; @@ -1868,19 +4082,20 @@ ppc_frob_symbol (sym) if (SF_GET_FUNCTION (sym)) { if (ppc_last_function != (symbolS *) NULL) - as_warn ("two .function pseudo-ops with no intervening .ef"); + as_bad (_("two .function pseudo-ops with no intervening .ef")); ppc_last_function = sym; - if (sym->sy_tc.size != (symbolS *) NULL) + if (symbol_get_tc (sym)->size != (symbolS *) NULL) { - resolve_symbol_value (sym->sy_tc.size); - SA_SET_SYM_FSIZE (sym, (long) S_GET_VALUE (sym->sy_tc.size)); + resolve_symbol_value (symbol_get_tc (sym)->size, 1); + SA_SET_SYM_FSIZE (sym, + (long) S_GET_VALUE (symbol_get_tc (sym)->size)); } } else if (S_GET_STORAGE_CLASS (sym) == C_FCN && strcmp (S_GET_NAME (sym), ".ef") == 0) { if (ppc_last_function == (symbolS *) NULL) - as_warn (".ef with no preceding .function"); + as_bad (_(".ef with no preceding .function")); else { set_end = ppc_last_function; @@ -1893,11 +4108,14 @@ ppc_frob_symbol (sym) } if (! S_IS_EXTERNAL (sym) - && (sym->bsym->flags & BSF_SECTION_SYM) == 0 + && (symbol_get_bfdsym (sym)->flags & BSF_SECTION_SYM) == 0 && S_GET_STORAGE_CLASS (sym) != C_FILE && S_GET_STORAGE_CLASS (sym) != C_FCN + && S_GET_STORAGE_CLASS (sym) != C_BLOCK && S_GET_STORAGE_CLASS (sym) != C_BSTAT && S_GET_STORAGE_CLASS (sym) != C_ESTAT + && S_GET_STORAGE_CLASS (sym) != C_BINCL + && S_GET_STORAGE_CLASS (sym) != C_EINCL && S_GET_SEGMENT (sym) != ppc_coff_debug_section) S_SET_STORAGE_CLASS (sym, C_HIDEXT); @@ -1910,39 +4128,48 @@ ppc_frob_symbol (sym) /* Create a csect aux. */ i = S_GET_NUMBER_AUXILIARY (sym); S_SET_NUMBER_AUXILIARY (sym, i + 1); - a = &coffsymbol (sym->bsym)->native[i + 1].u.auxent; - if (sym->sy_tc.class == XMC_TC0) + a = &coffsymbol (symbol_get_bfdsym (sym))->native[i + 1].u.auxent; + if (symbol_get_tc (sym)->class == XMC_TC0) { /* This is the TOC table. */ know (strcmp (S_GET_NAME (sym), "TOC") == 0); a->x_csect.x_scnlen.l = 0; a->x_csect.x_smtyp = (2 << 3) | XTY_SD; } - else if (sym->sy_tc.subseg != 0) + else if (symbol_get_tc (sym)->subseg != 0) { /* This is a csect symbol. x_scnlen is the size of the csect. */ - if (sym->sy_tc.next == (symbolS *) NULL) + if (symbol_get_tc (sym)->next == (symbolS *) NULL) a->x_csect.x_scnlen.l = (bfd_section_size (stdoutput, S_GET_SEGMENT (sym)) - S_GET_VALUE (sym)); else { - resolve_symbol_value (sym->sy_tc.next); - a->x_csect.x_scnlen.l = (S_GET_VALUE (sym->sy_tc.next) + resolve_symbol_value (symbol_get_tc (sym)->next, 1); + a->x_csect.x_scnlen.l = (S_GET_VALUE (symbol_get_tc (sym)->next) - S_GET_VALUE (sym)); } - a->x_csect.x_smtyp = (sym->sy_tc.align << 3) | XTY_SD; + a->x_csect.x_smtyp = (symbol_get_tc (sym)->align << 3) | XTY_SD; } else if (S_GET_SEGMENT (sym) == bss_section) { /* This is a common symbol. */ - a->x_csect.x_scnlen.l = sym->sy_frag->fr_offset; - a->x_csect.x_smtyp = (sym->sy_tc.align << 3) | XTY_CM; + a->x_csect.x_scnlen.l = symbol_get_frag (sym)->fr_offset; + a->x_csect.x_smtyp = (symbol_get_tc (sym)->align << 3) | XTY_CM; if (S_IS_EXTERNAL (sym)) - sym->sy_tc.class = XMC_RW; + symbol_get_tc (sym)->class = XMC_RW; else - sym->sy_tc.class = XMC_BS; + symbol_get_tc (sym)->class = XMC_BS; + } + else if (S_GET_SEGMENT (sym) == absolute_section) + { + /* This is an absolute symbol. The csect will be created by + ppc_adjust_symtab. */ + ppc_saw_abs = true; + a->x_csect.x_smtyp = XTY_LD; + if (symbol_get_tc (sym)->class == -1) + symbol_get_tc (sym)->class = XMC_XO; } else if (! S_IS_DEFINED (sym)) { @@ -1950,17 +4177,17 @@ ppc_frob_symbol (sym) a->x_csect.x_scnlen.l = 0; a->x_csect.x_smtyp = XTY_ER; } - else if (sym->sy_tc.class == XMC_TC) + else if (symbol_get_tc (sym)->class == XMC_TC) { symbolS *next; /* This is a TOC definition. x_scnlen is the size of the TOC entry. */ next = symbol_next (sym); - while (next->sy_tc.class == XMC_TC0) + while (symbol_get_tc (next)->class == XMC_TC0) next = symbol_next (next); if (next == (symbolS *) NULL - || next->sy_tc.class != XMC_TC) + || symbol_get_tc (next)->class != XMC_TC) { if (ppc_after_toc_frag == (fragS *) NULL) a->x_csect.x_scnlen.l = (bfd_section_size (stdoutput, @@ -1972,7 +4199,7 @@ ppc_frob_symbol (sym) } else { - resolve_symbol_value (next); + resolve_symbol_value (next, 1); a->x_csect.x_scnlen.l = (S_GET_VALUE (next) - S_GET_VALUE (sym)); } @@ -1992,34 +4219,43 @@ ppc_frob_symbol (sym) abort (); /* Skip the initial dummy symbol. */ - csect = csect->sy_tc.next; + csect = symbol_get_tc (csect)->next; if (csect == (symbolS *) NULL) - a->x_csect.x_scnlen.l = 0; + { + as_warn (_("warning: symbol %s has no csect"), S_GET_NAME (sym)); + a->x_csect.x_scnlen.l = 0; + } else { - while (csect->sy_tc.next != (symbolS *) NULL) + while (symbol_get_tc (csect)->next != (symbolS *) NULL) { - resolve_symbol_value (csect->sy_tc.next); - if (S_GET_VALUE (csect->sy_tc.next) > S_GET_VALUE (sym)) + resolve_symbol_value (symbol_get_tc (csect)->next, 1); + if (S_GET_VALUE (symbol_get_tc (csect)->next) + > S_GET_VALUE (sym)) break; - csect = csect->sy_tc.next; + csect = symbol_get_tc (csect)->next; } - a->x_csect.x_scnlen.p = coffsymbol (csect->bsym)->native; - coffsymbol (sym->bsym)->native[i + 1].fix_scnlen = 1; + a->x_csect.x_scnlen.p = + coffsymbol (symbol_get_bfdsym (csect))->native; + coffsymbol (symbol_get_bfdsym (sym))->native[i + 1].fix_scnlen = + 1; } a->x_csect.x_smtyp = XTY_LD; } a->x_csect.x_parmhash = 0; a->x_csect.x_snhash = 0; - if (sym->sy_tc.class == -1) + if (symbol_get_tc (sym)->class == -1) a->x_csect.x_smclas = XMC_PR; else - a->x_csect.x_smclas = sym->sy_tc.class; + a->x_csect.x_smclas = symbol_get_tc (sym)->class; a->x_csect.x_stab = 0; a->x_csect.x_snstab = 0; + + /* Don't let the COFF backend resort these symbols. */ + symbol_get_bfdsym (sym)->flags |= BSF_NOT_AT_END; } else if (S_GET_STORAGE_CLASS (sym) == C_BSTAT) { @@ -2027,8 +4263,10 @@ ppc_frob_symbol (sym) csect symbol. BFD will do that for us if we set the right flags. */ S_SET_VALUE (sym, - (valueT) coffsymbol (sym->sy_tc.within->bsym)->native); - coffsymbol (sym->bsym)->native->fix_value = 1; + ((valueT) + coffsymbol (symbol_get_bfdsym + (symbol_get_tc (sym)->within))->native)); + coffsymbol (symbol_get_bfdsym (sym))->native->fix_value = 1; } else if (S_GET_STORAGE_CLASS (sym) == C_STSYM) { @@ -2036,15 +4274,69 @@ ppc_frob_symbol (sym) symbolS *csect; /* The value is the offset from the enclosing csect. */ - block = sym->sy_tc.within; - csect = block->sy_tc.within; - resolve_symbol_value (csect); + block = symbol_get_tc (sym)->within; + csect = symbol_get_tc (block)->within; + resolve_symbol_value (csect, 1); S_SET_VALUE (sym, S_GET_VALUE (sym) - S_GET_VALUE (csect)); } + else if (S_GET_STORAGE_CLASS (sym) == C_BINCL + || S_GET_STORAGE_CLASS (sym) == C_EINCL) + { + /* We want the value to be a file offset into the line numbers. + BFD will do that for us if we set the right flags. We have + already set the value correctly. */ + coffsymbol (symbol_get_bfdsym (sym))->native->fix_line = 1; + } return 0; } +/* Adjust the symbol table. This creates csect symbols for all + absolute symbols. */ + +void +ppc_adjust_symtab () +{ + symbolS *sym; + + if (! ppc_saw_abs) + return; + + for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym)) + { + symbolS *csect; + int i; + union internal_auxent *a; + + if (S_GET_SEGMENT (sym) != absolute_section) + continue; + + csect = symbol_create (".abs[XO]", absolute_section, + S_GET_VALUE (sym), &zero_address_frag); + symbol_get_bfdsym (csect)->value = S_GET_VALUE (sym); + S_SET_STORAGE_CLASS (csect, C_HIDEXT); + i = S_GET_NUMBER_AUXILIARY (csect); + S_SET_NUMBER_AUXILIARY (csect, i + 1); + a = &coffsymbol (symbol_get_bfdsym (csect))->native[i + 1].u.auxent; + a->x_csect.x_scnlen.l = 0; + a->x_csect.x_smtyp = XTY_SD; + a->x_csect.x_parmhash = 0; + a->x_csect.x_snhash = 0; + a->x_csect.x_smclas = XMC_XO; + a->x_csect.x_stab = 0; + a->x_csect.x_snstab = 0; + + symbol_insert (csect, sym, &symbol_rootP, &symbol_lastP); + + i = S_GET_NUMBER_AUXILIARY (sym); + a = &coffsymbol (symbol_get_bfdsym (sym))->native[i].u.auxent; + a->x_csect.x_scnlen.p = coffsymbol (symbol_get_bfdsym (csect))->native; + coffsymbol (symbol_get_bfdsym (sym))->native[i].fix_scnlen = 1; + } + + ppc_saw_abs = false; +} + /* Set the VMA for a section. This is called on all the sections in turn. */ @@ -2058,26 +4350,7 @@ ppc_frob_section (sec) vma += bfd_section_size (stdoutput, sec); } -/* Adjust the file by adding a .debug section if needed. */ - -void -ppc_frob_file () -{ - if (ppc_debug_name_section_size > 0) - { - asection *sec; - - sec = bfd_make_section (stdoutput, ".debug"); - if (sec == (asection *) NULL - || ! bfd_set_section_size (stdoutput, sec, - ppc_debug_name_section_size) - || ! bfd_set_section_flags (stdoutput, sec, - SEC_HAS_CONTENTS | SEC_LOAD)) - as_fatal ("can't make .debug section"); - } -} - -#endif /* OBJ_COFF */ +#endif /* OBJ_XCOFF */ /* Turn a string in input_line_pointer into a floating point constant of type type, and store the appropriate bytes in *litp. The number @@ -2107,7 +4380,7 @@ md_atof (type, litp, sizep) default: *sizep = 0; - return "bad call to md_atof"; + return _("bad call to md_atof"); } t = atof_ieee (input_line_pointer, type, words); @@ -2116,7 +4389,7 @@ md_atof (type, litp, sizep) *sizep = prec * 2; - if (ppc_big_endian) + if (target_big_endian) { for (i = 0; i < prec; i++) { @@ -2145,7 +4418,7 @@ md_number_to_chars (buf, val, n) valueT val; int n; { - if (ppc_big_endian) + if (target_big_endian) number_to_chars_bigendian (buf, val, n); else number_to_chars_littleendian (buf, val, n); @@ -2171,13 +4444,9 @@ md_estimate_size_before_relax (fragp, seg) asection *seg; { abort (); + return 0; } -const relax_typeS md_relax_table[] = -{ - { 0 } -}; - /* Convert a machine dependent frag. We never generate these. */ void @@ -2189,16 +4458,6 @@ md_convert_frag (abfd, sec, fragp) abort (); } -/* Parse an operand that is machine-specific. We just return without - modifying the expression if we have nothing to do. */ - -/*ARGSUSED*/ -void -md_operand (expressionP) - expressionS *expressionP; -{ -} - /* We have no need to default values of symbols. */ /*ARGSUSED*/ @@ -2215,19 +4474,14 @@ md_undefined_symbol (name) given a PC relative reloc. */ long -md_pcrel_from (fixp) +md_pcrel_from_section (fixp, sec) fixS *fixp; + segT sec; { -#ifdef OBJ_ELF - if (fixp->fx_addsy != (symbolS *) NULL - && ! S_IS_DEFINED (fixp->fx_addsy)) - return 0; -#endif - return fixp->fx_frag->fr_address + fixp->fx_where; } -#ifdef OBJ_COFF +#ifdef OBJ_XCOFF /* This is called to see whether a fixup should be adjusted to use a section symbol. We take the opportunity to change a fixup against @@ -2240,7 +4494,7 @@ ppc_fix_adjustable (fix) { valueT val; - resolve_symbol_value (fix->fx_addsy); + resolve_symbol_value (fix->fx_addsy, 1); val = S_GET_VALUE (fix->fx_addsy); if (ppc_toc_csect != (symbolS *) NULL && fix->fx_addsy != (symbolS *) NULL @@ -2256,11 +4510,11 @@ ppc_fix_adjustable (fix) sy != (symbolS *) NULL; sy = symbol_next (sy)) { - if (sy->sy_tc.class == XMC_TC0) + if (symbol_get_tc (sy)->class == XMC_TC0) continue; - if (sy->sy_tc.class != XMC_TC) + if (symbol_get_tc (sy)->class != XMC_TC) break; - resolve_symbol_value (sy); + resolve_symbol_value (sy, 1); if (val == S_GET_VALUE (sy)) { fix->fx_addsy = sy; @@ -2270,15 +4524,21 @@ ppc_fix_adjustable (fix) } as_bad_where (fix->fx_file, fix->fx_line, - "symbol in .toc does not match any .tc"); + _("symbol in .toc does not match any .tc")); } /* Possibly adjust the reloc to be against the csect. */ if (fix->fx_addsy != (symbolS *) NULL - && fix->fx_addsy->sy_tc.subseg == 0 - && fix->fx_addsy->sy_tc.class != XMC_TC0 - && fix->fx_addsy->sy_tc.class != XMC_TC - && S_GET_SEGMENT (fix->fx_addsy) != bss_section) + && symbol_get_tc (fix->fx_addsy)->subseg == 0 + && symbol_get_tc (fix->fx_addsy)->class != XMC_TC0 + && symbol_get_tc (fix->fx_addsy)->class != XMC_TC + && S_GET_SEGMENT (fix->fx_addsy) != bss_section + /* Don't adjust if this is a reloc in the toc section. */ + && (S_GET_SEGMENT (fix->fx_addsy) != data_section + || ppc_toc_csect == NULL + || val < ppc_toc_frag->fr_address + || (ppc_after_toc_frag != NULL + && val >= ppc_after_toc_frag->fr_address))) { symbolS *csect; @@ -2290,17 +4550,45 @@ ppc_fix_adjustable (fix) abort (); /* Skip the initial dummy symbol. */ - csect = csect->sy_tc.next; + csect = symbol_get_tc (csect)->next; if (csect != (symbolS *) NULL) { - while (csect->sy_tc.next != (symbolS *) NULL - && (csect->sy_tc.next->sy_frag->fr_address - <= fix->fx_addsy->sy_frag->fr_address)) - csect = csect->sy_tc.next; + while (symbol_get_tc (csect)->next != (symbolS *) NULL + && (symbol_get_frag (symbol_get_tc (csect)->next)->fr_address + <= symbol_get_frag (fix->fx_addsy)->fr_address)) + { + /* If the csect address equals the symbol value, then we + have to look through the full symbol table to see + whether this is the csect we want. Note that we will + only get here if the csect has zero length. */ + if ((symbol_get_frag (csect)->fr_address + == symbol_get_frag (fix->fx_addsy)->fr_address) + && S_GET_VALUE (csect) == S_GET_VALUE (fix->fx_addsy)) + { + symbolS *scan; + + for (scan = symbol_next (csect); + scan != NULL; + scan = symbol_next (scan)) + { + if (symbol_get_tc (scan)->subseg != 0) + break; + if (scan == fix->fx_addsy) + break; + } + + /* If we found the symbol before the next csect + symbol, then this is the csect we want. */ + if (scan == fix->fx_addsy) + break; + } + + csect = symbol_get_tc (csect)->next; + } fix->fx_offset += (S_GET_VALUE (fix->fx_addsy) - - csect->sy_frag->fr_address); + - symbol_get_frag (csect)->fr_address); fix->fx_addsy = csect; } } @@ -2311,16 +4599,42 @@ ppc_fix_adjustable (fix) && S_GET_SEGMENT (fix->fx_addsy) == bss_section && ! S_IS_EXTERNAL (fix->fx_addsy)) { - resolve_symbol_value (fix->fx_addsy->sy_frag->fr_symbol); - fix->fx_offset += (S_GET_VALUE (fix->fx_addsy) - - S_GET_VALUE (fix->fx_addsy->sy_frag->fr_symbol)); - fix->fx_addsy = fix->fx_addsy->sy_frag->fr_symbol; + resolve_symbol_value (symbol_get_frag (fix->fx_addsy)->fr_symbol, 1); + fix->fx_offset += + (S_GET_VALUE (fix->fx_addsy) + - S_GET_VALUE (symbol_get_frag (fix->fx_addsy)->fr_symbol)); + fix->fx_addsy = symbol_get_frag (fix->fx_addsy)->fr_symbol; } return 0; } -#endif +/* A reloc from one csect to another must be kept. The assembler + will, of course, keep relocs between sections, and it will keep + absolute relocs, but we need to force it to keep PC relative relocs + between two csects in the same section. */ + +int +ppc_force_relocation (fix) + fixS *fix; +{ + /* At this point fix->fx_addsy should already have been converted to + a csect symbol. If the csect does not include the fragment, then + we need to force the relocation. */ + if (fix->fx_pcrel + && fix->fx_addsy != NULL + && symbol_get_tc (fix->fx_addsy)->subseg != 0 + && ((symbol_get_frag (fix->fx_addsy)->fr_address + > fix->fx_frag->fr_address) + || (symbol_get_tc (fix->fx_addsy)->next != NULL + && (symbol_get_frag (symbol_get_tc (fix->fx_addsy)->next)->fr_address + <= fix->fx_frag->fr_address)))) + return 1; + + return 0; +} + +#endif /* OBJ_XCOFF */ /* See whether a symbol is in the TOC section. */ @@ -2328,8 +4642,8 @@ static int ppc_is_toc_sym (sym) symbolS *sym; { -#ifdef OBJ_COFF - return sym->sy_tc.class == XMC_TC; +#ifdef OBJ_XCOFF + return symbol_get_tc (sym)->class == XMC_TC; #else return strcmp (segment_name (S_GET_SEGMENT (sym)), ".got") == 0; #endif @@ -2345,23 +4659,46 @@ ppc_is_toc_sym (sym) fixup. */ int -md_apply_fix (fixp, valuep) +md_apply_fix3 (fixp, valuep, seg) fixS *fixp; valueT *valuep; + segT seg; { valueT value; +#ifdef OBJ_ELF + value = *valuep; + if (fixp->fx_addsy != NULL) + { + /* `*valuep' may contain the value of the symbol on which the reloc + will be based; we have to remove it. */ + if (symbol_used_in_reloc_p (fixp->fx_addsy) + && S_GET_SEGMENT (fixp->fx_addsy) != absolute_section + && S_GET_SEGMENT (fixp->fx_addsy) != undefined_section + && ! bfd_is_com_section (S_GET_SEGMENT (fixp->fx_addsy))) + value -= S_GET_VALUE (fixp->fx_addsy); + + /* FIXME: Why '+'? Better yet, what exactly is '*valuep' + supposed to be? I think this is related to various similar + FIXMEs in tc-i386.c and tc-sparc.c. */ + if (fixp->fx_pcrel) + value += fixp->fx_frag->fr_address + fixp->fx_where; + } + else + { + fixp->fx_done = 1; + } +#else /* FIXME FIXME FIXME: The value we are passed in *valuep includes the symbol values. Since we are using BFD_ASSEMBLER, if we are doing this relocation the code in write.c is going to call - bfd_perform_relocation, which is also going to use the symbol + bfd_install_relocation, which is also going to use the symbol value. That means that if the reloc is fully resolved we want to - use *valuep since bfd_perform_relocation is not being used. + use *valuep since bfd_install_relocation is not being used. However, if the reloc is not fully resolved we do not want to use *valuep, and must use fx_offset instead. However, if the reloc is PC relative, we do want to use *valuep since it includes the result of md_pcrel_from. This is confusing. */ - if (fixp->fx_addsy == (symbolS *) NULL) { value = *valuep; @@ -2380,10 +4717,11 @@ md_apply_fix (fixp, valuep) { /* We can't actually support subtracting a symbol. */ as_bad_where (fixp->fx_file, fixp->fx_line, - "expression too complex"); + _("expression too complex")); } } } +#endif if ((int) fixp->fx_r_type >= (int) BFD_RELOC_UNUSED) { @@ -2396,7 +4734,7 @@ md_apply_fix (fixp, valuep) operand = &powerpc_operands[opindex]; -#ifdef OBJ_COFF +#ifdef OBJ_XCOFF /* It appears that an instruction like l 9,LC..1(30) when LC..1 is not a TOC symbol does not generate a reloc. It @@ -2409,9 +4747,9 @@ md_apply_fix (fixp, valuep) && operand->shift == 0 && operand->insert == NULL && fixp->fx_addsy != NULL - && fixp->fx_addsy->sy_tc.subseg != 0 - && fixp->fx_addsy->sy_tc.class != XMC_TC - && fixp->fx_addsy->sy_tc.class != XMC_TC0 + && symbol_get_tc (fixp->fx_addsy)->subseg != 0 + && symbol_get_tc (fixp->fx_addsy)->class != XMC_TC + && symbol_get_tc (fixp->fx_addsy)->class != XMC_TC0 && S_GET_SEGMENT (fixp->fx_addsy) != bss_section) { value = fixp->fx_offset; @@ -2422,13 +4760,13 @@ md_apply_fix (fixp, valuep) /* Fetch the instruction, insert the fully resolved operand value, and stuff the instruction back again. */ where = fixp->fx_frag->fr_literal + fixp->fx_where; - if (ppc_big_endian) + if (target_big_endian) insn = bfd_getb32 ((unsigned char *) where); else insn = bfd_getl32 ((unsigned char *) where); insn = ppc_insert_operand (insn, operand, (offsetT) value, fixp->fx_file, fixp->fx_line); - if (ppc_big_endian) + if (target_big_endian) bfd_putb32 ((bfd_vma) insn, (unsigned char *) where); else bfd_putl32 ((bfd_vma) insn, (unsigned char *) where); @@ -2443,17 +4781,24 @@ md_apply_fix (fixp, valuep) We are only prepared to turn a few of the operands into relocs. FIXME: We need to handle the DS field at the very least. - FIXME: Handling 16 bit branches would also be reasonable. FIXME: Selecting the reloc type is a bit haphazard; perhaps there should be a new field in the operand table. */ if ((operand->flags & PPC_OPERAND_RELATIVE) != 0 && operand->bits == 26 && operand->shift == 0) fixp->fx_r_type = BFD_RELOC_PPC_B26; + else if ((operand->flags & PPC_OPERAND_RELATIVE) != 0 + && operand->bits == 16 + && operand->shift == 0) + fixp->fx_r_type = BFD_RELOC_PPC_B16; else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0 && operand->bits == 26 && operand->shift == 0) fixp->fx_r_type = BFD_RELOC_PPC_BA26; + else if ((operand->flags & PPC_OPERAND_ABSOLUTE) != 0 + && operand->bits == 16 + && operand->shift == 0) + fixp->fx_r_type = BFD_RELOC_PPC_BA16; else if ((operand->flags & PPC_OPERAND_PARENS) != 0 && operand->bits == 16 && operand->shift == 0 @@ -2462,35 +4807,173 @@ md_apply_fix (fixp, valuep) && ppc_is_toc_sym (fixp->fx_addsy)) { fixp->fx_size = 2; - if (ppc_big_endian) + if (target_big_endian) fixp->fx_where += 2; fixp->fx_r_type = BFD_RELOC_PPC_TOC16; } else { - as_bad_where (fixp->fx_file, fixp->fx_line, - "unresolved expression that must be resolved"); + char *sfile; + unsigned int sline; + + /* Use expr_symbol_where to see if this is an expression + symbol. */ + if (expr_symbol_where (fixp->fx_addsy, &sfile, &sline)) + as_bad_where (fixp->fx_file, fixp->fx_line, + _("unresolved expression that must be resolved")); + else + as_bad_where (fixp->fx_file, fixp->fx_line, + _("unsupported relocation type")); fixp->fx_done = 1; return 1; } } else { +#ifdef OBJ_ELF + ppc_elf_validate_fix (fixp, seg); +#endif switch (fixp->fx_r_type) { case BFD_RELOC_32: + case BFD_RELOC_CTOR: + if (fixp->fx_pcrel) + fixp->fx_r_type = BFD_RELOC_32_PCREL; + /* fall through */ + + case BFD_RELOC_RVA: + case BFD_RELOC_32_PCREL: + case BFD_RELOC_32_BASEREL: + case BFD_RELOC_PPC_EMB_NADDR32: md_number_to_chars (fixp->fx_frag->fr_literal + fixp->fx_where, value, 4); break; + + case BFD_RELOC_LO16: case BFD_RELOC_16: + case BFD_RELOC_GPREL16: + case BFD_RELOC_16_GOT_PCREL: + case BFD_RELOC_16_GOTOFF: + case BFD_RELOC_LO16_GOTOFF: + case BFD_RELOC_HI16_GOTOFF: + case BFD_RELOC_HI16_S_GOTOFF: + case BFD_RELOC_LO16_BASEREL: + case BFD_RELOC_HI16_BASEREL: + case BFD_RELOC_HI16_S_BASEREL: + case BFD_RELOC_PPC_EMB_NADDR16: + case BFD_RELOC_PPC_EMB_NADDR16_LO: + case BFD_RELOC_PPC_EMB_NADDR16_HI: + case BFD_RELOC_PPC_EMB_NADDR16_HA: + case BFD_RELOC_PPC_EMB_SDAI16: + case BFD_RELOC_PPC_EMB_SDA2REL: + case BFD_RELOC_PPC_EMB_SDA2I16: + case BFD_RELOC_PPC_EMB_RELSEC16: + case BFD_RELOC_PPC_EMB_RELST_LO: + case BFD_RELOC_PPC_EMB_RELST_HI: + case BFD_RELOC_PPC_EMB_RELST_HA: + case BFD_RELOC_PPC_EMB_RELSDA: + case BFD_RELOC_PPC_TOC16: + if (fixp->fx_pcrel) + { + if (fixp->fx_addsy != NULL) + as_bad_where (fixp->fx_file, fixp->fx_line, + _("cannot emit PC relative %s relocation against %s"), + bfd_get_reloc_code_name (fixp->fx_r_type), + S_GET_NAME (fixp->fx_addsy)); + else + as_bad_where (fixp->fx_file, fixp->fx_line, + _("cannot emit PC relative %s relocation"), + bfd_get_reloc_code_name (fixp->fx_r_type)); + } + + md_number_to_chars (fixp->fx_frag->fr_literal + fixp->fx_where, + value, 2); + break; + + /* This case happens when you write, for example, + lis %r3,(L1-L2)@ha + where L1 and L2 are defined later. */ + case BFD_RELOC_HI16: + if (fixp->fx_pcrel) + abort (); + md_number_to_chars (fixp->fx_frag->fr_literal + fixp->fx_where, + value >> 16, 2); + break; + case BFD_RELOC_HI16_S: + if (fixp->fx_pcrel) + abort (); md_number_to_chars (fixp->fx_frag->fr_literal + fixp->fx_where, + (value + 0x8000) >> 16, 2); + break; + + /* Because SDA21 modifies the register field, the size is set to 4 + bytes, rather than 2, so offset it here appropriately */ + case BFD_RELOC_PPC_EMB_SDA21: + if (fixp->fx_pcrel) + abort (); + + md_number_to_chars (fixp->fx_frag->fr_literal + fixp->fx_where + + ((target_big_endian) ? 2 : 0), value, 2); break; + case BFD_RELOC_8: + if (fixp->fx_pcrel) + abort (); + md_number_to_chars (fixp->fx_frag->fr_literal + fixp->fx_where, value, 1); break; + + case BFD_RELOC_24_PLT_PCREL: + case BFD_RELOC_PPC_LOCAL24PC: + if (!fixp->fx_pcrel && !fixp->fx_done) + abort (); + + if (fixp->fx_done) + { + char *where; + unsigned long insn; + + /* Fetch the instruction, insert the fully resolved operand + value, and stuff the instruction back again. */ + where = fixp->fx_frag->fr_literal + fixp->fx_where; + if (target_big_endian) + insn = bfd_getb32 ((unsigned char *) where); + else + insn = bfd_getl32 ((unsigned char *) where); + if ((value & 3) != 0) + as_bad_where (fixp->fx_file, fixp->fx_line, + _("must branch to an address a multiple of 4")); + if ((offsetT) value < -0x40000000 + || (offsetT) value >= 0x40000000) + as_bad_where (fixp->fx_file, fixp->fx_line, + _("@local or @plt branch destination is too far away, %ld bytes"), + value); + insn = insn | (value & 0x03fffffc); + if (target_big_endian) + bfd_putb32 ((bfd_vma) insn, (unsigned char *) where); + else + bfd_putl32 ((bfd_vma) insn, (unsigned char *) where); + } + break; + + case BFD_RELOC_VTABLE_INHERIT: + fixp->fx_done = 0; + if (fixp->fx_addsy + && !S_IS_DEFINED (fixp->fx_addsy) + && !S_IS_WEAK (fixp->fx_addsy)) + S_SET_WEAK (fixp->fx_addsy); + break; + + case BFD_RELOC_VTABLE_ENTRY: + fixp->fx_done = 0; + break; + default: + fprintf(stderr, + _("Gas failure, reloc value %d\n"), fixp->fx_r_type); + fflush(stderr); abort (); } } @@ -2502,10 +4985,14 @@ md_apply_fix (fixp, valuep) fixp->fx_addnumber = 0; else { +#ifdef TE_PE + fixp->fx_addnumber = 0; +#else /* We want to use the offset within the data segment of the symbol, not the actual VMA of the symbol. */ fixp->fx_addnumber = - bfd_get_section_vma (stdoutput, S_GET_SEGMENT (fixp->fx_addsy)); +#endif } #endif @@ -2521,15 +5008,16 @@ tc_gen_reloc (seg, fixp) { arelent *reloc; - reloc = (arelent *) bfd_alloc_by_size_t (stdoutput, sizeof (arelent)); + reloc = (arelent *) xmalloc (sizeof (arelent)); - reloc->sym_ptr_ptr = &fixp->fx_addsy->bsym; + reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type); if (reloc->howto == (reloc_howto_type *) NULL) { as_bad_where (fixp->fx_file, fixp->fx_line, - "reloc not supported by object file format"); + _("reloc %d not supported by object file format"), (int)fixp->fx_r_type); return NULL; } reloc->addend = fixp->fx_addnumber;