diff --git a/contrib/toolchain/binutils/gas/Makefile b/contrib/toolchain/binutils/gas/Makefile new file mode 100644 index 0000000000..e17ebfd9bc --- /dev/null +++ b/contrib/toolchain/binutils/gas/Makefile @@ -0,0 +1,45 @@ + +LIB_DIR:= $(SDK_DIR)/lib + +CFLAGS_OPT = -U_Win32 -U_WIN32 -U__MINGW32__ -UWIN32 -U_MSC_VER -O2 +CFLAGS_OPT+= -fomit-frame-pointer -fno-ident -mno-ms-bitfields +CFLAGS_OPT+= -W -Wall -Wmissing-prototypes -Wno-format +CFLAGS = -c $(CFLAGS_OPT) + +INCLUDES= -I. -I../bfd -I./config -I../include -I../ +INCLUDES+= -I$(SDK_DIR)/sources/newlib/libc/include -I$(SDK_DIR)/sources/zlib + +DEFINES= -DHAVE_CONFIG_H -DLOCALEDIR='"/home/autobuild/tools/win32/share/locale"' + +LIBS= -lopcodes -lbfd -liberty -lz -lgcc -lc.dll -lapp + +LIBPATH:= -L$(LIB_DIR) -L/home/autobuild/tools/win32/mingw32/lib + +LDFLAGS = -static -nostdlib --stack 12582912 -T$(SDK_DIR)/sources/newlib/app.lds --image-base 0 + + +SRCS = \ + app.c as.c atof-generic.c compress-debug.c \ + cond.c depend.c dwarf2dbg.c dw2gencfi.c ecoff.c \ + ehopt.c expr.c flonum-copy.c flonum-konst.c \ + flonum-mult.c frags.c hash.c input-file.c \ + input-scrub.c listing.c literal.c macro.c \ + messages.c output-file.c read.c remap.c sb.c \ + stabs.c subsegs.c symbols.c write.c \ + config/atof-ieee.c config/obj-coff.c \ + config/tc-i386.c + +OBJS = $(patsubst %.cpp, %.o, $(patsubst %.c, %.o, $(SRCS))) + +# targets + +all: as + +as: $(OBJS) Makefile + $(LD) $(LDFLAGS) $(LIBPATH) -o $@ $(OBJS) $(LIBS) + kos32-objcopy $@ -O binary + +%.o : %.c Makefile + $(CC) $(CFLAGS) $(DEFINES) $(INCLUDES) -o $@ $< + + \ No newline at end of file diff --git a/contrib/toolchain/binutils/gas/app.c b/contrib/toolchain/binutils/gas/app.c new file mode 100644 index 0000000000..ec3a35ee86 --- /dev/null +++ b/contrib/toolchain/binutils/gas/app.c @@ -0,0 +1,1473 @@ +/* This is the Assembler Pre-Processor + Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2007, 2008, 2009, 2010, 2012 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* Modified by Allen Wirfs-Brock, Instantiations Inc 2/90. */ +/* App, the assembler pre-processor. This pre-processor strips out + excess spaces, turns single-quoted characters into a decimal + constant, and turns the # in # into a + .linefile. This needs better error-handling. */ + +#include "as.h" + +#if (__STDC__ != 1) +#ifndef const +#define const /* empty */ +#endif +#endif + +#ifdef H_TICK_HEX +int enable_h_tick_hex = 0; +#endif + +#ifdef TC_M68K +/* Whether we are scrubbing in m68k MRI mode. This is different from + flag_m68k_mri, because the two flags will be affected by the .mri + pseudo-op at different times. */ +static int scrub_m68k_mri; + +/* The pseudo-op which switches in and out of MRI mode. See the + comment in do_scrub_chars. */ +static const char mri_pseudo[] = ".mri 0"; +#else +#define scrub_m68k_mri 0 +#endif + +#if defined TC_ARM && defined OBJ_ELF +/* The pseudo-op for which we need to special-case `@' characters. + See the comment in do_scrub_chars. */ +static const char symver_pseudo[] = ".symver"; +static const char * symver_state; +#endif + +static char lex[256]; +static const char symbol_chars[] = +"$._ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + +#define LEX_IS_SYMBOL_COMPONENT 1 +#define LEX_IS_WHITESPACE 2 +#define LEX_IS_LINE_SEPARATOR 3 +#define LEX_IS_COMMENT_START 4 +#define LEX_IS_LINE_COMMENT_START 5 +#define LEX_IS_TWOCHAR_COMMENT_1ST 6 +#define LEX_IS_STRINGQUOTE 8 +#define LEX_IS_COLON 9 +#define LEX_IS_NEWLINE 10 +#define LEX_IS_ONECHAR_QUOTE 11 +#ifdef TC_V850 +#define LEX_IS_DOUBLEDASH_1ST 12 +#endif +#ifdef TC_M32R +#define DOUBLEBAR_PARALLEL +#endif +#ifdef DOUBLEBAR_PARALLEL +#define LEX_IS_DOUBLEBAR_1ST 13 +#endif +#define LEX_IS_PARALLEL_SEPARATOR 14 +#ifdef H_TICK_HEX +#define LEX_IS_H 15 +#endif +#define IS_SYMBOL_COMPONENT(c) (lex[c] == LEX_IS_SYMBOL_COMPONENT) +#define IS_WHITESPACE(c) (lex[c] == LEX_IS_WHITESPACE) +#define IS_LINE_SEPARATOR(c) (lex[c] == LEX_IS_LINE_SEPARATOR) +#define IS_PARALLEL_SEPARATOR(c) (lex[c] == LEX_IS_PARALLEL_SEPARATOR) +#define IS_COMMENT(c) (lex[c] == LEX_IS_COMMENT_START) +#define IS_LINE_COMMENT(c) (lex[c] == LEX_IS_LINE_COMMENT_START) +#define IS_NEWLINE(c) (lex[c] == LEX_IS_NEWLINE) + +static int process_escape (int); + +/* FIXME-soon: The entire lexer/parser thingy should be + built statically at compile time rather than dynamically + each and every time the assembler is run. xoxorich. */ + +void +do_scrub_begin (int m68k_mri ATTRIBUTE_UNUSED) +{ + const char *p; + int c; + + lex[' '] = LEX_IS_WHITESPACE; + lex['\t'] = LEX_IS_WHITESPACE; + lex['\r'] = LEX_IS_WHITESPACE; + lex['\n'] = LEX_IS_NEWLINE; + lex[':'] = LEX_IS_COLON; + +#ifdef TC_M68K + scrub_m68k_mri = m68k_mri; + + if (! m68k_mri) +#endif + { + lex['"'] = LEX_IS_STRINGQUOTE; + +#if ! defined (TC_HPPA) && ! defined (TC_I370) + /* I370 uses single-quotes to delimit integer, float constants. */ + lex['\''] = LEX_IS_ONECHAR_QUOTE; +#endif + +#ifdef SINGLE_QUOTE_STRINGS + lex['\''] = LEX_IS_STRINGQUOTE; +#endif + } + + /* Note: if any other character can be LEX_IS_STRINGQUOTE, the loop + in state 5 of do_scrub_chars must be changed. */ + + /* Note that these override the previous defaults, e.g. if ';' is a + comment char, then it isn't a line separator. */ + for (p = symbol_chars; *p; ++p) + lex[(unsigned char) *p] = LEX_IS_SYMBOL_COMPONENT; + + for (c = 128; c < 256; ++c) + lex[c] = LEX_IS_SYMBOL_COMPONENT; + +#ifdef tc_symbol_chars + /* This macro permits the processor to specify all characters which + may appears in an operand. This will prevent the scrubber from + discarding meaningful whitespace in certain cases. The i386 + backend uses this to support prefixes, which can confuse the + scrubber as to whether it is parsing operands or opcodes. */ + for (p = tc_symbol_chars; *p; ++p) + lex[(unsigned char) *p] = LEX_IS_SYMBOL_COMPONENT; +#endif + + /* The m68k backend wants to be able to change comment_chars. */ +#ifndef tc_comment_chars +#define tc_comment_chars comment_chars +#endif + for (p = tc_comment_chars; *p; p++) + lex[(unsigned char) *p] = LEX_IS_COMMENT_START; + + for (p = line_comment_chars; *p; p++) + lex[(unsigned char) *p] = LEX_IS_LINE_COMMENT_START; + + for (p = line_separator_chars; *p; p++) + lex[(unsigned char) *p] = LEX_IS_LINE_SEPARATOR; + +#ifdef tc_parallel_separator_chars + /* This macro permits the processor to specify all characters which + separate parallel insns on the same line. */ + for (p = tc_parallel_separator_chars; *p; p++) + lex[(unsigned char) *p] = LEX_IS_PARALLEL_SEPARATOR; +#endif + + /* Only allow slash-star comments if slash is not in use. + FIXME: This isn't right. We should always permit them. */ + if (lex['/'] == 0) + lex['/'] = LEX_IS_TWOCHAR_COMMENT_1ST; + +#ifdef TC_M68K + if (m68k_mri) + { + lex['\''] = LEX_IS_STRINGQUOTE; + lex[';'] = LEX_IS_COMMENT_START; + lex['*'] = LEX_IS_LINE_COMMENT_START; + /* The MRI documentation says '!' is LEX_IS_COMMENT_START, but + then it can't be used in an expression. */ + lex['!'] = LEX_IS_LINE_COMMENT_START; + } +#endif + +#ifdef TC_V850 + lex['-'] = LEX_IS_DOUBLEDASH_1ST; +#endif +#ifdef DOUBLEBAR_PARALLEL + lex['|'] = LEX_IS_DOUBLEBAR_1ST; +#endif +#ifdef TC_D30V + /* Must do this is we want VLIW instruction with "->" or "<-". */ + lex['-'] = LEX_IS_SYMBOL_COMPONENT; +#endif + +#ifdef H_TICK_HEX + if (enable_h_tick_hex) + { + lex['h'] = LEX_IS_H; + lex['H'] = LEX_IS_H; + } +#endif +} + +/* Saved state of the scrubber. */ +static int state; +static int old_state; +static char *out_string; +static char out_buf[20]; +static int add_newlines; +static char *saved_input; +static size_t saved_input_len; +static char input_buffer[32 * 1024]; +static const char *mri_state; +static char mri_last_ch; + +/* Data structure for saving the state of app across #include's. Note that + app is called asynchronously to the parsing of the .include's, so our + state at the time .include is interpreted is completely unrelated. + That's why we have to save it all. */ + +struct app_save +{ + int state; + int old_state; + char * out_string; + char out_buf[sizeof (out_buf)]; + int add_newlines; + char * saved_input; + size_t saved_input_len; +#ifdef TC_M68K + int scrub_m68k_mri; +#endif + const char * mri_state; + char mri_last_ch; +#if defined TC_ARM && defined OBJ_ELF + const char * symver_state; +#endif +}; + +char * +app_push (void) +{ + register struct app_save *saved; + + saved = (struct app_save *) xmalloc (sizeof (*saved)); + saved->state = state; + saved->old_state = old_state; + saved->out_string = out_string; + memcpy (saved->out_buf, out_buf, sizeof (out_buf)); + saved->add_newlines = add_newlines; + if (saved_input == NULL) + saved->saved_input = NULL; + else + { + saved->saved_input = (char *) xmalloc (saved_input_len); + memcpy (saved->saved_input, saved_input, saved_input_len); + saved->saved_input_len = saved_input_len; + } +#ifdef TC_M68K + saved->scrub_m68k_mri = scrub_m68k_mri; +#endif + saved->mri_state = mri_state; + saved->mri_last_ch = mri_last_ch; +#if defined TC_ARM && defined OBJ_ELF + saved->symver_state = symver_state; +#endif + + /* do_scrub_begin() is not useful, just wastes time. */ + + state = 0; + saved_input = NULL; + add_newlines = 0; + + return (char *) saved; +} + +void +app_pop (char *arg) +{ + register struct app_save *saved = (struct app_save *) arg; + + /* There is no do_scrub_end (). */ + state = saved->state; + old_state = saved->old_state; + out_string = saved->out_string; + memcpy (out_buf, saved->out_buf, sizeof (out_buf)); + add_newlines = saved->add_newlines; + if (saved->saved_input == NULL) + saved_input = NULL; + else + { + gas_assert (saved->saved_input_len <= sizeof (input_buffer)); + memcpy (input_buffer, saved->saved_input, saved->saved_input_len); + saved_input = input_buffer; + saved_input_len = saved->saved_input_len; + free (saved->saved_input); + } +#ifdef TC_M68K + scrub_m68k_mri = saved->scrub_m68k_mri; +#endif + mri_state = saved->mri_state; + mri_last_ch = saved->mri_last_ch; +#if defined TC_ARM && defined OBJ_ELF + symver_state = saved->symver_state; +#endif + + free (arg); +} + +/* @@ This assumes that \n &c are the same on host and target. This is not + necessarily true. */ + +static int +process_escape (int ch) +{ + switch (ch) + { + case 'b': + return '\b'; + case 'f': + return '\f'; + case 'n': + return '\n'; + case 'r': + return '\r'; + case 't': + return '\t'; + case '\'': + return '\''; + case '"': + return '\"'; + default: + return ch; + } +} + +/* This function is called to process input characters. The GET + parameter is used to retrieve more input characters. GET should + set its parameter to point to a buffer, and return the length of + the buffer; it should return 0 at end of file. The scrubbed output + characters are put into the buffer starting at TOSTART; the TOSTART + buffer is TOLEN bytes in length. The function returns the number + of scrubbed characters put into TOSTART. This will be TOLEN unless + end of file was seen. This function is arranged as a state + machine, and saves its state so that it may return at any point. + This is the way the old code used to work. */ + +size_t +do_scrub_chars (size_t (*get) (char *, size_t), char *tostart, size_t tolen) +{ + char *to = tostart; + char *toend = tostart + tolen; + char *from; + char *fromend; + size_t fromlen; + register int ch, ch2 = 0; + /* Character that started the string we're working on. */ + static char quotechar; + + /*State 0: beginning of normal line + 1: After first whitespace on line (flush more white) + 2: After first non-white (opcode) on line (keep 1white) + 3: after second white on line (into operands) (flush white) + 4: after putting out a .linefile, put out digits + 5: parsing a string, then go to old-state + 6: putting out \ escape in a "d string. + 7: no longer used + 8: no longer used + 9: After seeing symbol char in state 3 (keep 1white after symchar) + 10: After seeing whitespace in state 9 (keep white before symchar) + 11: After seeing a symbol character in state 0 (eg a label definition) + -1: output string in out_string and go to the state in old_state + -2: flush text until a '*' '/' is seen, then go to state old_state +#ifdef TC_V850 + 12: After seeing a dash, looking for a second dash as a start + of comment. +#endif +#ifdef DOUBLEBAR_PARALLEL + 13: After seeing a vertical bar, looking for a second + vertical bar as a parallel expression separator. +#endif +#ifdef TC_PREDICATE_START_CHAR + 14: After seeing a predicate start character at state 0, looking + for a predicate end character as predicate. + 15: After seeing a predicate start character at state 1, looking + for a predicate end character as predicate. +#endif +#ifdef TC_Z80 + 16: After seeing an 'a' or an 'A' at the start of a symbol + 17: After seeing an 'f' or an 'F' in state 16 +#endif + */ + + /* I added states 9 and 10 because the MIPS ECOFF assembler uses + constructs like ``.loc 1 20''. This was turning into ``.loc + 120''. States 9 and 10 ensure that a space is never dropped in + between characters which could appear in an identifier. Ian + Taylor, ian@cygnus.com. + + I added state 11 so that something like "Lfoo add %r25,%r26,%r27" works + correctly on the PA (and any other target where colons are optional). + Jeff Law, law@cs.utah.edu. + + I added state 13 so that something like "cmp r1, r2 || trap #1" does not + get squashed into "cmp r1,r2||trap#1", with the all important space + between the 'trap' and the '#1' being eliminated. nickc@cygnus.com */ + + /* This macro gets the next input character. */ + +#define GET() \ + (from < fromend \ + ? * (unsigned char *) (from++) \ + : (saved_input = NULL, \ + fromlen = (*get) (input_buffer, sizeof input_buffer), \ + from = input_buffer, \ + fromend = from + fromlen, \ + (fromlen == 0 \ + ? EOF \ + : * (unsigned char *) (from++)))) + + /* This macro pushes a character back on the input stream. */ + +#define UNGET(uch) (*--from = (uch)) + + /* This macro puts a character into the output buffer. If this + character fills the output buffer, this macro jumps to the label + TOFULL. We use this rather ugly approach because we need to + handle two different termination conditions: EOF on the input + stream, and a full output buffer. It would be simpler if we + always read in the entire input stream before processing it, but + I don't want to make such a significant change to the assembler's + memory usage. */ + +#define PUT(pch) \ + do \ + { \ + *to++ = (pch); \ + if (to >= toend) \ + goto tofull; \ + } \ + while (0) + + if (saved_input != NULL) + { + from = saved_input; + fromend = from + saved_input_len; + } + else + { + fromlen = (*get) (input_buffer, sizeof input_buffer); + if (fromlen == 0) + return 0; + from = input_buffer; + fromend = from + fromlen; + } + + while (1) + { + /* The cases in this switch end with continue, in order to + branch back to the top of this while loop and generate the + next output character in the appropriate state. */ + switch (state) + { + case -1: + ch = *out_string++; + if (*out_string == '\0') + { + state = old_state; + old_state = 3; + } + PUT (ch); + continue; + + case -2: + for (;;) + { + do + { + ch = GET (); + + if (ch == EOF) + { + as_warn (_("end of file in comment")); + goto fromeof; + } + + if (ch == '\n') + PUT ('\n'); + } + while (ch != '*'); + + while ((ch = GET ()) == '*') + ; + + if (ch == EOF) + { + as_warn (_("end of file in comment")); + goto fromeof; + } + + if (ch == '/') + break; + + UNGET (ch); + } + + state = old_state; + UNGET (' '); + continue; + + case 4: + ch = GET (); + if (ch == EOF) + goto fromeof; + else if (ch >= '0' && ch <= '9') + PUT (ch); + else + { + while (ch != EOF && IS_WHITESPACE (ch)) + ch = GET (); + if (ch == '"') + { + quotechar = ch; + state = 5; + old_state = 3; + PUT (ch); + } + else + { + while (ch != EOF && ch != '\n') + ch = GET (); + state = 0; + PUT (ch); + } + } + continue; + + case 5: + /* We are going to copy everything up to a quote character, + with special handling for a backslash. We try to + optimize the copying in the simple case without using the + GET and PUT macros. */ + { + char *s; + ptrdiff_t len; + + for (s = from; s < fromend; s++) + { + ch = *s; + if (ch == '\\' + || ch == quotechar + || ch == '\n') + break; + } + len = s - from; + if (len > toend - to) + len = toend - to; + if (len > 0) + { + memcpy (to, from, len); + to += len; + from += len; + if (to >= toend) + goto tofull; + } + } + + ch = GET (); + if (ch == EOF) + { + /* This buffer is here specifically so + that the UNGET below will work. */ + static char one_char_buf[1]; + + as_warn (_("end of file in string; '%c' inserted"), quotechar); + state = old_state; + from = fromend = one_char_buf + 1; + fromlen = 1; + UNGET ('\n'); + PUT (quotechar); + } + else if (ch == quotechar) + { + state = old_state; + PUT (ch); + } +#ifndef NO_STRING_ESCAPES + else if (ch == '\\') + { + state = 6; + PUT (ch); + } +#endif + else if (scrub_m68k_mri && ch == '\n') + { + /* Just quietly terminate the string. This permits lines like + bne label loop if we haven't reach end yet. */ + state = old_state; + UNGET (ch); + PUT ('\''); + } + else + { + PUT (ch); + } + continue; + + case 6: + state = 5; + ch = GET (); + switch (ch) + { + /* Handle strings broken across lines, by turning '\n' into + '\\' and 'n'. */ + case '\n': + UNGET ('n'); + add_newlines++; + PUT ('\\'); + continue; + + case EOF: + as_warn (_("end of file in string; '%c' inserted"), quotechar); + PUT (quotechar); + continue; + + case '"': + case '\\': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + case 'x': + case 'X': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + break; + + default: +#ifdef ONLY_STANDARD_ESCAPES + as_warn (_("unknown escape '\\%c' in string; ignored"), ch); +#endif + break; + } + PUT (ch); + continue; + +#ifdef DOUBLEBAR_PARALLEL + case 13: + ch = GET (); + if (ch != '|') + abort (); + + /* Reset back to state 1 and pretend that we are parsing a + line from just after the first white space. */ + state = 1; + PUT ('|'); +#ifdef TC_TIC6X + /* "||^" is used for SPMASKed instructions. */ + ch = GET (); + if (ch == EOF) + goto fromeof; + else if (ch == '^') + PUT ('^'); + else + UNGET (ch); +#endif + continue; +#endif +#ifdef TC_Z80 + case 16: + /* We have seen an 'a' at the start of a symbol, look for an 'f'. */ + ch = GET (); + if (ch == 'f' || ch == 'F') + { + state = 17; + PUT (ch); + } + else + { + state = 9; + break; + } + case 17: + /* We have seen "af" at the start of a symbol, + a ' here is a part of that symbol. */ + ch = GET (); + state = 9; + if (ch == '\'') + /* Change to avoid warning about unclosed string. */ + PUT ('`'); + else if (ch != EOF) + UNGET (ch); + break; +#endif + } + + /* OK, we are somewhere in states 0 through 4 or 9 through 11. */ + + /* flushchar: */ + ch = GET (); + +#ifdef TC_PREDICATE_START_CHAR + if (ch == TC_PREDICATE_START_CHAR && (state == 0 || state == 1)) + { + state += 14; + PUT (ch); + continue; + } + else if (state == 14 || state == 15) + { + if (ch == TC_PREDICATE_END_CHAR) + { + state -= 14; + PUT (ch); + ch = GET (); + } + else + { + PUT (ch); + continue; + } + } +#endif + + recycle: + +#if defined TC_ARM && defined OBJ_ELF + /* We need to watch out for .symver directives. See the comment later + in this function. */ + if (symver_state == NULL) + { + if ((state == 0 || state == 1) && ch == symver_pseudo[0]) + symver_state = symver_pseudo + 1; + } + else + { + /* We advance to the next state if we find the right + character. */ + if (ch != '\0' && (*symver_state == ch)) + ++symver_state; + else if (*symver_state != '\0') + /* We did not get the expected character, or we didn't + get a valid terminating character after seeing the + entire pseudo-op, so we must go back to the beginning. */ + symver_state = NULL; + else + { + /* We've read the entire pseudo-op. If this is the end + of the line, go back to the beginning. */ + if (IS_NEWLINE (ch)) + symver_state = NULL; + } + } +#endif /* TC_ARM && OBJ_ELF */ + +#ifdef TC_M68K + /* We want to have pseudo-ops which control whether we are in + MRI mode or not. Unfortunately, since m68k MRI mode affects + the scrubber, that means that we need a special purpose + recognizer here. */ + if (mri_state == NULL) + { + if ((state == 0 || state == 1) + && ch == mri_pseudo[0]) + mri_state = mri_pseudo + 1; + } + else + { + /* We advance to the next state if we find the right + character, or if we need a space character and we get any + whitespace character, or if we need a '0' and we get a + '1' (this is so that we only need one state to handle + ``.mri 0'' and ``.mri 1''). */ + if (ch != '\0' + && (*mri_state == ch + || (*mri_state == ' ' + && lex[ch] == LEX_IS_WHITESPACE) + || (*mri_state == '0' + && ch == '1'))) + { + mri_last_ch = ch; + ++mri_state; + } + else if (*mri_state != '\0' + || (lex[ch] != LEX_IS_WHITESPACE + && lex[ch] != LEX_IS_NEWLINE)) + { + /* We did not get the expected character, or we didn't + get a valid terminating character after seeing the + entire pseudo-op, so we must go back to the + beginning. */ + mri_state = NULL; + } + else + { + /* We've read the entire pseudo-op. mips_last_ch is + either '0' or '1' indicating whether to enter or + leave MRI mode. */ + do_scrub_begin (mri_last_ch == '1'); + mri_state = NULL; + + /* We continue handling the character as usual. The + main gas reader must also handle the .mri pseudo-op + to control expression parsing and the like. */ + } + } +#endif + + if (ch == EOF) + { + if (state != 0) + { + as_warn (_("end of file not at end of a line; newline inserted")); + state = 0; + PUT ('\n'); + } + goto fromeof; + } + + switch (lex[ch]) + { + case LEX_IS_WHITESPACE: + do + { + ch = GET (); + } + while (ch != EOF && IS_WHITESPACE (ch)); + if (ch == EOF) + goto fromeof; + + if (state == 0) + { + /* Preserve a single whitespace character at the + beginning of a line. */ + state = 1; + UNGET (ch); + PUT (' '); + break; + } + +#ifdef KEEP_WHITE_AROUND_COLON + if (lex[ch] == LEX_IS_COLON) + { + /* Only keep this white if there's no white *after* the + colon. */ + ch2 = GET (); + if (ch2 != EOF) + UNGET (ch2); + if (!IS_WHITESPACE (ch2)) + { + state = 9; + UNGET (ch); + PUT (' '); + break; + } + } +#endif + if (IS_COMMENT (ch) + || ch == '/' + || IS_LINE_SEPARATOR (ch) + || IS_PARALLEL_SEPARATOR (ch)) + { + if (scrub_m68k_mri) + { + /* In MRI mode, we keep these spaces. */ + UNGET (ch); + PUT (' '); + break; + } + goto recycle; + } + + /* If we're in state 2 or 11, we've seen a non-white + character followed by whitespace. If the next character + is ':', this is whitespace after a label name which we + normally must ignore. In MRI mode, though, spaces are + not permitted between the label and the colon. */ + if ((state == 2 || state == 11) + && lex[ch] == LEX_IS_COLON + && ! scrub_m68k_mri) + { + state = 1; + PUT (ch); + break; + } + + switch (state) + { + case 1: + /* We can arrive here if we leave a leading whitespace + character at the beginning of a line. */ + goto recycle; + case 2: + state = 3; + if (to + 1 < toend) + { + /* Optimize common case by skipping UNGET/GET. */ + PUT (' '); /* Sp after opco */ + goto recycle; + } + UNGET (ch); + PUT (' '); + break; + case 3: +#ifndef TC_KEEP_OPERAND_SPACES + /* For TI C6X, we keep these spaces as they may separate + functional unit specifiers from operands. */ + if (scrub_m68k_mri) +#endif + { + /* In MRI mode, we keep these spaces. */ + UNGET (ch); + PUT (' '); + break; + } + goto recycle; /* Sp in operands */ + case 9: + case 10: +#ifndef TC_KEEP_OPERAND_SPACES + if (scrub_m68k_mri) +#endif + { + /* In MRI mode, we keep these spaces. */ + state = 3; + UNGET (ch); + PUT (' '); + break; + } + state = 10; /* Sp after symbol char */ + goto recycle; + case 11: + if (LABELS_WITHOUT_COLONS || flag_m68k_mri) + state = 1; + else + { + /* We know that ch is not ':', since we tested that + case above. Therefore this is not a label, so it + must be the opcode, and we've just seen the + whitespace after it. */ + state = 3; + } + UNGET (ch); + PUT (' '); /* Sp after label definition. */ + break; + default: + BAD_CASE (state); + } + break; + + case LEX_IS_TWOCHAR_COMMENT_1ST: + ch2 = GET (); + if (ch2 == '*') + { + for (;;) + { + do + { + ch2 = GET (); + if (ch2 != EOF && IS_NEWLINE (ch2)) + add_newlines++; + } + while (ch2 != EOF && ch2 != '*'); + + while (ch2 == '*') + ch2 = GET (); + + if (ch2 == EOF || ch2 == '/') + break; + + /* This UNGET will ensure that we count newlines + correctly. */ + UNGET (ch2); + } + + if (ch2 == EOF) + as_warn (_("end of file in multiline comment")); + + ch = ' '; + goto recycle; + } +#ifdef DOUBLESLASH_LINE_COMMENTS + else if (ch2 == '/') + { + do + { + ch = GET (); + } + while (ch != EOF && !IS_NEWLINE (ch)); + if (ch == EOF) + as_warn ("end of file in comment; newline inserted"); + state = 0; + PUT ('\n'); + break; + } +#endif + else + { + if (ch2 != EOF) + UNGET (ch2); + if (state == 9 || state == 10) + state = 3; + PUT (ch); + } + break; + + case LEX_IS_STRINGQUOTE: + quotechar = ch; + if (state == 10) + { + /* Preserve the whitespace in foo "bar". */ + UNGET (ch); + state = 3; + PUT (' '); + + /* PUT didn't jump out. We could just break, but we + know what will happen, so optimize a bit. */ + ch = GET (); + old_state = 3; + } + else if (state == 9) + old_state = 3; + else + old_state = state; + state = 5; + PUT (ch); + break; + +#ifndef IEEE_STYLE + case LEX_IS_ONECHAR_QUOTE: +#ifdef H_TICK_HEX + if (state == 9 && enable_h_tick_hex) + { + char c; + + c = GET (); + as_warn ("'%c found after symbol", c); + UNGET (c); + } +#endif + if (state == 10) + { + /* Preserve the whitespace in foo 'b'. */ + UNGET (ch); + state = 3; + PUT (' '); + break; + } + ch = GET (); + if (ch == EOF) + { + as_warn (_("end of file after a one-character quote; \\0 inserted")); + ch = 0; + } + if (ch == '\\') + { + ch = GET (); + if (ch == EOF) + { + as_warn (_("end of file in escape character")); + ch = '\\'; + } + else + ch = process_escape (ch); + } + sprintf (out_buf, "%d", (int) (unsigned char) ch); + + /* None of these 'x constants for us. We want 'x'. */ + if ((ch = GET ()) != '\'') + { +#ifdef REQUIRE_CHAR_CLOSE_QUOTE + as_warn (_("missing close quote; (assumed)")); +#else + if (ch != EOF) + UNGET (ch); +#endif + } + if (strlen (out_buf) == 1) + { + PUT (out_buf[0]); + break; + } + if (state == 9) + old_state = 3; + else + old_state = state; + state = -1; + out_string = out_buf; + PUT (*out_string++); + break; +#endif + + case LEX_IS_COLON: +#ifdef KEEP_WHITE_AROUND_COLON + state = 9; +#else + if (state == 9 || state == 10) + state = 3; + else if (state != 3) + state = 1; +#endif + PUT (ch); + break; + + case LEX_IS_NEWLINE: + /* Roll out a bunch of newlines from inside comments, etc. */ + if (add_newlines) + { + --add_newlines; + UNGET (ch); + } + /* Fall through. */ + + case LEX_IS_LINE_SEPARATOR: + state = 0; + PUT (ch); + break; + + case LEX_IS_PARALLEL_SEPARATOR: + state = 1; + PUT (ch); + break; + +#ifdef TC_V850 + case LEX_IS_DOUBLEDASH_1ST: + ch2 = GET (); + if (ch2 != '-') + { + if (ch2 != EOF) + UNGET (ch2); + goto de_fault; + } + /* Read and skip to end of line. */ + do + { + ch = GET (); + } + while (ch != EOF && ch != '\n'); + + if (ch == EOF) + as_warn (_("end of file in comment; newline inserted")); + + state = 0; + PUT ('\n'); + break; +#endif +#ifdef DOUBLEBAR_PARALLEL + case LEX_IS_DOUBLEBAR_1ST: + ch2 = GET (); + if (ch2 != EOF) + UNGET (ch2); + if (ch2 != '|') + goto de_fault; + + /* Handle '||' in two states as invoking PUT twice might + result in the first one jumping out of this loop. We'd + then lose track of the state and one '|' char. */ + state = 13; + PUT ('|'); + break; +#endif + case LEX_IS_LINE_COMMENT_START: + /* FIXME-someday: The two character comment stuff was badly + thought out. On i386, we want '/' as line comment start + AND we want C style comments. hence this hack. The + whole lexical process should be reworked. xoxorich. */ + if (ch == '/') + { + ch2 = GET (); + if (ch2 == '*') + { + old_state = 3; + state = -2; + break; + } + else + { + UNGET (ch2); + } + } + + if (state == 0 || state == 1) /* Only comment at start of line. */ + { + int startch; + + startch = ch; + + do + { + ch = GET (); + } + while (ch != EOF && IS_WHITESPACE (ch)); + + if (ch == EOF) + { + as_warn (_("end of file in comment; newline inserted")); + PUT ('\n'); + break; + } + + if (ch < '0' || ch > '9' || state != 0 || startch != '#') + { + /* Not a cpp line. */ + while (ch != EOF && !IS_NEWLINE (ch)) + ch = GET (); + if (ch == EOF) + as_warn (_("end of file in comment; newline inserted")); + state = 0; + PUT ('\n'); + break; + } + /* Looks like `# 123 "filename"' from cpp. */ + UNGET (ch); + old_state = 4; + state = -1; + if (scrub_m68k_mri) + out_string = "\tlinefile "; + else + out_string = "\t.linefile "; + PUT (*out_string++); + break; + } + +#ifdef TC_D10V + /* All insns end in a char for which LEX_IS_SYMBOL_COMPONENT is true. + Trap is the only short insn that has a first operand that is + neither register nor label. + We must prevent exef0f ||trap #1 to degenerate to exef0f ||trap#1 . + We can't make '#' LEX_IS_SYMBOL_COMPONENT because it is + already LEX_IS_LINE_COMMENT_START. However, it is the + only character in line_comment_chars for d10v, hence we + can recognize it as such. */ + /* An alternative approach would be to reset the state to 1 when + we see '||', '<'- or '->', but that seems to be overkill. */ + if (state == 10) + PUT (' '); +#endif + /* We have a line comment character which is not at the + start of a line. If this is also a normal comment + character, fall through. Otherwise treat it as a default + character. */ + if (strchr (tc_comment_chars, ch) == NULL + && (! scrub_m68k_mri + || (ch != '!' && ch != '*'))) + goto de_fault; + if (scrub_m68k_mri + && (ch == '!' || ch == '*' || ch == '#') + && state != 1 + && state != 10) + goto de_fault; + /* Fall through. */ + case LEX_IS_COMMENT_START: +#if defined TC_ARM && defined OBJ_ELF + /* On the ARM, `@' is the comment character. + Unfortunately this is also a special character in ELF .symver + directives (and .type, though we deal with those another way). + So we check if this line is such a directive, and treat + the character as default if so. This is a hack. */ + if ((symver_state != NULL) && (*symver_state == 0)) + goto de_fault; +#endif + +#ifdef TC_ARM + /* For the ARM, care is needed not to damage occurrences of \@ + by stripping the @ onwards. Yuck. */ + if (to > tostart && *(to - 1) == '\\') + /* Do not treat the @ as a start-of-comment. */ + goto de_fault; +#endif + +#ifdef WARN_COMMENTS + if (!found_comment) + as_where (&found_comment_file, &found_comment); +#endif + do + { + ch = GET (); + } + while (ch != EOF && !IS_NEWLINE (ch)); + if (ch == EOF) + as_warn (_("end of file in comment; newline inserted")); + state = 0; + PUT ('\n'); + break; + +#ifdef H_TICK_HEX + case LEX_IS_H: + /* Look for strings like H'[0-9A-Fa-f] and if found, replace + the H' with 0x to make them gas-style hex characters. */ + if (enable_h_tick_hex) + { + char quot; + + quot = GET (); + if (quot == '\'') + { + UNGET ('x'); + ch = '0'; + } + else + UNGET (quot); + } + /* FALL THROUGH */ +#endif + + case LEX_IS_SYMBOL_COMPONENT: + if (state == 10) + { + /* This is a symbol character following another symbol + character, with whitespace in between. We skipped + the whitespace earlier, so output it now. */ + UNGET (ch); + state = 3; + PUT (' '); + break; + } + +#ifdef TC_Z80 + /* "af'" is a symbol containing '\''. */ + if (state == 3 && (ch == 'a' || ch == 'A')) + { + state = 16; + PUT (ch); + ch = GET (); + if (ch == 'f' || ch == 'F') + { + state = 17; + PUT (ch); + break; + } + else + { + state = 9; + if (ch == EOF || !IS_SYMBOL_COMPONENT (ch)) + { + if (ch != EOF) + UNGET (ch); + break; + } + } + } +#endif + if (state == 3) + state = 9; + + /* This is a common case. Quickly copy CH and all the + following symbol component or normal characters. */ + if (to + 1 < toend + && mri_state == NULL +#if defined TC_ARM && defined OBJ_ELF + && symver_state == NULL +#endif + ) + { + char *s; + ptrdiff_t len; + + for (s = from; s < fromend; s++) + { + int type; + + ch2 = *(unsigned char *) s; + type = lex[ch2]; + if (type != 0 + && type != LEX_IS_SYMBOL_COMPONENT) + break; + } + + if (s > from) + /* Handle the last character normally, for + simplicity. */ + --s; + + len = s - from; + + if (len > (toend - to) - 1) + len = (toend - to) - 1; + + if (len > 0) + { + PUT (ch); + memcpy (to, from, len); + to += len; + from += len; + if (to >= toend) + goto tofull; + ch = GET (); + } + } + + /* Fall through. */ + default: + de_fault: + /* Some relatively `normal' character. */ + if (state == 0) + { + state = 11; /* Now seeing label definition. */ + } + else if (state == 1) + { + state = 2; /* Ditto. */ + } + else if (state == 9) + { + if (!IS_SYMBOL_COMPONENT (ch)) + state = 3; + } + else if (state == 10) + { + if (ch == '\\') + { + /* Special handling for backslash: a backslash may + be the beginning of a formal parameter (of a + macro) following another symbol character, with + whitespace in between. If that is the case, we + output a space before the parameter. Strictly + speaking, correct handling depends upon what the + macro parameter expands into; if the parameter + expands into something which does not start with + an operand character, then we don't want to keep + the space. We don't have enough information to + make the right choice, so here we are making the + choice which is more likely to be correct. */ + if (to + 1 >= toend) + { + /* If we're near the end of the buffer, save the + character for the next time round. Otherwise + we'll lose our state. */ + UNGET (ch); + goto tofull; + } + *to++ = ' '; + } + + state = 3; + } + PUT (ch); + break; + } + } + + /*NOTREACHED*/ + + fromeof: + /* We have reached the end of the input. */ + return to - tostart; + + tofull: + /* The output buffer is full. Save any input we have not yet + processed. */ + if (fromend > from) + { + saved_input = from; + saved_input_len = fromend - from; + } + else + saved_input = NULL; + + return to - tostart; +} diff --git a/contrib/toolchain/binutils/gas/as b/contrib/toolchain/binutils/gas/as new file mode 100644 index 0000000000..befff57d2b Binary files /dev/null and b/contrib/toolchain/binutils/gas/as differ diff --git a/contrib/toolchain/binutils/gas/as.c b/contrib/toolchain/binutils/gas/as.c new file mode 100644 index 0000000000..f198043f15 --- /dev/null +++ b/contrib/toolchain/binutils/gas/as.c @@ -0,0 +1,1326 @@ +/* as.c - GAS main program. + Copyright 1987-2013 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* Main program for AS; a 32-bit assembler of GNU. + Understands command arguments. + Has a few routines that don't fit in other modules because they + are shared. + + bugs + + : initialisers + Since no-one else says they will support them in future: I + don't support them now. */ + +#define COMMON + +#include "as.h" +#include "subsegs.h" +#include "output-file.h" +#include "sb.h" +#include "macro.h" +#include "dwarf2dbg.h" +#include "dw2gencfi.h" +#include "bfdver.h" + +#ifdef HAVE_ITBL_CPU +#include "itbl-ops.h" +#else +#define itbl_init() +#endif + +#ifdef HAVE_SBRK +#ifdef NEED_DECLARATION_SBRK +extern void *sbrk (); +#endif +#endif + +#ifdef USING_CGEN +/* Perform any cgen specific initialisation for gas. */ +extern void gas_cgen_begin (void); +#endif + +/* We build a list of defsyms as we read the options, and then define + them after we have initialized everything. */ +struct defsym_list +{ + struct defsym_list *next; + char *name; + valueT value; +}; + + +/* True if a listing is wanted. */ +int listing; + +/* Type of debugging to generate. */ +enum debug_info_type debug_type = DEBUG_UNSPECIFIED; +int use_gnu_debug_info_extensions = 0; + +#ifndef MD_DEBUG_FORMAT_SELECTOR +#define MD_DEBUG_FORMAT_SELECTOR NULL +#endif +static enum debug_info_type (*md_debug_format_selector) (int *) = MD_DEBUG_FORMAT_SELECTOR; + +/* Maximum level of macro nesting. */ +int max_macro_nest = 100; + +/* argv[0] */ +static char * myname; + +/* The default obstack chunk size. If we set this to zero, the + obstack code will use whatever will fit in a 4096 byte block. */ +int chunksize = 0; + +/* To monitor memory allocation more effectively, make this non-zero. + Then the chunk sizes for gas and bfd will be reduced. */ +int debug_memory = 0; + +/* Enable verbose mode. */ +int verbose = 0; + +/* Keep the output file. */ +int keep_it = 0; + +segT reg_section; +segT expr_section; +segT text_section; +segT data_section; +segT bss_section; + +/* Name of listing file. */ +static char *listing_filename = NULL; + +static struct defsym_list *defsyms; + +#ifdef HAVE_ITBL_CPU +/* Keep a record of the itbl files we read in. */ +struct itbl_file_list +{ + struct itbl_file_list *next; + char *name; +}; +static struct itbl_file_list *itbl_files; +#endif + +static long start_time; +#ifdef HAVE_SBRK +char *start_sbrk; +#endif + +static int flag_macro_alternate; + + +#ifdef USE_EMULATIONS +#define EMULATION_ENVIRON "AS_EMULATION" + +extern struct emulation mipsbelf, mipslelf, mipself; +extern struct emulation i386coff, i386elf, i386aout; +extern struct emulation crisaout, criself; + +static struct emulation *const emulations[] = { EMULATIONS }; +static const int n_emulations = sizeof (emulations) / sizeof (emulations[0]); + +static void +select_emulation_mode (int argc, char **argv) +{ + int i; + char *p, *em = 0; + + for (i = 1; i < argc; i++) + if (!strncmp ("--em", argv[i], 4)) + break; + + if (i == argc) + goto do_default; + + p = strchr (argv[i], '='); + if (p) + p++; + else + p = argv[i + 1]; + + if (!p || !*p) + as_fatal (_("missing emulation mode name")); + em = p; + + do_default: + if (em == 0) + em = getenv (EMULATION_ENVIRON); + if (em == 0) + em = DEFAULT_EMULATION; + + if (em) + { + for (i = 0; i < n_emulations; i++) + if (!strcmp (emulations[i]->name, em)) + break; + if (i == n_emulations) + as_fatal (_("unrecognized emulation name `%s'"), em); + this_emulation = emulations[i]; + } + else + this_emulation = emulations[0]; + + this_emulation->init (); +} + +const char * +default_emul_bfd_name (void) +{ + abort (); + return NULL; +} + +void +common_emul_init (void) +{ + this_format = this_emulation->format; + + if (this_emulation->leading_underscore == 2) + this_emulation->leading_underscore = this_format->dfl_leading_underscore; + + if (this_emulation->default_endian != 2) + target_big_endian = this_emulation->default_endian; + + if (this_emulation->fake_label_name == 0) + { + if (this_emulation->leading_underscore) + this_emulation->fake_label_name = "L0\001"; + else + /* What other parameters should we test? */ + this_emulation->fake_label_name = ".L0\001"; + } +} +#endif + +void +print_version_id (void) +{ + static int printed; + + if (printed) + return; + printed = 1; + + fprintf (stderr, _("GNU assembler version %s (%s) using BFD version %s\n"), + VERSION, TARGET_ALIAS, BFD_VERSION_STRING); +} + +static void +show_usage (FILE * stream) +{ + fprintf (stream, _("Usage: %s [option...] [asmfile...]\n"), myname); + + fprintf (stream, _("\ +Options:\n\ + -a[sub-option...] turn on listings\n\ + Sub-options [default hls]:\n\ + c omit false conditionals\n\ + d omit debugging directives\n\ + g include general info\n\ + h include high-level source\n\ + l include assembly\n\ + m include macro expansions\n\ + n omit forms processing\n\ + s include symbols\n\ + =FILE list to FILE (must be last sub-option)\n")); + + fprintf (stream, _("\ + --alternate initially turn on alternate macro syntax\n")); +#ifdef HAVE_ZLIB_H + fprintf (stream, _("\ + --compress-debug-sections\n\ + compress DWARF debug sections using zlib\n")); + fprintf (stream, _("\ + --nocompress-debug-sections\n\ + don't compress DWARF debug sections\n")); +#endif /* HAVE_ZLIB_H */ + fprintf (stream, _("\ + -D produce assembler debugging messages\n")); + fprintf (stream, _("\ + --debug-prefix-map OLD=NEW\n\ + map OLD to NEW in debug information\n")); + fprintf (stream, _("\ + --defsym SYM=VAL define symbol SYM to given value\n")); +#ifdef USE_EMULATIONS + { + int i; + char *def_em; + + fprintf (stream, "\ + --em=["); + for (i = 0; i < n_emulations - 1; i++) + fprintf (stream, "%s | ", emulations[i]->name); + fprintf (stream, "%s]\n", emulations[i]->name); + + def_em = getenv (EMULATION_ENVIRON); + if (!def_em) + def_em = DEFAULT_EMULATION; + fprintf (stream, _("\ + emulate output (default %s)\n"), def_em); + } +#endif +#if defined OBJ_ELF || defined OBJ_MAYBE_ELF + fprintf (stream, _("\ + --execstack require executable stack for this object\n")); + fprintf (stream, _("\ + --noexecstack don't require executable stack for this object\n")); + fprintf (stream, _("\ + --size-check=[error|warning]\n\ + ELF .size directive check (default --size-check=error)\n")); +#endif + fprintf (stream, _("\ + -f skip whitespace and comment preprocessing\n")); + fprintf (stream, _("\ + -g --gen-debug generate debugging information\n")); + fprintf (stream, _("\ + --gstabs generate STABS debugging information\n")); + fprintf (stream, _("\ + --gstabs+ generate STABS debug info with GNU extensions\n")); + fprintf (stream, _("\ + --gdwarf-2 generate DWARF2 debugging information\n")); + fprintf (stream, _("\ + --gdwarf-sections generate per-function section names for DWARF line information\n")); + fprintf (stream, _("\ + --hash-size= set the hash table size close to \n")); + fprintf (stream, _("\ + --help show this message and exit\n")); + fprintf (stream, _("\ + --target-help show target specific options\n")); + fprintf (stream, _("\ + -I DIR add DIR to search list for .include directives\n")); + fprintf (stream, _("\ + -J don't warn about signed overflow\n")); + fprintf (stream, _("\ + -K warn when differences altered for long displacements\n")); + fprintf (stream, _("\ + -L,--keep-locals keep local symbols (e.g. starting with `L')\n")); + fprintf (stream, _("\ + -M,--mri assemble in MRI compatibility mode\n")); + fprintf (stream, _("\ + --MD FILE write dependency information in FILE (default none)\n")); + fprintf (stream, _("\ + -nocpp ignored\n")); + fprintf (stream, _("\ + -o OBJFILE name the object-file output OBJFILE (default a.out)\n")); + fprintf (stream, _("\ + -R fold data section into text section\n")); + fprintf (stream, _("\ + --reduce-memory-overheads \n\ + prefer smaller memory use at the cost of longer\n\ + assembly times\n")); + fprintf (stream, _("\ + --statistics print various measured statistics from execution\n")); + fprintf (stream, _("\ + --strip-local-absolute strip local absolute symbols\n")); + fprintf (stream, _("\ + --traditional-format Use same format as native assembler when possible\n")); + fprintf (stream, _("\ + --version print assembler version number and exit\n")); + fprintf (stream, _("\ + -W --no-warn suppress warnings\n")); + fprintf (stream, _("\ + --warn don't suppress warnings\n")); + fprintf (stream, _("\ + --fatal-warnings treat warnings as errors\n")); +#ifdef HAVE_ITBL_CPU + fprintf (stream, _("\ + --itbl INSTTBL extend instruction set to include instructions\n\ + matching the specifications defined in file INSTTBL\n")); +#endif + fprintf (stream, _("\ + -w ignored\n")); + fprintf (stream, _("\ + -X ignored\n")); + fprintf (stream, _("\ + -Z generate object file even after errors\n")); + fprintf (stream, _("\ + --listing-lhs-width set the width in words of the output data column of\n\ + the listing\n")); + fprintf (stream, _("\ + --listing-lhs-width2 set the width in words of the continuation lines\n\ + of the output data column; ignored if smaller than\n\ + the width of the first line\n")); + fprintf (stream, _("\ + --listing-rhs-width set the max width in characters of the lines from\n\ + the source file\n")); + fprintf (stream, _("\ + --listing-cont-lines set the maximum number of continuation lines used\n\ + for the output data column of the listing\n")); + fprintf (stream, _("\ + @FILE read options from FILE\n")); + + md_show_usage (stream); + + fputc ('\n', stream); + + if (REPORT_BUGS_TO[0] && stream == stdout) + fprintf (stream, _("Report bugs to %s\n"), REPORT_BUGS_TO); +} + +/* Since it is easy to do here we interpret the special arg "-" + to mean "use stdin" and we set that argv[] pointing to "". + After we have munged argv[], the only things left are source file + name(s) and ""(s) denoting stdin. These file names are used + (perhaps more than once) later. + + check for new machine-dep cmdline options in + md_parse_option definitions in config/tc-*.c. */ + +static void +parse_args (int * pargc, char *** pargv) +{ + int old_argc; + int new_argc; + char ** old_argv; + char ** new_argv; + /* Starting the short option string with '-' is for programs that + expect options and other ARGV-elements in any order and that care about + the ordering of the two. We describe each non-option ARGV-element + as if it were the argument of an option with character code 1. */ + char *shortopts; + extern const char *md_shortopts; + static const char std_shortopts[] = + { + '-', 'J', +#ifndef WORKING_DOT_WORD + /* -K is not meaningful if .word is not being hacked. */ + 'K', +#endif + 'L', 'M', 'R', 'W', 'Z', 'a', ':', ':', 'D', 'f', 'g', ':',':', 'I', ':', 'o', ':', +#ifndef VMS + /* -v takes an argument on VMS, so we don't make it a generic + option. */ + 'v', +#endif + 'w', 'X', +#ifdef HAVE_ITBL_CPU + /* New option for extending instruction set (see also --itbl below). */ + 't', ':', +#endif + '\0' + }; + struct option *longopts; + extern struct option md_longopts[]; + extern size_t md_longopts_size; + /* Codes used for the long options with no short synonyms. */ + enum option_values + { + OPTION_HELP = OPTION_STD_BASE, + OPTION_NOCPP, + OPTION_STATISTICS, + OPTION_VERSION, + OPTION_DUMPCONFIG, + OPTION_VERBOSE, + OPTION_EMULATION, + OPTION_DEBUG_PREFIX_MAP, + OPTION_DEFSYM, + OPTION_LISTING_LHS_WIDTH, + OPTION_LISTING_LHS_WIDTH2, + OPTION_LISTING_RHS_WIDTH, + OPTION_LISTING_CONT_LINES, + OPTION_DEPFILE, + OPTION_GSTABS, + OPTION_GSTABS_PLUS, + OPTION_GDWARF2, + OPTION_GDWARF_SECTIONS, + OPTION_STRIP_LOCAL_ABSOLUTE, + OPTION_TRADITIONAL_FORMAT, + OPTION_WARN, + OPTION_TARGET_HELP, + OPTION_EXECSTACK, + OPTION_NOEXECSTACK, + OPTION_SIZE_CHECK, + OPTION_ALTERNATE, + OPTION_AL, + OPTION_HASH_TABLE_SIZE, + OPTION_REDUCE_MEMORY_OVERHEADS, + OPTION_WARN_FATAL, + OPTION_COMPRESS_DEBUG, + OPTION_NOCOMPRESS_DEBUG + /* When you add options here, check that they do + not collide with OPTION_MD_BASE. See as.h. */ + }; + + static const struct option std_longopts[] = + { + /* Note: commas are placed at the start of the line rather than + the end of the preceding line so that it is simpler to + selectively add and remove lines from this list. */ + {"alternate", no_argument, NULL, OPTION_ALTERNATE} + /* The entry for "a" is here to prevent getopt_long_only() from + considering that -a is an abbreviation for --alternate. This is + necessary because -a= is a valid switch but getopt would + normally reject it since --alternate does not take an argument. */ + ,{"a", optional_argument, NULL, 'a'} + /* Handle -al=. */ + ,{"al", optional_argument, NULL, OPTION_AL} + ,{"compress-debug-sections", no_argument, NULL, OPTION_COMPRESS_DEBUG} + ,{"nocompress-debug-sections", no_argument, NULL, OPTION_NOCOMPRESS_DEBUG} + ,{"debug-prefix-map", required_argument, NULL, OPTION_DEBUG_PREFIX_MAP} + ,{"defsym", required_argument, NULL, OPTION_DEFSYM} + ,{"dump-config", no_argument, NULL, OPTION_DUMPCONFIG} + ,{"emulation", required_argument, NULL, OPTION_EMULATION} +#if defined OBJ_ELF || defined OBJ_MAYBE_ELF + ,{"execstack", no_argument, NULL, OPTION_EXECSTACK} + ,{"noexecstack", no_argument, NULL, OPTION_NOEXECSTACK} + ,{"size-check", required_argument, NULL, OPTION_SIZE_CHECK} +#endif + ,{"fatal-warnings", no_argument, NULL, OPTION_WARN_FATAL} + ,{"gdwarf-2", no_argument, NULL, OPTION_GDWARF2} + /* GCC uses --gdwarf-2 but GAS uses to use --gdwarf2, + so we keep it here for backwards compatibility. */ + ,{"gdwarf2", no_argument, NULL, OPTION_GDWARF2} + ,{"gdwarf-sections", no_argument, NULL, OPTION_GDWARF_SECTIONS} + ,{"gen-debug", no_argument, NULL, 'g'} + ,{"gstabs", no_argument, NULL, OPTION_GSTABS} + ,{"gstabs+", no_argument, NULL, OPTION_GSTABS_PLUS} + ,{"hash-size", required_argument, NULL, OPTION_HASH_TABLE_SIZE} + ,{"help", no_argument, NULL, OPTION_HELP} +#ifdef HAVE_ITBL_CPU + /* New option for extending instruction set (see also -t above). + The "-t file" or "--itbl file" option extends the basic set of + valid instructions by reading "file", a text file containing a + list of instruction formats. The additional opcodes and their + formats are added to the built-in set of instructions, and + mnemonics for new registers may also be defined. */ + ,{"itbl", required_argument, NULL, 't'} +#endif + /* getopt allows abbreviations, so we do this to stop it from + treating -k as an abbreviation for --keep-locals. Some + ports use -k to enable PIC assembly. */ + ,{"keep-locals", no_argument, NULL, 'L'} + ,{"keep-locals", no_argument, NULL, 'L'} + ,{"listing-lhs-width", required_argument, NULL, OPTION_LISTING_LHS_WIDTH} + ,{"listing-lhs-width2", required_argument, NULL, OPTION_LISTING_LHS_WIDTH2} + ,{"listing-rhs-width", required_argument, NULL, OPTION_LISTING_RHS_WIDTH} + ,{"listing-cont-lines", required_argument, NULL, OPTION_LISTING_CONT_LINES} + ,{"MD", required_argument, NULL, OPTION_DEPFILE} + ,{"mri", no_argument, NULL, 'M'} + ,{"nocpp", no_argument, NULL, OPTION_NOCPP} + ,{"no-warn", no_argument, NULL, 'W'} + ,{"reduce-memory-overheads", no_argument, NULL, OPTION_REDUCE_MEMORY_OVERHEADS} + ,{"statistics", no_argument, NULL, OPTION_STATISTICS} + ,{"strip-local-absolute", no_argument, NULL, OPTION_STRIP_LOCAL_ABSOLUTE} + ,{"version", no_argument, NULL, OPTION_VERSION} + ,{"verbose", no_argument, NULL, OPTION_VERBOSE} + ,{"target-help", no_argument, NULL, OPTION_TARGET_HELP} + ,{"traditional-format", no_argument, NULL, OPTION_TRADITIONAL_FORMAT} + ,{"warn", no_argument, NULL, OPTION_WARN} + }; + + /* Construct the option lists from the standard list and the target + dependent list. Include space for an extra NULL option and + always NULL terminate. */ + shortopts = concat (std_shortopts, md_shortopts, (char *) NULL); + longopts = (struct option *) xmalloc (sizeof (std_longopts) + + md_longopts_size + sizeof (struct option)); + memcpy (longopts, std_longopts, sizeof (std_longopts)); + memcpy (((char *) longopts) + sizeof (std_longopts), md_longopts, md_longopts_size); + memset (((char *) longopts) + sizeof (std_longopts) + md_longopts_size, + 0, sizeof (struct option)); + + /* Make a local copy of the old argv. */ + old_argc = *pargc; + old_argv = *pargv; + + /* Initialize a new argv that contains no options. */ + new_argv = (char **) xmalloc (sizeof (char *) * (old_argc + 1)); + new_argv[0] = old_argv[0]; + new_argc = 1; + new_argv[new_argc] = NULL; + + while (1) + { + /* getopt_long_only is like getopt_long, but '-' as well as '--' can + indicate a long option. */ + int longind; + int optc = getopt_long_only (old_argc, old_argv, shortopts, longopts, + &longind); + + if (optc == -1) + break; + + switch (optc) + { + default: + /* md_parse_option should return 1 if it recognizes optc, + 0 if not. */ + if (md_parse_option (optc, optarg) != 0) + break; + /* `-v' isn't included in the general short_opts list, so check for + it explicitly here before deciding we've gotten a bad argument. */ + if (optc == 'v') + { +#ifdef VMS + /* Telling getopt to treat -v's value as optional can result + in it picking up a following filename argument here. The + VMS code in md_parse_option can return 0 in that case, + but it has no way of pushing the filename argument back. */ + if (optarg && *optarg) + new_argv[new_argc++] = optarg, new_argv[new_argc] = NULL; + else +#else + case 'v': +#endif + case OPTION_VERBOSE: + print_version_id (); + verbose = 1; + break; + } + else + as_bad (_("unrecognized option -%c%s"), optc, optarg ? optarg : ""); + /* Fall through. */ + + case '?': + exit (EXIT_FAILURE); + + case 1: /* File name. */ + if (!strcmp (optarg, "-")) + optarg = ""; + new_argv[new_argc++] = optarg; + new_argv[new_argc] = NULL; + break; + + case OPTION_TARGET_HELP: + md_show_usage (stdout); + exit (EXIT_SUCCESS); + + case OPTION_HELP: + show_usage (stdout); + exit (EXIT_SUCCESS); + + case OPTION_NOCPP: + break; + + case OPTION_STATISTICS: + flag_print_statistics = 1; + break; + + case OPTION_STRIP_LOCAL_ABSOLUTE: + flag_strip_local_absolute = 1; + break; + + case OPTION_TRADITIONAL_FORMAT: + flag_traditional_format = 1; + break; + + case OPTION_VERSION: + /* This output is intended to follow the GNU standards document. */ + printf (_("GNU assembler %s\n"), BFD_VERSION_STRING); + printf (_("Copyright 2013 Free Software Foundation, Inc.\n")); + printf (_("\ +This program is free software; you may redistribute it under the terms of\n\ +the GNU General Public License version 3 or later.\n\ +This program has absolutely no warranty.\n")); + printf (_("This assembler was configured for a target of `%s'.\n"), + TARGET_ALIAS); + exit (EXIT_SUCCESS); + + case OPTION_EMULATION: +#ifdef USE_EMULATIONS + if (strcmp (optarg, this_emulation->name)) + as_fatal (_("multiple emulation names specified")); +#else + as_fatal (_("emulations not handled in this configuration")); +#endif + break; + + case OPTION_DUMPCONFIG: + fprintf (stderr, _("alias = %s\n"), TARGET_ALIAS); + fprintf (stderr, _("canonical = %s\n"), TARGET_CANONICAL); + fprintf (stderr, _("cpu-type = %s\n"), TARGET_CPU); +#ifdef TARGET_OBJ_FORMAT + fprintf (stderr, _("format = %s\n"), TARGET_OBJ_FORMAT); +#endif +#ifdef TARGET_FORMAT + fprintf (stderr, _("bfd-target = %s\n"), TARGET_FORMAT); +#endif + exit (EXIT_SUCCESS); + + case OPTION_COMPRESS_DEBUG: +#ifdef HAVE_ZLIB_H + flag_compress_debug = 1; +#else + as_warn (_("cannot compress debug sections (zlib not installed)")); +#endif /* HAVE_ZLIB_H */ + break; + + case OPTION_NOCOMPRESS_DEBUG: + flag_compress_debug = 0; + break; + + case OPTION_DEBUG_PREFIX_MAP: + add_debug_prefix_map (optarg); + break; + + case OPTION_DEFSYM: + { + char *s; + valueT i; + struct defsym_list *n; + + for (s = optarg; *s != '\0' && *s != '='; s++) + ; + if (*s == '\0') + as_fatal (_("bad defsym; format is --defsym name=value")); + *s++ = '\0'; + i = bfd_scan_vma (s, (const char **) NULL, 0); + n = (struct defsym_list *) xmalloc (sizeof *n); + n->next = defsyms; + n->name = optarg; + n->value = i; + defsyms = n; + } + break; + +#ifdef HAVE_ITBL_CPU + case 't': + { + /* optarg is the name of the file containing the instruction + formats, opcodes, register names, etc. */ + struct itbl_file_list *n; + + if (optarg == NULL) + { + as_warn (_("no file name following -t option")); + break; + } + + n = xmalloc (sizeof * n); + n->next = itbl_files; + n->name = optarg; + itbl_files = n; + + /* Parse the file and add the new instructions to our internal + table. If multiple instruction tables are specified, the + information from this table gets appended onto the existing + internal table. */ + itbl_files->name = xstrdup (optarg); + if (itbl_parse (itbl_files->name) != 0) + as_fatal (_("failed to read instruction table %s\n"), + itbl_files->name); + } + break; +#endif + + case OPTION_DEPFILE: + start_dependencies (optarg); + break; + + case 'g': + /* Some backends, eg Alpha and Mips, use the -g switch for their + own purposes. So we check here for an explicit -g and allow + the backend to decide if it wants to process it. */ + if ( old_argv[optind - 1][1] == 'g' + && md_parse_option (optc, optarg)) + continue; + + if (md_debug_format_selector) + debug_type = md_debug_format_selector (& use_gnu_debug_info_extensions); + else if (IS_ELF) + debug_type = DEBUG_DWARF2; + else + debug_type = DEBUG_STABS; + break; + + case OPTION_GSTABS_PLUS: + use_gnu_debug_info_extensions = 1; + /* Fall through. */ + case OPTION_GSTABS: + debug_type = DEBUG_STABS; + break; + + case OPTION_GDWARF2: + debug_type = DEBUG_DWARF2; + break; + + case OPTION_GDWARF_SECTIONS: + flag_dwarf_sections = TRUE; + break; + + case 'J': + flag_signed_overflow_ok = 1; + break; + +#ifndef WORKING_DOT_WORD + case 'K': + flag_warn_displacement = 1; + break; +#endif + case 'L': + flag_keep_locals = 1; + break; + + case OPTION_LISTING_LHS_WIDTH: + listing_lhs_width = atoi (optarg); + if (listing_lhs_width_second < listing_lhs_width) + listing_lhs_width_second = listing_lhs_width; + break; + case OPTION_LISTING_LHS_WIDTH2: + { + int tmp = atoi (optarg); + + if (tmp > listing_lhs_width) + listing_lhs_width_second = tmp; + } + break; + case OPTION_LISTING_RHS_WIDTH: + listing_rhs_width = atoi (optarg); + break; + case OPTION_LISTING_CONT_LINES: + listing_lhs_cont_lines = atoi (optarg); + break; + + case 'M': + flag_mri = 1; +#ifdef TC_M68K + flag_m68k_mri = 1; +#endif + break; + + case 'R': + flag_readonly_data_in_text = 1; + break; + + case 'W': + flag_no_warnings = 1; + break; + + case OPTION_WARN: + flag_no_warnings = 0; + flag_fatal_warnings = 0; + break; + + case OPTION_WARN_FATAL: + flag_no_warnings = 0; + flag_fatal_warnings = 1; + break; + +#if defined OBJ_ELF || defined OBJ_MAYBE_ELF + case OPTION_EXECSTACK: + flag_execstack = 1; + flag_noexecstack = 0; + break; + + case OPTION_NOEXECSTACK: + flag_noexecstack = 1; + flag_execstack = 0; + break; + + case OPTION_SIZE_CHECK: + if (strcasecmp (optarg, "error") == 0) + flag_size_check = size_check_error; + else if (strcasecmp (optarg, "warning") == 0) + flag_size_check = size_check_warning; + else + as_fatal (_("Invalid --size-check= option: `%s'"), optarg); + break; +#endif + case 'Z': + flag_always_generate_output = 1; + break; + + case OPTION_AL: + listing |= LISTING_LISTING; + if (optarg) + listing_filename = xstrdup (optarg); + break; + + case OPTION_ALTERNATE: + optarg = old_argv [optind - 1]; + while (* optarg == '-') + optarg ++; + + if (strcmp (optarg, "alternate") == 0) + { + flag_macro_alternate = 1; + break; + } + optarg ++; + /* Fall through. */ + + case 'a': + if (optarg) + { + if (optarg != old_argv[optind] && optarg[-1] == '=') + --optarg; + + if (md_parse_option (optc, optarg) != 0) + break; + + while (*optarg) + { + switch (*optarg) + { + case 'c': + listing |= LISTING_NOCOND; + break; + case 'd': + listing |= LISTING_NODEBUG; + break; + case 'g': + listing |= LISTING_GENERAL; + break; + case 'h': + listing |= LISTING_HLL; + break; + case 'l': + listing |= LISTING_LISTING; + break; + case 'm': + listing |= LISTING_MACEXP; + break; + case 'n': + listing |= LISTING_NOFORM; + break; + case 's': + listing |= LISTING_SYMBOLS; + break; + case '=': + listing_filename = xstrdup (optarg + 1); + optarg += strlen (listing_filename); + break; + default: + as_fatal (_("invalid listing option `%c'"), *optarg); + break; + } + optarg++; + } + } + if (!listing) + listing = LISTING_DEFAULT; + break; + + case 'D': + /* DEBUG is implemented: it debugs different + things from other people's assemblers. */ + flag_debug = 1; + break; + + case 'f': + flag_no_comments = 1; + break; + + case 'I': + { /* Include file directory. */ + char *temp = xstrdup (optarg); + + add_include_dir (temp); + break; + } + + case 'o': + out_file_name = xstrdup (optarg); + break; + + case 'w': + break; + + case 'X': + /* -X means treat warnings as errors. */ + break; + + case OPTION_REDUCE_MEMORY_OVERHEADS: + /* The only change we make at the moment is to reduce + the size of the hash tables that we use. */ + set_gas_hash_table_size (4051); + break; + + case OPTION_HASH_TABLE_SIZE: + { + unsigned long new_size; + + new_size = strtoul (optarg, NULL, 0); + if (new_size) + set_gas_hash_table_size (new_size); + else + as_fatal (_("--hash-size needs a numeric argument")); + break; + } + } + } + + free (shortopts); + free (longopts); + + *pargc = new_argc; + *pargv = new_argv; + +#ifdef md_after_parse_args + md_after_parse_args (); +#endif +} + +static void +dump_statistics (void) +{ +#ifdef HAVE_SBRK + char *lim = (char *) sbrk (0); +#endif + long run_time = get_run_time () - start_time; + + fprintf (stderr, _("%s: total time in assembly: %ld.%06ld\n"), + myname, run_time / 1000000, run_time % 1000000); +#ifdef HAVE_SBRK + fprintf (stderr, _("%s: data size %ld\n"), + myname, (long) (lim - start_sbrk)); +#endif + + subsegs_print_statistics (stderr); + write_print_statistics (stderr); + symbol_print_statistics (stderr); + read_print_statistics (stderr); + +#ifdef tc_print_statistics + tc_print_statistics (stderr); +#endif + +#ifdef obj_print_statistics + obj_print_statistics (stderr); +#endif +} + +static void +close_output_file (void) +{ + output_file_close (out_file_name); + if (!keep_it) + unlink_if_ordinary (out_file_name); +} + +/* The interface between the macro code and gas expression handling. */ + +static size_t +macro_expr (const char *emsg, size_t idx, sb *in, offsetT *val) +{ + char *hold; + expressionS ex; + + sb_terminate (in); + + hold = input_line_pointer; + input_line_pointer = in->ptr + idx; + expression_and_evaluate (&ex); + idx = input_line_pointer - in->ptr; + input_line_pointer = hold; + + if (ex.X_op != O_constant) + as_bad ("%s", emsg); + + *val = ex.X_add_number; + + return idx; +} + +/* Here to attempt 1 pass over each input file. + We scan argv[*] looking for filenames or exactly "" which is + shorthand for stdin. Any argv that is NULL is not a file-name. + We set need_pass_2 TRUE if, after this, we still have unresolved + expressions of the form (unknown value)+-(unknown value). + + Note the un*x semantics: there is only 1 logical input file, but it + may be a catenation of many 'physical' input files. */ + +static void +perform_an_assembly_pass (int argc, char ** argv) +{ + int saw_a_file = 0; +#ifndef OBJ_MACH_O + flagword applicable; +#endif + + need_pass_2 = 0; + +#ifndef OBJ_MACH_O + /* Create the standard sections, and those the assembler uses + internally. */ + text_section = subseg_new (TEXT_SECTION_NAME, 0); + data_section = subseg_new (DATA_SECTION_NAME, 0); + bss_section = subseg_new (BSS_SECTION_NAME, 0); + /* @@ FIXME -- we're setting the RELOC flag so that sections are assumed + to have relocs, otherwise we don't find out in time. */ + applicable = bfd_applicable_section_flags (stdoutput); + bfd_set_section_flags (stdoutput, text_section, + applicable & (SEC_ALLOC | SEC_LOAD | SEC_RELOC + | SEC_CODE | SEC_READONLY)); + bfd_set_section_flags (stdoutput, data_section, + applicable & (SEC_ALLOC | SEC_LOAD | SEC_RELOC + | SEC_DATA)); + bfd_set_section_flags (stdoutput, bss_section, applicable & SEC_ALLOC); + seg_info (bss_section)->bss = 1; +#endif + subseg_new (BFD_ABS_SECTION_NAME, 0); + subseg_new (BFD_UND_SECTION_NAME, 0); + reg_section = subseg_new ("*GAS `reg' section*", 0); + expr_section = subseg_new ("*GAS `expr' section*", 0); + +#ifndef OBJ_MACH_O + subseg_set (text_section, 0); +#endif + + /* This may add symbol table entries, which requires having an open BFD, + and sections already created. */ + md_begin (); + +#ifdef USING_CGEN + gas_cgen_begin (); +#endif +#ifdef obj_begin + obj_begin (); +#endif + + /* Skip argv[0]. */ + argv++; + argc--; + + while (argc--) + { + if (*argv) + { /* Is it a file-name argument? */ + PROGRESS (1); + saw_a_file++; + /* argv->"" if stdin desired, else->filename. */ + read_a_source_file (*argv); + } + argv++; /* Completed that argv. */ + } + if (!saw_a_file) + read_a_source_file (""); +} + +#ifdef OBJ_ELF +static void +create_obj_attrs_section (void) +{ + segT s; + char *p; + offsetT size; + const char *name; + + size = bfd_elf_obj_attr_size (stdoutput); + if (size) + { + name = get_elf_backend_data (stdoutput)->obj_attrs_section; + if (!name) + name = ".gnu.attributes"; + s = subseg_new (name, 0); + elf_section_type (s) + = get_elf_backend_data (stdoutput)->obj_attrs_section_type; + bfd_set_section_flags (stdoutput, s, SEC_READONLY | SEC_DATA); + frag_now_fix (); + p = frag_more (size); + bfd_elf_set_obj_attr_contents (stdoutput, (bfd_byte *)p, size); + } +} +#endif + + +int +main (int argc, char ** argv) +{ + char ** argv_orig = argv; + + int macro_strip_at; + + start_time = get_run_time (); +#ifdef HAVE_SBRK + start_sbrk = (char *) sbrk (0); +#endif + +#if defined (HAVE_SETLOCALE) && defined (HAVE_LC_MESSAGES) + setlocale (LC_MESSAGES, ""); +#endif +#if defined (HAVE_SETLOCALE) + setlocale (LC_CTYPE, ""); +#endif + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + if (debug_memory) + chunksize = 64; + +#ifdef HOST_SPECIAL_INIT + HOST_SPECIAL_INIT (argc, argv); +#endif + + myname = argv[0]; + xmalloc_set_program_name (myname); + + expandargv (&argc, &argv); + + START_PROGRESS (myname, 0); + +#ifndef OBJ_DEFAULT_OUTPUT_FILE_NAME +#define OBJ_DEFAULT_OUTPUT_FILE_NAME "a.out" +#endif + + out_file_name = OBJ_DEFAULT_OUTPUT_FILE_NAME; + + hex_init (); + bfd_init (); + bfd_set_error_program_name (myname); + +#ifdef USE_EMULATIONS + select_emulation_mode (argc, argv); +#endif + + PROGRESS (1); + /* Call parse_args before any of the init/begin functions + so that switches like --hash-size can be honored. */ + parse_args (&argc, &argv); + symbol_begin (); + frag_init (); + subsegs_begin (); + read_begin (); + input_scrub_begin (); + expr_begin (); + + /* It has to be called after dump_statistics (). */ + xatexit (close_output_file); + + if (flag_print_statistics) + xatexit (dump_statistics); + + macro_strip_at = 0; +#ifdef TC_I960 + macro_strip_at = flag_mri; +#endif + + macro_init (flag_macro_alternate, flag_mri, macro_strip_at, macro_expr); + + PROGRESS (1); + + output_file_create (out_file_name); + gas_assert (stdoutput != 0); + + dot_symbol_init (); + +#ifdef tc_init_after_args + tc_init_after_args (); +#endif + + itbl_init (); + + dwarf2_init (); + + local_symbol_make (".gasversion.", absolute_section, + BFD_VERSION / 10000UL, &predefined_address_frag); + + /* Now that we have fully initialized, and have created the output + file, define any symbols requested by --defsym command line + arguments. */ + while (defsyms != NULL) + { + symbolS *sym; + struct defsym_list *next; + + sym = symbol_new (defsyms->name, absolute_section, defsyms->value, + &zero_address_frag); + /* Make symbols defined on the command line volatile, so that they + can be redefined inside a source file. This makes this assembler's + behaviour compatible with earlier versions, but it may not be + completely intuitive. */ + S_SET_VOLATILE (sym); + symbol_table_insert (sym); + next = defsyms->next; + free (defsyms); + defsyms = next; + } + + PROGRESS (1); + + /* Assemble it. */ + perform_an_assembly_pass (argc, argv); + + cond_finish_check (-1); + +#ifdef md_end + md_end (); +#endif + +#ifdef OBJ_ELF + if (IS_ELF) + create_obj_attrs_section (); +#endif + +#if defined OBJ_ELF || defined OBJ_MAYBE_ELF + if ((flag_execstack || flag_noexecstack) + && OUTPUT_FLAVOR == bfd_target_elf_flavour) + { + segT gnustack; + + gnustack = subseg_new (".note.GNU-stack", 0); + bfd_set_section_flags (stdoutput, gnustack, + SEC_READONLY | (flag_execstack ? SEC_CODE : 0)); + + } +#endif + + /* If we've been collecting dwarf2 .debug_line info, either for + assembly debugging or on behalf of the compiler, emit it now. */ + dwarf2_finish (); + + /* If we constructed dwarf2 .eh_frame info, either via .cfi + directives from the user or by the backend, emit it now. */ + cfi_finish (); + + if (seen_at_least_1_file () + && (flag_always_generate_output || had_errors () == 0)) + keep_it = 1; + else + keep_it = 0; + + /* This used to be done at the start of write_object_file in + write.c, but that caused problems when doing listings when + keep_it was zero. This could probably be moved above md_end, but + I didn't want to risk the change. */ + subsegs_finish (); + + if (keep_it) + write_object_file (); + + fflush (stderr); + +#ifndef NO_LISTING + listing_print (listing_filename, argv_orig); +#endif + + if (flag_fatal_warnings && had_warnings () > 0 && had_errors () == 0) + as_bad (_("%d warnings, treating warnings as errors"), had_warnings ()); + + if (had_errors () > 0 && ! flag_always_generate_output) + keep_it = 0; + + input_scrub_end (); + + END_PROGRESS (myname); + + /* Use xexit instead of return, because under VMS environments they + may not place the same interpretation on the value given. */ + if (had_errors () > 0) + xexit (EXIT_FAILURE); + + /* Only generate dependency file if assembler was successful. */ + print_dependencies (); + + xexit (EXIT_SUCCESS); +} diff --git a/contrib/toolchain/binutils/gas/as.h b/contrib/toolchain/binutils/gas/as.h new file mode 100644 index 0000000000..1fefee98fd --- /dev/null +++ b/contrib/toolchain/binutils/gas/as.h @@ -0,0 +1,634 @@ +/* as.h - global header file + Copyright 1987-2013 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef GAS +#define GAS 1 +/* I think this stuff is largely out of date. xoxorich. + + CAPITALISED names are #defined. + "lowercaseH" is #defined if "lowercase.h" has been #include-d. + "lowercaseT" is a typedef of "lowercase" objects. + "lowercaseP" is type "pointer to object of type 'lowercase'". + "lowercaseS" is typedef struct ... lowercaseS. + + #define DEBUG to enable all the "know" assertion tests. + #define SUSPECT when debugging hash code. + #define COMMON as "extern" for all modules except one, where you #define + COMMON as "". + If TEST is #defined, then we are testing a module: #define COMMON as "". */ + +#include "alloca-conf.h" + +/* Now, tend to the rest of the configuration. */ + +/* System include files first... */ +#include + +#ifdef STRING_WITH_STRINGS +#include +#include +#else +#ifdef HAVE_STRING_H +#include +#else +#ifdef HAVE_STRINGS_H +#include +#endif +#endif +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#endif +#ifdef HAVE_SYS_TYPES_H +/* for size_t, pid_t */ +#include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif + +#include + +#include "getopt.h" +/* The first getopt value for machine-independent long options. + 150 isn't special; it's just an arbitrary non-ASCII char value. */ +#define OPTION_STD_BASE 150 +/* The first getopt value for machine-dependent long options. + 190 gives the standard options room to grow. */ +#define OPTION_MD_BASE 190 + +#ifdef DEBUG +#undef NDEBUG +#endif +#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 6) +#define __PRETTY_FUNCTION__ ((char *) NULL) +#endif +#define gas_assert(P) \ + ((void) ((P) ? 0 : (as_assert (__FILE__, __LINE__, __PRETTY_FUNCTION__), 0))) +#undef abort +#define abort() as_abort (__FILE__, __LINE__, __PRETTY_FUNCTION__) + +/* Now GNU header files... */ +#include "ansidecl.h" +#include "bfd.h" +#include "libiberty.h" + +/* Define the standard progress macros. */ +#include "progress.h" + +/* This doesn't get taken care of anywhere. */ +#ifndef __MWERKS__ /* Metrowerks C chokes on the "defined (inline)" */ +#if !defined (__GNUC__) && !defined (inline) +#define inline +#endif +#endif /* !__MWERKS__ */ + +/* Other stuff from config.h. */ +#ifdef NEED_DECLARATION_ENVIRON +extern char **environ; +#endif +#ifdef NEED_DECLARATION_ERRNO +extern int errno; +#endif +#ifdef NEED_DECLARATION_FFS +extern int ffs (int); +#endif +#ifdef NEED_DECLARATION_FREE +extern void free (); +#endif +#ifdef NEED_DECLARATION_MALLOC +extern void *malloc (); +extern void *realloc (); +#endif +#ifdef NEED_DECLARATION_STRSTR +extern char *strstr (); +#endif + +#if !HAVE_DECL_MEMPCPY +void *mempcpy(void *, const void *, size_t); +#endif + +#if !HAVE_DECL_VSNPRINTF +extern int vsnprintf(char *, size_t, const char *, va_list); +#endif + +/* This is needed for VMS. */ +#if ! defined (HAVE_UNLINK) && defined (HAVE_REMOVE) +#define unlink remove +#endif + +/* Hack to make "gcc -Wall" not complain about obstack macros. */ +#if !defined (memcpy) && !defined (bcopy) +#define bcopy(src,dest,size) memcpy (dest, src, size) +#endif + +/* Make Saber happier on obstack.h. */ +#ifdef SABER +#undef __PTR_TO_INT +#define __PTR_TO_INT(P) ((int) (P)) +#undef __INT_TO_PTR +#define __INT_TO_PTR(P) ((char *) (P)) +#endif + +#ifndef __LINE__ +#define __LINE__ "unknown" +#endif /* __LINE__ */ + +#ifndef __FILE__ +#define __FILE__ "unknown" +#endif /* __FILE__ */ + +#ifndef FOPEN_WB +#ifdef USE_BINARY_FOPEN +#include "fopen-bin.h" +#else +#include "fopen-same.h" +#endif +#endif + +#ifndef EXIT_SUCCESS +#define EXIT_SUCCESS 0 +#define EXIT_FAILURE 1 +#endif + +#ifndef SEEK_SET +#define SEEK_SET 0 +#endif + +#define obstack_chunk_alloc xmalloc +#define obstack_chunk_free xfree + +#define xfree free + +#include "asintl.h" + +#define BAD_CASE(val) \ + { \ + as_fatal (_("Case value %ld unexpected at line %d of file \"%s\"\n"), \ + (long) val, __LINE__, __FILE__); \ + } + +#include "flonum.h" + +/* These are assembler-wide concepts */ + +extern bfd *stdoutput; +typedef bfd_vma addressT; +typedef bfd_signed_vma offsetT; + +/* Type of symbol value, etc. For use in prototypes. */ +typedef addressT valueT; + +#ifndef COMMON +#ifdef TEST +#define COMMON /* Declare our COMMONs storage here. */ +#else +#define COMMON extern /* Our commons live elsewhere. */ +#endif +#endif +/* COMMON now defined */ + +#ifndef ENABLE_CHECKING +#define ENABLE_CHECKING 0 +#endif + +#if ENABLE_CHECKING || defined (DEBUG) +#ifndef know +#define know(p) gas_assert(p) /* Verify our assumptions! */ +#endif /* not yet defined */ +#else +#define know(p) do {} while (0) /* know() checks are no-op.ed */ +#endif + +/* input_scrub.c */ + +/* Supplies sanitised buffers to read.c. + Also understands printing line-number part of error messages. */ + +/* subsegs.c Sub-segments. Also, segment(=expression type)s.*/ + +typedef asection *segT; +#define SEG_NORMAL(SEG) ( (SEG) != absolute_section \ + && (SEG) != undefined_section \ + && (SEG) != reg_section \ + && (SEG) != expr_section) +typedef int subsegT; + +/* What subseg we are accessing now? */ +COMMON subsegT now_subseg; + +/* Segment our instructions emit to. */ +COMMON segT now_seg; + +#define segment_name(SEG) bfd_get_section_name (stdoutput, SEG) + +extern segT reg_section, expr_section; +/* Shouldn't these be eliminated someday? */ +extern segT text_section, data_section, bss_section; +#define absolute_section bfd_abs_section_ptr +#define undefined_section bfd_und_section_ptr + +enum _relax_state +{ + /* Dummy frag used by listing code. */ + rs_dummy = 0, + + /* Variable chars to be repeated fr_offset times. + Fr_symbol unused. Used with fr_offset == 0 for a + constant length frag. */ + rs_fill, + + /* Align. The fr_offset field holds the power of 2 to which to + align. The fr_var field holds the number of characters in the + fill pattern. The fr_subtype field holds the maximum number of + bytes to skip when aligning, or 0 if there is no maximum. */ + rs_align, + + /* Align code. The fr_offset field holds the power of 2 to which + to align. This type is only generated by machine specific + code, which is normally responsible for handling the fill + pattern. The fr_subtype field holds the maximum number of + bytes to skip when aligning, or 0 if there is no maximum. */ + rs_align_code, + + /* Test for alignment. Like rs_align, but used by several targets + to warn if data is not properly aligned. */ + rs_align_test, + + /* Org: Fr_offset, fr_symbol: address. 1 variable char: fill + character. */ + rs_org, + +#ifndef WORKING_DOT_WORD + /* JF: gunpoint */ + rs_broken_word, +#endif + + /* Machine specific relaxable (or similarly alterable) instruction. */ + rs_machine_dependent, + + /* .space directive with expression operand that needs to be computed + later. Similar to rs_org, but different. + fr_symbol: operand + 1 variable char: fill character */ + rs_space, + + /* A DWARF leb128 value; only ELF uses this. The subtype is 0 for + unsigned, 1 for signed. */ + rs_leb128, + + /* Exception frame information which we may be able to optimize. */ + rs_cfa, + + /* Cross-fragment dwarf2 line number optimization. */ + rs_dwarf2dbg +}; + +typedef enum _relax_state relax_stateT; + +/* This type is used in prototypes, so it can't be a type that will be + widened for argument passing. */ +typedef unsigned int relax_substateT; + +/* Enough bits for address, but still an integer type. + Could be a problem, cross-assembling for 64-bit machines. */ +typedef addressT relax_addressT; + +struct relax_type +{ + /* Forward reach. Signed number. > 0. */ + offsetT rlx_forward; + /* Backward reach. Signed number. < 0. */ + offsetT rlx_backward; + + /* Bytes length of this address. */ + unsigned char rlx_length; + + /* Next longer relax-state. 0 means there is no 'next' relax-state. */ + relax_substateT rlx_more; +}; + +typedef struct relax_type relax_typeS; + +/* main program "as.c" (command arguments etc). */ + +COMMON unsigned char flag_no_comments; /* -f */ +COMMON unsigned char flag_debug; /* -D */ +COMMON unsigned char flag_signed_overflow_ok; /* -J */ +#ifndef WORKING_DOT_WORD +COMMON unsigned char flag_warn_displacement; /* -K */ +#endif + +/* True if local symbols should be retained. */ +COMMON int flag_keep_locals; /* -L */ + +/* True if we are assembling in MRI mode. */ +COMMON int flag_mri; + +/* Should the data section be made read-only and appended to the text + section? */ +COMMON unsigned char flag_readonly_data_in_text; /* -R */ + +/* True if warnings should be inhibited. */ +COMMON int flag_no_warnings; /* -W */ + +/* True if warnings count as errors. */ +COMMON int flag_fatal_warnings; /* --fatal-warnings */ + +/* True if we should attempt to generate output even if non-fatal errors + are detected. */ +COMMON unsigned char flag_always_generate_output; /* -Z */ + +/* This is true if the assembler should output time and space usage. */ +COMMON unsigned char flag_print_statistics; + +/* True if local absolute symbols are to be stripped. */ +COMMON int flag_strip_local_absolute; + +/* True if we should generate a traditional format object file. */ +COMMON int flag_traditional_format; + +/* TRUE if debug sections should be compressed. */ +COMMON int flag_compress_debug; + +/* TRUE if .note.GNU-stack section with SEC_CODE should be created */ +COMMON int flag_execstack; + +/* TRUE if .note.GNU-stack section with SEC_CODE should be created */ +COMMON int flag_noexecstack; + +/* name of emitted object file */ +COMMON char *out_file_name; + +/* name of file defining extensions to the basic instruction set */ +COMMON char *insttbl_file_name; + +/* TRUE if we need a second pass. */ +COMMON int need_pass_2; + +/* TRUE if we should do no relaxing, and + leave lots of padding. */ +COMMON int linkrelax; + +/* TRUE if we should produce a listing. */ +extern int listing; + +/* Type of debugging information we should generate. We currently support + stabs, ECOFF, and DWARF2. + + NOTE! This means debug information about the assembly source code itself + and _not_ about possible debug information from a high-level language. + This is especially relevant to DWARF2, since the compiler may emit line + number directives that the assembler resolves. */ + +enum debug_info_type +{ + DEBUG_UNSPECIFIED, + DEBUG_NONE, + DEBUG_STABS, + DEBUG_ECOFF, + DEBUG_DWARF, + DEBUG_DWARF2 +}; + +extern enum debug_info_type debug_type; +extern int use_gnu_debug_info_extensions; +COMMON bfd_boolean flag_dwarf_sections; + +/* Maximum level of macro nesting. */ +extern int max_macro_nest; + +/* Verbosity level. */ +extern int verbose; + +/* Obstack chunk size. Keep large for efficient space use, make small to + increase malloc calls for monitoring memory allocation. */ +extern int chunksize; + +struct _pseudo_type +{ + /* assembler mnemonic, lower case, no '.' */ + const char *poc_name; + /* Do the work */ + void (*poc_handler) (int); + /* Value to pass to handler */ + int poc_val; +}; + +typedef struct _pseudo_type pseudo_typeS; + +#if (__GNUC__ >= 2) && !defined(VMS) +/* for use with -Wformat */ + +#if __GNUC__ == 2 && __GNUC_MINOR__ < 6 +/* Support for double underscores in attribute names was added in gcc + 2.6, so avoid them if we are using an earlier version. */ +#define __printf__ printf +#define __format__ format +#endif + +#define PRINTF_LIKE(FCN) \ + void FCN (const char *format, ...) \ + __attribute__ ((__format__ (__printf__, 1, 2))) +#define PRINTF_WHERE_LIKE(FCN) \ + void FCN (char *file, unsigned int line, const char *format, ...) \ + __attribute__ ((__format__ (__printf__, 3, 4))) + +#else /* __GNUC__ < 2 || defined(VMS) */ + +#define PRINTF_LIKE(FCN) void FCN (const char *format, ...) +#define PRINTF_WHERE_LIKE(FCN) void FCN (char *file, \ + unsigned int line, \ + const char *format, ...) + +#endif /* __GNUC__ < 2 || defined(VMS) */ + +PRINTF_LIKE (as_bad); +PRINTF_LIKE (as_fatal) ATTRIBUTE_NORETURN; +PRINTF_LIKE (as_tsktsk); +PRINTF_LIKE (as_warn); +PRINTF_WHERE_LIKE (as_bad_where); +PRINTF_WHERE_LIKE (as_warn_where); + +void as_assert (const char *, int, const char *); +void as_abort (const char *, int, const char *) ATTRIBUTE_NORETURN; +void sprint_value (char *, addressT); +int had_errors (void); +int had_warnings (void); +void as_warn_value_out_of_range (char *, offsetT, offsetT, offsetT, char *, unsigned); +void as_bad_value_out_of_range (char *, offsetT, offsetT, offsetT, char *, unsigned); +void print_version_id (void); +char * app_push (void); +char * atof_ieee (char *, int, LITTLENUM_TYPE *); +char * ieee_md_atof (int, char *, int *, bfd_boolean); +char * vax_md_atof (int, char *, int *); +char * input_scrub_include_file (char *, char *); +void input_scrub_insert_line (const char *); +void input_scrub_insert_file (char *); +char * input_scrub_new_file (char *); +char * input_scrub_next_buffer (char **bufp); +size_t do_scrub_chars (size_t (*get) (char *, size_t), char *, size_t); +int gen_to_words (LITTLENUM_TYPE *, int, long); +int had_err (void); +int ignore_input (void); +void cond_finish_check (int); +void cond_exit_macro (int); +int seen_at_least_1_file (void); +void app_pop (char *); +void as_where (char **, unsigned int *); +void bump_line_counters (void); +void do_scrub_begin (int); +void input_scrub_begin (void); +void input_scrub_close (void); +void input_scrub_end (void); +int new_logical_line (char *, int); +int new_logical_line_flags (char *, int, int); +void subsegs_begin (void); +void subseg_change (segT, int); +segT subseg_new (const char *, subsegT); +segT subseg_force_new (const char *, subsegT); +void subseg_set (segT, subsegT); +int subseg_text_p (segT); +int seg_not_empty_p (segT); +void start_dependencies (char *); +void register_dependency (char *); +void print_dependencies (void); +segT subseg_get (const char *, int); + +const char *remap_debug_filename (const char *); +void add_debug_prefix_map (const char *); + +struct expressionS; +struct fix; +typedef struct symbol symbolS; +typedef struct frag fragS; + +/* literal.c */ +valueT add_to_literal_pool (symbolS *, valueT, segT, int); + +int check_eh_frame (struct expressionS *, unsigned int *); +int eh_frame_estimate_size_before_relax (fragS *); +int eh_frame_relax_frag (fragS *); +void eh_frame_convert_frag (fragS *); +int generic_force_reloc (struct fix *); + +#include "expr.h" /* Before targ-*.h */ + +/* This one starts the chain of target dependant headers. */ +#include "targ-env.h" + +#ifdef OBJ_MAYBE_ELF +#define IS_ELF (OUTPUT_FLAVOR == bfd_target_elf_flavour) +#else +#ifdef OBJ_ELF +#define IS_ELF 1 +#else +#define IS_ELF 0 +#endif +#endif + +#include "write.h" +#include "frags.h" +#include "hash.h" +#include "read.h" +#include "symbols.h" + +#include "tc.h" +#include "obj.h" + +#ifdef USE_EMULATIONS +#include "emul.h" +#endif +#include "listing.h" + +#ifdef H_TICK_HEX +extern int enable_h_tick_hex; +#endif + +#ifdef TC_M68K +/* True if we are assembling in m68k MRI mode. */ +COMMON int flag_m68k_mri; +#define DOLLAR_AMBIGU flag_m68k_mri +#else +#define flag_m68k_mri 0 +#endif + +#ifdef WARN_COMMENTS +COMMON int warn_comment; +COMMON unsigned int found_comment; +COMMON char * found_comment_file; +#endif + +#if defined OBJ_ELF || defined OBJ_MAYBE_ELF +/* If .size directive failure should be error or warning. */ +COMMON enum + { + size_check_error = 0, + size_check_warning + } +flag_size_check; +#endif + +#ifndef DOLLAR_AMBIGU +#define DOLLAR_AMBIGU 0 +#endif + +#ifndef NUMBERS_WITH_SUFFIX +#define NUMBERS_WITH_SUFFIX 0 +#endif + +#ifndef LOCAL_LABELS_DOLLAR +#define LOCAL_LABELS_DOLLAR 0 +#endif + +#ifndef LOCAL_LABELS_FB +#define LOCAL_LABELS_FB 0 +#endif + +#ifndef LABELS_WITHOUT_COLONS +#define LABELS_WITHOUT_COLONS 0 +#endif + +#ifndef NO_PSEUDO_DOT +#define NO_PSEUDO_DOT 0 +#endif + +#ifndef TEXT_SECTION_NAME +#define TEXT_SECTION_NAME ".text" +#define DATA_SECTION_NAME ".data" +#define BSS_SECTION_NAME ".bss" +#endif + +#ifndef OCTETS_PER_BYTE_POWER +#define OCTETS_PER_BYTE_POWER 0 +#endif +#ifndef OCTETS_PER_BYTE +#define OCTETS_PER_BYTE (1< + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifdef HAVE_LOCALE_H +# ifndef ENABLE_NLS + /* The Solaris version of locale.h always includes libintl.h. If we have + been configured with --disable-nls then ENABLE_NLS will not be defined + and the dummy definitions of bindtextdomain (et al) below will conflict + with the defintions in libintl.h. So we define these values to prevent + the bogus inclusion of libintl.h. */ +# define _LIBINTL_H +# define _LIBGETTEXT_H +# endif +# include +#endif + +#ifdef ENABLE_NLS +# include +# define _(String) gettext (String) +# ifdef gettext_noop +# define N_(String) gettext_noop (String) +# else +# define N_(String) (String) +# endif +#else +# define gettext(Msgid) (Msgid) +# define dgettext(Domainname, Msgid) (Msgid) +# define dcgettext(Domainname, Msgid, Category) (Msgid) +# define textdomain(Domainname) while (0) /* nothing */ +# define bindtextdomain(Domainname, Dirname) while (0) /* nothing */ +# define _(String) (String) +# define N_(String) (String) +#endif diff --git a/contrib/toolchain/binutils/gas/atof-generic.c b/contrib/toolchain/binutils/gas/atof-generic.c new file mode 100644 index 0000000000..40a9ff0492 --- /dev/null +++ b/contrib/toolchain/binutils/gas/atof-generic.c @@ -0,0 +1,614 @@ +/* atof_generic.c - turn a string of digits into a Flonum + Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1998, 1999, 2000, + 2001, 2003, 2005, 2006, 2007, 2009 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" +#include "safe-ctype.h" + +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (1) +#endif + +#ifdef TRACE +static void flonum_print (const FLONUM_TYPE *); +#endif + +#define ASSUME_DECIMAL_MARK_IS_DOT + +/***********************************************************************\ + * * + * Given a string of decimal digits , with optional decimal * + * mark and optional decimal exponent (place value) of the * + * lowest_order decimal digit: produce a floating point * + * number. The number is 'generic' floating point: our * + * caller will encode it for a specific machine architecture. * + * * + * Assumptions * + * uses base (radix) 2 * + * this machine uses 2's complement binary integers * + * target flonums use " " " " * + * target flonums exponents fit in a long * + * * + \***********************************************************************/ + +/* + + Syntax: + + ::= + ::= '+' | '-' | {empty} + ::= + | + | + | + + ::= {empty} + | + + ::= | + ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' + ::= {one character from "string_of_decimal_exponent_marks"} + ::= {one character from "string_of_decimal_marks"} + + */ + +int +atof_generic (/* return pointer to just AFTER number we read. */ + char **address_of_string_pointer, + /* At most one per number. */ + const char *string_of_decimal_marks, + const char *string_of_decimal_exponent_marks, + FLONUM_TYPE *address_of_generic_floating_point_number) +{ + int return_value; /* 0 means OK. */ + char *first_digit; + unsigned int number_of_digits_before_decimal; + unsigned int number_of_digits_after_decimal; + long decimal_exponent; + unsigned int number_of_digits_available; + char digits_sign_char; + + /* + * Scan the input string, abstracting (1)digits (2)decimal mark (3) exponent. + * It would be simpler to modify the string, but we don't; just to be nice + * to caller. + * We need to know how many digits we have, so we can allocate space for + * the digits' value. + */ + + char *p; + char c; + int seen_significant_digit; + +#ifdef ASSUME_DECIMAL_MARK_IS_DOT + gas_assert (string_of_decimal_marks[0] == '.' + && string_of_decimal_marks[1] == 0); +#define IS_DECIMAL_MARK(c) ((c) == '.') +#else +#define IS_DECIMAL_MARK(c) (0 != strchr (string_of_decimal_marks, (c))) +#endif + + first_digit = *address_of_string_pointer; + c = *first_digit; + + if (c == '-' || c == '+') + { + digits_sign_char = c; + first_digit++; + } + else + digits_sign_char = '+'; + + switch (first_digit[0]) + { + case 'n': + case 'N': + if (!strncasecmp ("nan", first_digit, 3)) + { + address_of_generic_floating_point_number->sign = 0; + address_of_generic_floating_point_number->exponent = 0; + address_of_generic_floating_point_number->leader = + address_of_generic_floating_point_number->low; + *address_of_string_pointer = first_digit + 3; + return 0; + } + break; + + case 'i': + case 'I': + if (!strncasecmp ("inf", first_digit, 3)) + { + address_of_generic_floating_point_number->sign = + digits_sign_char == '+' ? 'P' : 'N'; + address_of_generic_floating_point_number->exponent = 0; + address_of_generic_floating_point_number->leader = + address_of_generic_floating_point_number->low; + + first_digit += 3; + if (!strncasecmp ("inity", first_digit, 5)) + first_digit += 5; + + *address_of_string_pointer = first_digit; + + return 0; + } + break; + } + + number_of_digits_before_decimal = 0; + number_of_digits_after_decimal = 0; + decimal_exponent = 0; + seen_significant_digit = 0; + for (p = first_digit; + (((c = *p) != '\0') + && (!c || !IS_DECIMAL_MARK (c)) + && (!c || !strchr (string_of_decimal_exponent_marks, c))); + p++) + { + if (ISDIGIT (c)) + { + if (seen_significant_digit || c > '0') + { + ++number_of_digits_before_decimal; + seen_significant_digit = 1; + } + else + { + first_digit++; + } + } + else + { + break; /* p -> char after pre-decimal digits. */ + } + } /* For each digit before decimal mark. */ + +#ifndef OLD_FLOAT_READS + /* Ignore trailing 0's after the decimal point. The original code here + * (ifdef'd out) does not do this, and numbers like + * 4.29496729600000000000e+09 (2**31) + * come out inexact for some reason related to length of the digit + * string. + */ + if (c && IS_DECIMAL_MARK (c)) + { + unsigned int zeros = 0; /* Length of current string of zeros */ + + for (p++; (c = *p) && ISDIGIT (c); p++) + { + if (c == '0') + { + zeros++; + } + else + { + number_of_digits_after_decimal += 1 + zeros; + zeros = 0; + } + } + } +#else + if (c && IS_DECIMAL_MARK (c)) + { + for (p++; + (((c = *p) != '\0') + && (!c || !strchr (string_of_decimal_exponent_marks, c))); + p++) + { + if (ISDIGIT (c)) + { + /* This may be retracted below. */ + number_of_digits_after_decimal++; + + if ( /* seen_significant_digit || */ c > '0') + { + seen_significant_digit = TRUE; + } + } + else + { + if (!seen_significant_digit) + { + number_of_digits_after_decimal = 0; + } + break; + } + } /* For each digit after decimal mark. */ + } + + while (number_of_digits_after_decimal + && first_digit[number_of_digits_before_decimal + + number_of_digits_after_decimal] == '0') + --number_of_digits_after_decimal; +#endif + + if (flag_m68k_mri) + { + while (c == '_') + c = *++p; + } + if (c && strchr (string_of_decimal_exponent_marks, c)) + { + char digits_exponent_sign_char; + + c = *++p; + if (flag_m68k_mri) + { + while (c == '_') + c = *++p; + } + if (c && strchr ("+-", c)) + { + digits_exponent_sign_char = c; + c = *++p; + } + else + { + digits_exponent_sign_char = '+'; + } + + for (; (c); c = *++p) + { + if (ISDIGIT (c)) + { + decimal_exponent = decimal_exponent * 10 + c - '0'; + /* + * BUG! If we overflow here, we lose! + */ + } + else + { + break; + } + } + + if (digits_exponent_sign_char == '-') + { + decimal_exponent = -decimal_exponent; + } + } + + *address_of_string_pointer = p; + + number_of_digits_available = + number_of_digits_before_decimal + number_of_digits_after_decimal; + return_value = 0; + if (number_of_digits_available == 0) + { + address_of_generic_floating_point_number->exponent = 0; /* Not strictly necessary */ + address_of_generic_floating_point_number->leader + = -1 + address_of_generic_floating_point_number->low; + address_of_generic_floating_point_number->sign = digits_sign_char; + /* We have just concocted (+/-)0.0E0 */ + + } + else + { + int count; /* Number of useful digits left to scan. */ + + LITTLENUM_TYPE *digits_binary_low; + unsigned int precision; + unsigned int maximum_useful_digits; + unsigned int number_of_digits_to_use; + unsigned int more_than_enough_bits_for_digits; + unsigned int more_than_enough_littlenums_for_digits; + unsigned int size_of_digits_in_littlenums; + unsigned int size_of_digits_in_chars; + FLONUM_TYPE power_of_10_flonum; + FLONUM_TYPE digits_flonum; + + precision = (address_of_generic_floating_point_number->high + - address_of_generic_floating_point_number->low + + 1); /* Number of destination littlenums. */ + + /* Includes guard bits (two littlenums worth) */ + maximum_useful_digits = (((precision - 2)) + * ( (LITTLENUM_NUMBER_OF_BITS)) + * 1000000 / 3321928) + + 2; /* 2 :: guard digits. */ + + if (number_of_digits_available > maximum_useful_digits) + { + number_of_digits_to_use = maximum_useful_digits; + } + else + { + number_of_digits_to_use = number_of_digits_available; + } + + /* Cast these to SIGNED LONG first, otherwise, on systems with + LONG wider than INT (such as Alpha OSF/1), unsignedness may + cause unexpected results. */ + decimal_exponent += ((long) number_of_digits_before_decimal + - (long) number_of_digits_to_use); + + more_than_enough_bits_for_digits + = (number_of_digits_to_use * 3321928 / 1000000 + 1); + + more_than_enough_littlenums_for_digits + = (more_than_enough_bits_for_digits + / LITTLENUM_NUMBER_OF_BITS) + + 2; + + /* Compute (digits) part. In "12.34E56" this is the "1234" part. + Arithmetic is exact here. If no digits are supplied then this + part is a 0 valued binary integer. Allocate room to build up + the binary number as littlenums. We want this memory to + disappear when we leave this function. Assume no alignment + problems => (room for n objects) == n * (room for 1 + object). */ + + size_of_digits_in_littlenums = more_than_enough_littlenums_for_digits; + size_of_digits_in_chars = size_of_digits_in_littlenums + * sizeof (LITTLENUM_TYPE); + + digits_binary_low = (LITTLENUM_TYPE *) + alloca (size_of_digits_in_chars); + + memset ((char *) digits_binary_low, '\0', size_of_digits_in_chars); + + /* Digits_binary_low[] is allocated and zeroed. */ + + /* + * Parse the decimal digits as if * digits_low was in the units position. + * Emit a binary number into digits_binary_low[]. + * + * Use a large-precision version of: + * (((1st-digit) * 10 + 2nd-digit) * 10 + 3rd-digit ...) * 10 + last-digit + */ + + for (p = first_digit, count = number_of_digits_to_use; count; p++, --count) + { + c = *p; + if (ISDIGIT (c)) + { + /* + * Multiply by 10. Assume can never overflow. + * Add this digit to digits_binary_low[]. + */ + + long carry; + LITTLENUM_TYPE *littlenum_pointer; + LITTLENUM_TYPE *littlenum_limit; + + littlenum_limit = digits_binary_low + + more_than_enough_littlenums_for_digits + - 1; + + carry = c - '0'; /* char -> binary */ + + for (littlenum_pointer = digits_binary_low; + littlenum_pointer <= littlenum_limit; + littlenum_pointer++) + { + long work; + + work = carry + 10 * (long) (*littlenum_pointer); + *littlenum_pointer = work & LITTLENUM_MASK; + carry = work >> LITTLENUM_NUMBER_OF_BITS; + } + + if (carry != 0) + { + /* + * We have a GROSS internal error. + * This should never happen. + */ + as_fatal (_("failed sanity check")); + } + } + else + { + ++count; /* '.' doesn't alter digits used count. */ + } + } + + /* + * Digits_binary_low[] properly encodes the value of the digits. + * Forget about any high-order littlenums that are 0. + */ + while (digits_binary_low[size_of_digits_in_littlenums - 1] == 0 + && size_of_digits_in_littlenums >= 2) + size_of_digits_in_littlenums--; + + digits_flonum.low = digits_binary_low; + digits_flonum.high = digits_binary_low + size_of_digits_in_littlenums - 1; + digits_flonum.leader = digits_flonum.high; + digits_flonum.exponent = 0; + /* + * The value of digits_flonum . sign should not be important. + * We have already decided the output's sign. + * We trust that the sign won't influence the other parts of the number! + * So we give it a value for these reasons: + * (1) courtesy to humans reading/debugging + * these numbers so they don't get excited about strange values + * (2) in future there may be more meaning attached to sign, + * and what was + * harmless noise may become disruptive, ill-conditioned (or worse) + * input. + */ + digits_flonum.sign = '+'; + + { + /* + * Compute the mantssa (& exponent) of the power of 10. + * If successful, then multiply the power of 10 by the digits + * giving return_binary_mantissa and return_binary_exponent. + */ + + LITTLENUM_TYPE *power_binary_low; + int decimal_exponent_is_negative; + /* This refers to the "-56" in "12.34E-56". */ + /* FALSE: decimal_exponent is positive (or 0) */ + /* TRUE: decimal_exponent is negative */ + FLONUM_TYPE temporary_flonum; + LITTLENUM_TYPE *temporary_binary_low; + unsigned int size_of_power_in_littlenums; + unsigned int size_of_power_in_chars; + + size_of_power_in_littlenums = precision; + /* Precision has a built-in fudge factor so we get a few guard bits. */ + + decimal_exponent_is_negative = decimal_exponent < 0; + if (decimal_exponent_is_negative) + { + decimal_exponent = -decimal_exponent; + } + + /* From now on: the decimal exponent is > 0. Its sign is separate. */ + + size_of_power_in_chars = size_of_power_in_littlenums + * sizeof (LITTLENUM_TYPE) + 2; + + power_binary_low = (LITTLENUM_TYPE *) alloca (size_of_power_in_chars); + temporary_binary_low = (LITTLENUM_TYPE *) alloca (size_of_power_in_chars); + memset ((char *) power_binary_low, '\0', size_of_power_in_chars); + *power_binary_low = 1; + power_of_10_flonum.exponent = 0; + power_of_10_flonum.low = power_binary_low; + power_of_10_flonum.leader = power_binary_low; + power_of_10_flonum.high = power_binary_low + size_of_power_in_littlenums - 1; + power_of_10_flonum.sign = '+'; + temporary_flonum.low = temporary_binary_low; + temporary_flonum.high = temporary_binary_low + size_of_power_in_littlenums - 1; + /* + * (power) == 1. + * Space for temporary_flonum allocated. + */ + + /* + * ... + * + * WHILE more bits + * DO find next bit (with place value) + * multiply into power mantissa + * OD + */ + { + int place_number_limit; + /* Any 10^(2^n) whose "n" exceeds this */ + /* value will fall off the end of */ + /* flonum_XXXX_powers_of_ten[]. */ + int place_number; + const FLONUM_TYPE *multiplicand; /* -> 10^(2^n) */ + + place_number_limit = table_size_of_flonum_powers_of_ten; + + multiplicand = (decimal_exponent_is_negative + ? flonum_negative_powers_of_ten + : flonum_positive_powers_of_ten); + + for (place_number = 1;/* Place value of this bit of exponent. */ + decimal_exponent;/* Quit when no more 1 bits in exponent. */ + decimal_exponent >>= 1, place_number++) + { + if (decimal_exponent & 1) + { + if (place_number > place_number_limit) + { + /* The decimal exponent has a magnitude so great + that our tables can't help us fragment it. + Although this routine is in error because it + can't imagine a number that big, signal an + error as if it is the user's fault for + presenting such a big number. */ + return_value = ERROR_EXPONENT_OVERFLOW; + /* quit out of loop gracefully */ + decimal_exponent = 0; + } + else + { +#ifdef TRACE + printf ("before multiply, place_number = %d., power_of_10_flonum:\n", + place_number); + + flonum_print (&power_of_10_flonum); + (void) putchar ('\n'); +#endif +#ifdef TRACE + printf ("multiplier:\n"); + flonum_print (multiplicand + place_number); + (void) putchar ('\n'); +#endif + flonum_multip (multiplicand + place_number, + &power_of_10_flonum, &temporary_flonum); +#ifdef TRACE + printf ("after multiply:\n"); + flonum_print (&temporary_flonum); + (void) putchar ('\n'); +#endif + flonum_copy (&temporary_flonum, &power_of_10_flonum); +#ifdef TRACE + printf ("after copy:\n"); + flonum_print (&power_of_10_flonum); + (void) putchar ('\n'); +#endif + } /* If this bit of decimal_exponent was computable.*/ + } /* If this bit of decimal_exponent was set. */ + } /* For each bit of binary representation of exponent */ +#ifdef TRACE + printf ("after computing power_of_10_flonum:\n"); + flonum_print (&power_of_10_flonum); + (void) putchar ('\n'); +#endif + } + + } + + /* + * power_of_10_flonum is power of ten in binary (mantissa) , (exponent). + * It may be the number 1, in which case we don't NEED to multiply. + * + * Multiply (decimal digits) by power_of_10_flonum. + */ + + flonum_multip (&power_of_10_flonum, &digits_flonum, address_of_generic_floating_point_number); + /* Assert sign of the number we made is '+'. */ + address_of_generic_floating_point_number->sign = digits_sign_char; + + } + return return_value; +} + +#ifdef TRACE +static void +flonum_print (f) + const FLONUM_TYPE *f; +{ + LITTLENUM_TYPE *lp; + char littlenum_format[10]; + sprintf (littlenum_format, " %%0%dx", sizeof (LITTLENUM_TYPE) * 2); +#define print_littlenum(LP) (printf (littlenum_format, LP)) + printf ("flonum @%p %c e%ld", f, f->sign, f->exponent); + if (f->low < f->high) + for (lp = f->high; lp >= f->low; lp--) + print_littlenum (*lp); + else + for (lp = f->low; lp <= f->high; lp++) + print_littlenum (*lp); + printf ("\n"); + fflush (stdout); +} +#endif + +/* end of atof_generic.c */ diff --git a/contrib/toolchain/binutils/gas/bignum.h b/contrib/toolchain/binutils/gas/bignum.h new file mode 100644 index 0000000000..41c3559988 --- /dev/null +++ b/contrib/toolchain/binutils/gas/bignum.h @@ -0,0 +1,42 @@ +/* bignum.h-arbitrary precision integers + Copyright 1987, 1992, 2003, 2005, 2007 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the 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, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/***********************************************************************\ + * * + * Arbitrary-precision integer arithmetic. * + * For speed, we work in groups of bits, even though this * + * complicates algorithms. * + * Each group of bits is called a 'littlenum'. * + * A bunch of littlenums representing a (possibly large) * + * integer is called a 'bignum'. * + * Bignums are >= 0. * + * * + \***********************************************************************/ + +#define LITTLENUM_NUMBER_OF_BITS (16) +#define LITTLENUM_RADIX (1 << LITTLENUM_NUMBER_OF_BITS) +#define LITTLENUM_MASK (0xFFFF) +#define LITTLENUM_SHIFT (1) +#define CHARS_PER_LITTLENUM (1 << LITTLENUM_SHIFT) +#ifndef BITS_PER_CHAR +#define BITS_PER_CHAR (8) +#endif + +typedef unsigned short LITTLENUM_TYPE; diff --git a/contrib/toolchain/binutils/gas/bit_fix.h b/contrib/toolchain/binutils/gas/bit_fix.h new file mode 100644 index 0000000000..a83bddc3f0 --- /dev/null +++ b/contrib/toolchain/binutils/gas/bit_fix.h @@ -0,0 +1,48 @@ +/* bit_fix.h + Copyright 1987, 1992, 2000, 2001, 2003, 2005, 2007 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* The bit_fix was implemented to support machines that need variables + to be inserted in bitfields other than 1, 2 and 4 bytes. + Furthermore it gives us a possibility to mask in bits in the symbol + when it's fixed in the objectcode and check the symbols limits. + + The or-mask is used to set the huffman bits in displacements for the + ns32k port. + The acbi, addqi, movqi, cmpqi instruction requires an assembler that + can handle bitfields. Ie. handle an expression, evaluate it and insert + the result in some bitfield. (eg: 5 bits in a short field of an opcode). */ + +#ifndef __bit_fix_h__ +#define __bit_fix_h__ + +struct bit_fix { + int fx_bit_size; /* Length of bitfield */ + int fx_bit_offset; /* Bit offset to bitfield */ + long fx_bit_base; /* Where do we apply the bitfix. + If this is zero, default is assumed. */ + long fx_bit_base_adj; /* Adjustment of base */ + long fx_bit_max; /* Signextended max for bitfield */ + long fx_bit_min; /* Signextended min for bitfield */ + long fx_bit_add; /* Or mask, used for huffman prefix */ +}; +typedef struct bit_fix bit_fixS; + +#endif /* __bit_fix_h__ */ diff --git a/contrib/toolchain/binutils/gas/compress-debug.c b/contrib/toolchain/binutils/gas/compress-debug.c new file mode 100644 index 0000000000..09076002d7 --- /dev/null +++ b/contrib/toolchain/binutils/gas/compress-debug.c @@ -0,0 +1,117 @@ +/* compress-debug.c - compress debug sections + Copyright 2010 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "config.h" +#include +#include "ansidecl.h" +#include "compress-debug.h" + +#ifdef HAVE_ZLIB_H +#include +#endif + +/* Initialize the compression engine. */ + +struct z_stream_s * +compress_init (void) +{ +#ifndef HAVE_ZLIB_H + return NULL; +#else + static struct z_stream_s strm; + + strm.zalloc = NULL; + strm.zfree = NULL; + strm.opaque = NULL; + deflateInit (&strm, Z_DEFAULT_COMPRESSION); + return &strm; +#endif /* HAVE_ZLIB_H */ +} + +/* Stream the contents of a frag to the compression engine. Output + from the engine goes into the current frag on the obstack. */ + +int +compress_data (struct z_stream_s *strm ATTRIBUTE_UNUSED, + const char **next_in ATTRIBUTE_UNUSED, + int *avail_in ATTRIBUTE_UNUSED, + char **next_out ATTRIBUTE_UNUSED, + int *avail_out ATTRIBUTE_UNUSED) +{ +#ifndef HAVE_ZLIB_H + return -1; +#else + int out_size = 0; + int x; + + strm->next_in = (Bytef *) (*next_in); + strm->avail_in = *avail_in; + strm->next_out = (Bytef *) (*next_out); + strm->avail_out = *avail_out; + + x = deflate (strm, Z_NO_FLUSH); + if (x != Z_OK) + return -1; + + out_size = *avail_out - strm->avail_out; + *next_in = (char *) (strm->next_in); + *avail_in = strm->avail_in; + *next_out = (char *) (strm->next_out); + *avail_out = strm->avail_out; + + return out_size; +#endif /* HAVE_ZLIB_H */ +} + +/* Finish the compression and consume the remaining compressed output. + Returns -1 for error, 0 when done, 1 when more output buffer is + needed. */ + +int +compress_finish (struct z_stream_s *strm ATTRIBUTE_UNUSED, + char **next_out ATTRIBUTE_UNUSED, + int *avail_out ATTRIBUTE_UNUSED, + int *out_size ATTRIBUTE_UNUSED) +{ +#ifndef HAVE_ZLIB_H + return -1; +#else + int x; + + strm->avail_in = 0; + strm->next_out = (Bytef *) (*next_out); + strm->avail_out = *avail_out; + + x = deflate (strm, Z_FINISH); + + *out_size = *avail_out - strm->avail_out; + *next_out = (char *) (strm->next_out); + *avail_out = strm->avail_out; + + if (x == Z_STREAM_END) + { + deflateEnd (strm); + return 0; + } + if (strm->avail_out != 0) + return -1; + return 1; +#endif /* HAVE_ZLIB_H */ +} diff --git a/contrib/toolchain/binutils/gas/compress-debug.h b/contrib/toolchain/binutils/gas/compress-debug.h new file mode 100644 index 0000000000..9d821e9155 --- /dev/null +++ b/contrib/toolchain/binutils/gas/compress-debug.h @@ -0,0 +1,39 @@ +/* compress-debug.h - Header file for compressed debug sections. + Copyright 2010 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef COMPRESS_DEBUG_H +#define COMPRESS_DEBUG_H + +struct z_stream_s; + +/* Initialize the compression engine. */ +extern struct z_stream_s * +compress_init (void); + +/* Stream the contents of a frag to the compression engine. Output + from the engine goes into the current frag on the obstack. */ +extern int +compress_data (struct z_stream_s *, const char **, int *, char **, int *); + +/* Finish the compression and consume the remaining compressed output. */ +extern int +compress_finish (struct z_stream_s *, char **, int *, int *); + +#endif /* COMPRESS_DEBUG_H */ diff --git a/contrib/toolchain/binutils/gas/cond.c b/contrib/toolchain/binutils/gas/cond.c new file mode 100644 index 0000000000..c30912334f --- /dev/null +++ b/contrib/toolchain/binutils/gas/cond.c @@ -0,0 +1,577 @@ +/* cond.c - conditional assembly pseudo-ops, and .include + Copyright 1990, 1991, 1992, 1993, 1995, 1997, 1998, 2000, 2001, 2002, + 2003, 2005, 2006, 2007 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" +#include "sb.h" +#include "macro.h" + +#include "obstack.h" + +/* This is allocated to grow and shrink as .ifdef/.endif pairs are + scanned. */ +struct obstack cond_obstack; + +struct file_line { + char *file; + unsigned int line; +}; + +/* We push one of these structures for each .if, and pop it at the + .endif. */ + +struct conditional_frame { + /* The source file & line number of the "if". */ + struct file_line if_file_line; + /* The source file & line of the "else". */ + struct file_line else_file_line; + /* The previous conditional. */ + struct conditional_frame *previous_cframe; + /* Have we seen an else yet? */ + int else_seen; + /* Whether we are currently ignoring input. */ + int ignoring; + /* Whether a conditional at a higher level is ignoring input. + Set also when a branch of an "if .. elseif .." tree has matched + to prevent further matches. */ + int dead_tree; + /* Macro nesting level at which this conditional was created. */ + int macro_nest; +}; + +static void initialize_cframe (struct conditional_frame *cframe); +static char *get_mri_string (int, int *); + +static struct conditional_frame *current_cframe = NULL; + +/* Performs the .ifdef (test_defined == 1) and + the .ifndef (test_defined == 0) pseudo op. */ + +void +s_ifdef (int test_defined) +{ + /* Points to name of symbol. */ + char *name; + /* Points to symbol. */ + symbolS *symbolP; + struct conditional_frame cframe; + char c; + + /* Leading whitespace is part of operand. */ + SKIP_WHITESPACE (); + name = input_line_pointer; + + if (!is_name_beginner (*name)) + { + as_bad (_("invalid identifier for \".ifdef\"")); + obstack_1grow (&cond_obstack, 0); + ignore_rest_of_line (); + return; + } + + c = get_symbol_end (); + symbolP = symbol_find (name); + *input_line_pointer = c; + + initialize_cframe (&cframe); + + if (cframe.dead_tree) + cframe.ignoring = 1; + else + { + int is_defined; + + /* Use the same definition of 'defined' as .equiv so that a symbol + which has been referenced but not yet given a value/address is + considered to be undefined. */ + is_defined = + symbolP != NULL + && (S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP)) + && S_GET_SEGMENT (symbolP) != reg_section; + + cframe.ignoring = ! (test_defined ^ is_defined); + } + + current_cframe = ((struct conditional_frame *) + obstack_copy (&cond_obstack, &cframe, + sizeof (cframe))); + + if (LISTING_SKIP_COND () + && cframe.ignoring + && (cframe.previous_cframe == NULL + || ! cframe.previous_cframe->ignoring)) + listing_list (2); + + demand_empty_rest_of_line (); +} + +void +s_if (int arg) +{ + expressionS operand; + struct conditional_frame cframe; + int t; + char *stop = NULL; + char stopc; + + if (flag_mri) + stop = mri_comment_field (&stopc); + + /* Leading whitespace is part of operand. */ + SKIP_WHITESPACE (); + + if (current_cframe != NULL && current_cframe->ignoring) + { + operand.X_add_number = 0; + while (! is_end_of_line[(unsigned char) *input_line_pointer]) + ++input_line_pointer; + } + else + { + expression_and_evaluate (&operand); + if (operand.X_op != O_constant) + as_bad (_("non-constant expression in \".if\" statement")); + } + + switch ((operatorT) arg) + { + case O_eq: t = operand.X_add_number == 0; break; + case O_ne: t = operand.X_add_number != 0; break; + case O_lt: t = operand.X_add_number < 0; break; + case O_le: t = operand.X_add_number <= 0; break; + case O_ge: t = operand.X_add_number >= 0; break; + case O_gt: t = operand.X_add_number > 0; break; + default: + abort (); + return; + } + + /* If the above error is signaled, this will dispatch + using an undefined result. No big deal. */ + initialize_cframe (&cframe); + cframe.ignoring = cframe.dead_tree || ! t; + current_cframe = ((struct conditional_frame *) + obstack_copy (&cond_obstack, &cframe, sizeof (cframe))); + + if (LISTING_SKIP_COND () + && cframe.ignoring + && (cframe.previous_cframe == NULL + || ! cframe.previous_cframe->ignoring)) + listing_list (2); + + if (flag_mri) + mri_comment_end (stop, stopc); + + demand_empty_rest_of_line (); +} + +/* Performs the .ifb (test_blank == 1) and + the .ifnb (test_blank == 0) pseudo op. */ + +void +s_ifb (int test_blank) +{ + struct conditional_frame cframe; + + initialize_cframe (&cframe); + + if (cframe.dead_tree) + cframe.ignoring = 1; + else + { + int is_eol; + + SKIP_WHITESPACE (); + is_eol = is_end_of_line[(unsigned char) *input_line_pointer]; + cframe.ignoring = (test_blank == !is_eol); + } + + current_cframe = ((struct conditional_frame *) + obstack_copy (&cond_obstack, &cframe, + sizeof (cframe))); + + if (LISTING_SKIP_COND () + && cframe.ignoring + && (cframe.previous_cframe == NULL + || ! cframe.previous_cframe->ignoring)) + listing_list (2); + + ignore_rest_of_line (); +} + +/* Get a string for the MRI IFC or IFNC pseudo-ops. */ + +static char * +get_mri_string (int terminator, int *len) +{ + char *ret; + char *s; + + SKIP_WHITESPACE (); + s = ret = input_line_pointer; + if (*input_line_pointer == '\'') + { + ++s; + ++input_line_pointer; + while (! is_end_of_line[(unsigned char) *input_line_pointer]) + { + *s++ = *input_line_pointer++; + if (s[-1] == '\'') + { + if (*input_line_pointer != '\'') + break; + ++input_line_pointer; + } + } + SKIP_WHITESPACE (); + } + else + { + while (*input_line_pointer != terminator + && ! is_end_of_line[(unsigned char) *input_line_pointer]) + ++input_line_pointer; + s = input_line_pointer; + while (s > ret && (s[-1] == ' ' || s[-1] == '\t')) + --s; + } + + *len = s - ret; + return ret; +} + +/* The MRI IFC and IFNC pseudo-ops. */ + +void +s_ifc (int arg) +{ + char *stop = NULL; + char stopc; + char *s1, *s2; + int len1, len2; + int res; + struct conditional_frame cframe; + + if (flag_mri) + stop = mri_comment_field (&stopc); + + s1 = get_mri_string (',', &len1); + + if (*input_line_pointer != ',') + as_bad (_("bad format for ifc or ifnc")); + else + ++input_line_pointer; + + s2 = get_mri_string (';', &len2); + + res = len1 == len2 && strncmp (s1, s2, len1) == 0; + + initialize_cframe (&cframe); + cframe.ignoring = cframe.dead_tree || ! (res ^ arg); + current_cframe = ((struct conditional_frame *) + obstack_copy (&cond_obstack, &cframe, sizeof (cframe))); + + if (LISTING_SKIP_COND () + && cframe.ignoring + && (cframe.previous_cframe == NULL + || ! cframe.previous_cframe->ignoring)) + listing_list (2); + + if (flag_mri) + mri_comment_end (stop, stopc); + + demand_empty_rest_of_line (); +} + +void +s_elseif (int arg) +{ + if (current_cframe == NULL) + { + as_bad (_("\".elseif\" without matching \".if\"")); + } + else if (current_cframe->else_seen) + { + as_bad (_("\".elseif\" after \".else\"")); + as_bad_where (current_cframe->else_file_line.file, + current_cframe->else_file_line.line, + _("here is the previous \".else\"")); + as_bad_where (current_cframe->if_file_line.file, + current_cframe->if_file_line.line, + _("here is the previous \".if\"")); + } + else + { + as_where (¤t_cframe->else_file_line.file, + ¤t_cframe->else_file_line.line); + + current_cframe->dead_tree |= !current_cframe->ignoring; + current_cframe->ignoring = current_cframe->dead_tree; + } + + if (current_cframe == NULL || current_cframe->ignoring) + { + while (! is_end_of_line[(unsigned char) *input_line_pointer]) + ++input_line_pointer; + + if (current_cframe == NULL) + return; + } + else + { + expressionS operand; + int t; + + /* Leading whitespace is part of operand. */ + SKIP_WHITESPACE (); + + expression_and_evaluate (&operand); + if (operand.X_op != O_constant) + as_bad (_("non-constant expression in \".elseif\" statement")); + + switch ((operatorT) arg) + { + case O_eq: t = operand.X_add_number == 0; break; + case O_ne: t = operand.X_add_number != 0; break; + case O_lt: t = operand.X_add_number < 0; break; + case O_le: t = operand.X_add_number <= 0; break; + case O_ge: t = operand.X_add_number >= 0; break; + case O_gt: t = operand.X_add_number > 0; break; + default: + abort (); + return; + } + + current_cframe->ignoring = current_cframe->dead_tree || ! t; + } + + if (LISTING_SKIP_COND () + && (current_cframe->previous_cframe == NULL + || ! current_cframe->previous_cframe->ignoring)) + { + if (! current_cframe->ignoring) + listing_list (1); + else + listing_list (2); + } + + demand_empty_rest_of_line (); +} + +void +s_endif (int arg ATTRIBUTE_UNUSED) +{ + struct conditional_frame *hold; + + if (current_cframe == NULL) + { + as_bad (_("\".endif\" without \".if\"")); + } + else + { + if (LISTING_SKIP_COND () + && current_cframe->ignoring + && (current_cframe->previous_cframe == NULL + || ! current_cframe->previous_cframe->ignoring)) + listing_list (1); + + hold = current_cframe; + current_cframe = current_cframe->previous_cframe; + obstack_free (&cond_obstack, hold); + } /* if one pop too many */ + + if (flag_mri) + { + while (! is_end_of_line[(unsigned char) *input_line_pointer]) + ++input_line_pointer; + } + + demand_empty_rest_of_line (); +} + +void +s_else (int arg ATTRIBUTE_UNUSED) +{ + if (current_cframe == NULL) + { + as_bad (_("\".else\" without matching \".if\"")); + } + else if (current_cframe->else_seen) + { + as_bad (_("duplicate \".else\"")); + as_bad_where (current_cframe->else_file_line.file, + current_cframe->else_file_line.line, + _("here is the previous \".else\"")); + as_bad_where (current_cframe->if_file_line.file, + current_cframe->if_file_line.line, + _("here is the previous \".if\"")); + } + else + { + as_where (¤t_cframe->else_file_line.file, + ¤t_cframe->else_file_line.line); + + current_cframe->ignoring = + current_cframe->dead_tree | !current_cframe->ignoring; + + if (LISTING_SKIP_COND () + && (current_cframe->previous_cframe == NULL + || ! current_cframe->previous_cframe->ignoring)) + { + if (! current_cframe->ignoring) + listing_list (1); + else + listing_list (2); + } + + current_cframe->else_seen = 1; + } + + if (flag_mri) + { + while (! is_end_of_line[(unsigned char) *input_line_pointer]) + ++input_line_pointer; + } + + demand_empty_rest_of_line (); +} + +void +s_ifeqs (int arg) +{ + char *s1, *s2; + int len1, len2; + int res; + struct conditional_frame cframe; + + s1 = demand_copy_C_string (&len1); + + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + { + as_bad (_(".ifeqs syntax error")); + ignore_rest_of_line (); + return; + } + + ++input_line_pointer; + + s2 = demand_copy_C_string (&len2); + + res = len1 == len2 && strncmp (s1, s2, len1) == 0; + + initialize_cframe (&cframe); + cframe.ignoring = cframe.dead_tree || ! (res ^ arg); + current_cframe = ((struct conditional_frame *) + obstack_copy (&cond_obstack, &cframe, sizeof (cframe))); + + if (LISTING_SKIP_COND () + && cframe.ignoring + && (cframe.previous_cframe == NULL + || ! cframe.previous_cframe->ignoring)) + listing_list (2); + + demand_empty_rest_of_line (); +} + +int +ignore_input (void) +{ + char *s; + + s = input_line_pointer; + + if (NO_PSEUDO_DOT || flag_m68k_mri) + { + if (s[-1] != '.') + --s; + } + else + { + if (s[-1] != '.') + return (current_cframe != NULL) && (current_cframe->ignoring); + } + + /* We cannot ignore certain pseudo ops. */ + if (((s[0] == 'i' + || s[0] == 'I') + && (!strncasecmp (s, "if", 2) + || !strncasecmp (s, "ifdef", 5) + || !strncasecmp (s, "ifndef", 6))) + || ((s[0] == 'e' + || s[0] == 'E') + && (!strncasecmp (s, "else", 4) + || !strncasecmp (s, "endif", 5) + || !strncasecmp (s, "endc", 4)))) + return 0; + + return (current_cframe != NULL) && (current_cframe->ignoring); +} + +static void +initialize_cframe (struct conditional_frame *cframe) +{ + memset (cframe, 0, sizeof (*cframe)); + as_where (&cframe->if_file_line.file, + &cframe->if_file_line.line); + cframe->previous_cframe = current_cframe; + cframe->dead_tree = current_cframe != NULL && current_cframe->ignoring; + cframe->macro_nest = macro_nest; +} + +/* Give an error if a conditional is unterminated inside a macro or + the assembly as a whole. If NEST is non negative, we are being + called because of the end of a macro expansion. If NEST is + negative, we are being called at the of the input files. */ + +void +cond_finish_check (int nest) +{ + if (current_cframe != NULL && current_cframe->macro_nest >= nest) + { + if (nest >= 0) + as_bad (_("end of macro inside conditional")); + else + as_bad (_("end of file inside conditional")); + as_bad_where (current_cframe->if_file_line.file, + current_cframe->if_file_line.line, + _("here is the start of the unterminated conditional")); + if (current_cframe->else_seen) + as_bad_where (current_cframe->else_file_line.file, + current_cframe->else_file_line.line, + _("here is the \"else\" of the unterminated conditional")); + } +} + +/* This function is called when we exit out of a macro. We assume + that any conditionals which began within the macro are correctly + nested, and just pop them off the stack. */ + +void +cond_exit_macro (int nest) +{ + while (current_cframe != NULL && current_cframe->macro_nest >= nest) + { + struct conditional_frame *hold; + + hold = current_cframe; + current_cframe = current_cframe->previous_cframe; + obstack_free (&cond_obstack, hold); + } +} diff --git a/contrib/toolchain/binutils/gas/config.h b/contrib/toolchain/binutils/gas/config.h new file mode 100644 index 0000000000..04a37b0b68 --- /dev/null +++ b/contrib/toolchain/binutils/gas/config.h @@ -0,0 +1,363 @@ +/* config.h. Generated from config.in by configure. */ +/* config.in. Generated from configure.in by autoheader. */ + +/* Check that config.h is #included before system headers + (this works only for glibc, but that should be enough). */ +#if defined(__GLIBC__) && !defined(__FreeBSD_kernel__) && !defined(__CONFIG_H__) +# error config.h must be #included before system headers +#endif +#define __CONFIG_H__ 1 + +/* Define if building universal (internal helper macro) */ +/* #undef AC_APPLE_UNIVERSAL_BUILD */ + +/* Define if using AIX 5.2 value for C_WEAKEXT. */ +/* #undef AIX_WEAK_SUPPORT */ + +/* assert broken? */ +/* #undef BROKEN_ASSERT */ + +/* Define to one of `_getb67', `GETB67', `getb67' for Cray-2 and Cray-YMP + systems. This function is required for `alloca.c' support on those systems. + */ +/* #undef CRAY_STACKSEG_END */ + +/* Compiling cross-assembler? */ +/* #undef CROSS_COMPILE */ + +/* Define to 1 if using `alloca.c'. */ +/* #undef C_ALLOCA */ + +/* Default architecture. */ +#define DEFAULT_ARCH "i386" + +/* Default CRIS architecture. */ +/* #undef DEFAULT_CRIS_ARCH */ + +/* Default emulation. */ +#define DEFAULT_EMULATION "" + +/* Supported emulations. */ +#define EMULATIONS + +/* Define if you want run-time sanity checks. */ +/* #undef ENABLE_CHECKING */ + +/* Define to 1 if translation of program messages to the user's native + language is requested. */ +/* #undef ENABLE_NLS */ + +/* Define to 1 if you have `alloca', as a function or macro. */ +#define HAVE_ALLOCA 1 + +/* Define to 1 if you have and it should be used (not on Ultrix). + */ +/* #undef HAVE_ALLOCA_H */ + +/* Define to 1 if you have the declaration of `free', and to 0 if you don't. + */ +#define HAVE_DECL_FREE 1 + +/* Define to 1 if you have the declaration of `getenv', and to 0 if you don't. + */ +#define HAVE_DECL_GETENV 1 + +/* Is the prototype for getopt in in the expected format? */ +#define HAVE_DECL_GETOPT 1 + +/* Define to 1 if you have the declaration of `malloc', and to 0 if you don't. + */ +#define HAVE_DECL_MALLOC 1 + +/* Define to 1 if you have the declaration of `mempcpy', and to 0 if you + don't. */ +#define HAVE_DECL_MEMPCPY 0 + +/* Define to 1 if you have the declaration of `realloc', and to 0 if you + don't. */ +#define HAVE_DECL_REALLOC 1 + +/* Define to 1 if you have the declaration of `stpcpy', and to 0 if you don't. + */ +#define HAVE_DECL_STPCPY 0 + +/* Define to 1 if you have the declaration of `strstr', and to 0 if you don't. + */ +#define HAVE_DECL_STRSTR 1 + +/* Define to 1 if you have the declaration of `vsnprintf', and to 0 if you + don't. */ +#define HAVE_DECL_VSNPRINTF 1 + +/* Define to 1 if you have the header file. */ +/* #undef HAVE_DLFCN_H */ + +/* Define to 1 if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define if your file defines LC_MESSAGES. */ +/* #undef HAVE_LC_MESSAGES */ + +/* Define to 1 if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `remove' function. */ +#define HAVE_REMOVE + +/* Define to 1 if you have the `sbrk' function. */ +/* #undef HAVE_SBRK */ + +/* Define to 1 if you have the `setlocale' function. */ +#define HAVE_SETLOCALE 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if has struct stat.st_mtim.tv_nsec */ +/* #undef HAVE_ST_MTIM_TV_NSEC */ + +/* Define if has struct stat.st_mtim.tv_sec */ +/* #undef HAVE_ST_MTIM_TV_SEC */ + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_TIME_H 1 + +/* Define if has struct tm.tm_gmtoff. */ +/* #undef HAVE_TM_GMTOFF */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `unlink' function. */ +#define HAVE_UNLINK 1 + +/* Define to 1 if you have the header file. */ +#define HAVE_ZLIB_H 1 + +/* Using i386 COFF? */ +#define I386COFF 1 + +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#define LT_OBJDIR ".libs/" + +/* Using m68k COFF? */ +/* #undef M68KCOFF */ + +/* Using m88k COFF? */ +/* #undef M88KCOFF */ + +/* Default CPU for MIPS targets. */ +/* #undef MIPS_CPU_STRING_DEFAULT */ + +/* Generate 64-bit code by default on MIPS targets. */ +/* #undef MIPS_DEFAULT_64BIT */ + +/* Choose a default ABI for MIPS targets. */ +/* #undef MIPS_DEFAULT_ABI */ + +/* Define if environ is not declared in system header files. */ +/* #undef NEED_DECLARATION_ENVIRON */ + +/* Define if errno is not declared in system header files. */ +/* #undef NEED_DECLARATION_ERRNO */ + +/* Define if ffs is not declared in system header files. */ +#define NEED_DECLARATION_FFS 1 + +/* Define if free is not declared in system header files. */ +/* #undef NEED_DECLARATION_FREE */ + +/* Define if malloc is not declared in system header files. */ +/* #undef NEED_DECLARATION_MALLOC */ + +/* Define if sbrk is not declared in system header files. */ +#define NEED_DECLARATION_SBRK 1 + +/* Define if strstr is not declared in system header files. */ +/* #undef NEED_DECLARATION_STRSTR */ + +/* a.out support? */ +/* #undef OBJ_MAYBE_AOUT */ + +/* b.out support? */ +/* #undef OBJ_MAYBE_BOUT */ + +/* COFF support? */ +/* #undef OBJ_MAYBE_COFF */ + +/* ECOFF support? */ +/* #undef OBJ_MAYBE_ECOFF */ + +/* ELF support? */ +/* #undef OBJ_MAYBE_ELF */ + +/* generic support? */ +/* #undef OBJ_MAYBE_GENERIC */ + +/* SOM support? */ +/* #undef OBJ_MAYBE_SOM */ + +/* Name of package */ +#define PACKAGE "gas" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "" + +/* Define to the home page for this package. */ +#define PACKAGE_URL "" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "" + +/* Define if defaulting to ELF on SCO 5. */ +/* #undef SCO_ELF */ + +/* If using the C implementation of alloca, define if you know the + direction of stack growth for your system; otherwise it will be + automatically deduced at runtime. + STACK_DIRECTION > 0 => grows toward higher addresses + STACK_DIRECTION < 0 => grows toward lower addresses + STACK_DIRECTION = 0 => direction of growth unknown */ +/* #undef STACK_DIRECTION */ + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Using strict COFF? */ +/* #undef STRICTCOFF */ + +/* Define if you can safely include both and . */ +#define STRING_WITH_STRINGS 1 + +/* Target alias. */ +#define TARGET_ALIAS "mingw32" + +/* Define as 1 if big endian. */ +/* #undef TARGET_BYTES_BIG_ENDIAN */ + +/* Canonical target. */ +#define TARGET_CANONICAL "i686-pc-mingw32" + +/* Target CPU. */ +#define TARGET_CPU "i686" + +/* Target OS. */ +#define TARGET_OS "mingw32" + +/* Define if default target is PowerPC Solaris. */ +/* #undef TARGET_SOLARIS_COMMENT */ + +/* Define if target is Symbian OS. */ +/* #undef TARGET_SYMBIAN */ + +/* Target vendor. */ +#define TARGET_VENDOR "pc" + +/* Use b modifier when opening binary files? */ +#define USE_BINARY_FOPEN 1 + +/* Use emulation support? */ +/* #undef USE_EMULATIONS */ + +/* Allow use of E_MIPS_ABI_O32 on MIPS targets. */ +/* #undef USE_E_MIPS_ABI_O32 */ + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# define _ALL_SOURCE 1 +#endif +/* Enable GNU extensions on systems that have them. */ +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# define _POSIX_PTHREAD_SEMANTICS 1 +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# define _TANDEM_SOURCE 1 +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# define __EXTENSIONS__ 1 +#endif + + +/* Using cgen code? */ +/* #undef USING_CGEN */ + +/* Version number of package */ +#define VERSION "2.24" + +/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most + significant byte first (like Motorola and SPARC, unlike Intel). */ +#if defined AC_APPLE_UNIVERSAL_BUILD +# if defined __BIG_ENDIAN__ +# define WORDS_BIGENDIAN 1 +# endif +#else +# ifndef WORDS_BIGENDIAN +/* # undef WORDS_BIGENDIAN */ +# endif +#endif + +/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a + `char[]'. */ +/* #undef YYTEXT_POINTER */ + +/* Number of bits in a file offset, on hosts where this is settable. */ +/* #undef _FILE_OFFSET_BITS */ + +/* Define for large files, on AIX-style hosts. */ +/* #undef _LARGE_FILES */ + +/* Define to 1 if on MINIX. */ +/* #undef _MINIX */ + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +/* #undef _POSIX_1_SOURCE */ + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +/* #undef _POSIX_SOURCE */ + +/* Define to `__inline__' or `__inline' if that's what the C compiler + calls it, or to nothing if 'inline' is not supported under any name. */ +#ifndef __cplusplus +/* #undef inline */ +#endif diff --git a/contrib/toolchain/binutils/gas/config/atof-ieee.c b/contrib/toolchain/binutils/gas/config/atof-ieee.c new file mode 100644 index 0000000000..32e57076a4 --- /dev/null +++ b/contrib/toolchain/binutils/gas/config/atof-ieee.c @@ -0,0 +1,813 @@ +/* atof_ieee.c - turn a Flonum into an IEEE floating point number + Copyright 1987, 1992, 1994, 1996, 1997, 1998, 1999, 2000, 2001, 2005, + 2007, 2009 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" + +/* Flonums returned here. */ +extern FLONUM_TYPE generic_floating_point_number; + +extern const char EXP_CHARS[]; +/* Precision in LittleNums. */ +/* Don't count the gap in the m68k extended precision format. */ +#define MAX_PRECISION 5 +#define F_PRECISION 2 +#define D_PRECISION 4 +#define X_PRECISION 5 +#define P_PRECISION 5 + +/* Length in LittleNums of guard bits. */ +#define GUARD 2 + +#ifndef TC_LARGEST_EXPONENT_IS_NORMAL +#define TC_LARGEST_EXPONENT_IS_NORMAL(PRECISION) 0 +#endif + +static const unsigned long mask[] = +{ + 0x00000000, + 0x00000001, + 0x00000003, + 0x00000007, + 0x0000000f, + 0x0000001f, + 0x0000003f, + 0x0000007f, + 0x000000ff, + 0x000001ff, + 0x000003ff, + 0x000007ff, + 0x00000fff, + 0x00001fff, + 0x00003fff, + 0x00007fff, + 0x0000ffff, + 0x0001ffff, + 0x0003ffff, + 0x0007ffff, + 0x000fffff, + 0x001fffff, + 0x003fffff, + 0x007fffff, + 0x00ffffff, + 0x01ffffff, + 0x03ffffff, + 0x07ffffff, + 0x0fffffff, + 0x1fffffff, + 0x3fffffff, + 0x7fffffff, + 0xffffffff, +}; + +static int bits_left_in_littlenum; +static int littlenums_left; +static LITTLENUM_TYPE *littlenum_pointer; + +static int +next_bits (int number_of_bits) +{ + int return_value; + + if (!littlenums_left) + return 0; + + if (number_of_bits >= bits_left_in_littlenum) + { + return_value = mask[bits_left_in_littlenum] & *littlenum_pointer; + number_of_bits -= bits_left_in_littlenum; + return_value <<= number_of_bits; + + if (--littlenums_left) + { + bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS - number_of_bits; + --littlenum_pointer; + return_value |= + (*littlenum_pointer >> bits_left_in_littlenum) + & mask[number_of_bits]; + } + } + else + { + bits_left_in_littlenum -= number_of_bits; + return_value = + mask[number_of_bits] & (*littlenum_pointer >> bits_left_in_littlenum); + } + return return_value; +} + +/* Num had better be less than LITTLENUM_NUMBER_OF_BITS. */ + +static void +unget_bits (int num) +{ + if (!littlenums_left) + { + ++littlenum_pointer; + ++littlenums_left; + bits_left_in_littlenum = num; + } + else if (bits_left_in_littlenum + num > LITTLENUM_NUMBER_OF_BITS) + { + bits_left_in_littlenum = + num - (LITTLENUM_NUMBER_OF_BITS - bits_left_in_littlenum); + ++littlenum_pointer; + ++littlenums_left; + } + else + bits_left_in_littlenum += num; +} + +static void +make_invalid_floating_point_number (LITTLENUM_TYPE *words) +{ + as_bad (_("cannot create floating-point number")); + /* Zero the leftmost bit. */ + words[0] = (LITTLENUM_TYPE) ((unsigned) -1) >> 1; + words[1] = (LITTLENUM_TYPE) -1; + words[2] = (LITTLENUM_TYPE) -1; + words[3] = (LITTLENUM_TYPE) -1; + words[4] = (LITTLENUM_TYPE) -1; + words[5] = (LITTLENUM_TYPE) -1; +} + +/* Warning: This returns 16-bit LITTLENUMs. It is up to the caller to + figure out any alignment problems and to conspire for the + bytes/word to be emitted in the right order. Bigendians beware! */ + +/* Note that atof-ieee always has X and P precisions enabled. it is up + to md_atof to filter them out if the target machine does not support + them. */ + +/* Returns pointer past text consumed. */ + +char * +atof_ieee (char *str, /* Text to convert to binary. */ + int what_kind, /* 'd', 'f', 'x', 'p'. */ + LITTLENUM_TYPE *words) /* Build the binary here. */ +{ + /* Extra bits for zeroed low-order bits. + The 1st MAX_PRECISION are zeroed, the last contain flonum bits. */ + static LITTLENUM_TYPE bits[MAX_PRECISION + MAX_PRECISION + GUARD]; + char *return_value; + /* Number of 16-bit words in the format. */ + int precision; + long exponent_bits; + FLONUM_TYPE save_gen_flonum; + + /* We have to save the generic_floating_point_number because it + contains storage allocation about the array of LITTLENUMs where + the value is actually stored. We will allocate our own array of + littlenums below, but have to restore the global one on exit. */ + save_gen_flonum = generic_floating_point_number; + + return_value = str; + generic_floating_point_number.low = bits + MAX_PRECISION; + generic_floating_point_number.high = NULL; + generic_floating_point_number.leader = NULL; + generic_floating_point_number.exponent = 0; + generic_floating_point_number.sign = '\0'; + + /* Use more LittleNums than seems necessary: the highest flonum may + have 15 leading 0 bits, so could be useless. */ + + memset (bits, '\0', sizeof (LITTLENUM_TYPE) * MAX_PRECISION); + + switch (what_kind) + { + case 'f': + case 'F': + case 's': + case 'S': + precision = F_PRECISION; + exponent_bits = 8; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + precision = D_PRECISION; + exponent_bits = 11; + break; + + case 'x': + case 'X': + case 'e': + case 'E': + precision = X_PRECISION; + exponent_bits = 15; + break; + + case 'p': + case 'P': + precision = P_PRECISION; + exponent_bits = -1; + break; + + default: + make_invalid_floating_point_number (words); + return (NULL); + } + + generic_floating_point_number.high + = generic_floating_point_number.low + precision - 1 + GUARD; + + if (atof_generic (&return_value, ".", EXP_CHARS, + &generic_floating_point_number)) + { + make_invalid_floating_point_number (words); + return NULL; + } + gen_to_words (words, precision, exponent_bits); + + /* Restore the generic_floating_point_number's storage alloc (and + everything else). */ + generic_floating_point_number = save_gen_flonum; + + return return_value; +} + +/* Turn generic_floating_point_number into a real float/double/extended. */ + +int +gen_to_words (LITTLENUM_TYPE *words, int precision, long exponent_bits) +{ + int return_value = 0; + + long exponent_1; + long exponent_2; + long exponent_3; + long exponent_4; + int exponent_skippage; + LITTLENUM_TYPE word1; + LITTLENUM_TYPE *lp; + LITTLENUM_TYPE *words_end; + + words_end = words + precision; +#ifdef TC_M68K + if (precision == X_PRECISION) + /* On the m68k the extended precision format has a gap of 16 bits + between the exponent and the mantissa. */ + words_end++; +#endif + + if (generic_floating_point_number.low > generic_floating_point_number.leader) + { + /* 0.0e0 seen. */ + if (generic_floating_point_number.sign == '+') + words[0] = 0x0000; + else + words[0] = 0x8000; + memset (&words[1], '\0', + (words_end - words - 1) * sizeof (LITTLENUM_TYPE)); + return return_value; + } + + /* NaN: Do the right thing. */ + if (generic_floating_point_number.sign == 0) + { + if (TC_LARGEST_EXPONENT_IS_NORMAL (precision)) + as_warn (_("NaNs are not supported by this target\n")); + if (precision == F_PRECISION) + { + words[0] = 0x7fff; + words[1] = 0xffff; + } + else if (precision == X_PRECISION) + { +#ifdef TC_M68K + words[0] = 0x7fff; + words[1] = 0; + words[2] = 0xffff; + words[3] = 0xffff; + words[4] = 0xffff; + words[5] = 0xffff; +#else /* ! TC_M68K */ +#ifdef TC_I386 + words[0] = 0xffff; + words[1] = 0xc000; + words[2] = 0; + words[3] = 0; + words[4] = 0; +#else /* ! TC_I386 */ + abort (); +#endif /* ! TC_I386 */ +#endif /* ! TC_M68K */ + } + else + { + words[0] = 0x7fff; + words[1] = 0xffff; + words[2] = 0xffff; + words[3] = 0xffff; + } + return return_value; + } + else if (generic_floating_point_number.sign == 'P') + { + if (TC_LARGEST_EXPONENT_IS_NORMAL (precision)) + as_warn (_("Infinities are not supported by this target\n")); + + /* +INF: Do the right thing. */ + if (precision == F_PRECISION) + { + words[0] = 0x7f80; + words[1] = 0; + } + else if (precision == X_PRECISION) + { +#ifdef TC_M68K + words[0] = 0x7fff; + words[1] = 0; + words[2] = 0; + words[3] = 0; + words[4] = 0; + words[5] = 0; +#else /* ! TC_M68K */ +#ifdef TC_I386 + words[0] = 0x7fff; + words[1] = 0x8000; + words[2] = 0; + words[3] = 0; + words[4] = 0; +#else /* ! TC_I386 */ + abort (); +#endif /* ! TC_I386 */ +#endif /* ! TC_M68K */ + } + else + { + words[0] = 0x7ff0; + words[1] = 0; + words[2] = 0; + words[3] = 0; + } + return return_value; + } + else if (generic_floating_point_number.sign == 'N') + { + if (TC_LARGEST_EXPONENT_IS_NORMAL (precision)) + as_warn (_("Infinities are not supported by this target\n")); + + /* Negative INF. */ + if (precision == F_PRECISION) + { + words[0] = 0xff80; + words[1] = 0x0; + } + else if (precision == X_PRECISION) + { +#ifdef TC_M68K + words[0] = 0xffff; + words[1] = 0; + words[2] = 0; + words[3] = 0; + words[4] = 0; + words[5] = 0; +#else /* ! TC_M68K */ +#ifdef TC_I386 + words[0] = 0xffff; + words[1] = 0x8000; + words[2] = 0; + words[3] = 0; + words[4] = 0; +#else /* ! TC_I386 */ + abort (); +#endif /* ! TC_I386 */ +#endif /* ! TC_M68K */ + } + else + { + words[0] = 0xfff0; + words[1] = 0x0; + words[2] = 0x0; + words[3] = 0x0; + } + return return_value; + } + + /* The floating point formats we support have: + Bit 15 is sign bit. + Bits 14:n are excess-whatever exponent. + Bits n-1:0 (if any) are most significant bits of fraction. + Bits 15:0 of the next word(s) are the next most significant bits. + + So we need: number of bits of exponent, number of bits of + mantissa. */ + bits_left_in_littlenum = LITTLENUM_NUMBER_OF_BITS; + littlenum_pointer = generic_floating_point_number.leader; + littlenums_left = (1 + + generic_floating_point_number.leader + - generic_floating_point_number.low); + + /* Seek (and forget) 1st significant bit. */ + for (exponent_skippage = 0; !next_bits (1); ++exponent_skippage); + exponent_1 = (generic_floating_point_number.exponent + + generic_floating_point_number.leader + + 1 + - generic_floating_point_number.low); + + /* Radix LITTLENUM_RADIX, point just higher than + generic_floating_point_number.leader. */ + exponent_2 = exponent_1 * LITTLENUM_NUMBER_OF_BITS; + + /* Radix 2. */ + exponent_3 = exponent_2 - exponent_skippage; + + /* Forget leading zeros, forget 1st bit. */ + exponent_4 = exponent_3 + ((1 << (exponent_bits - 1)) - 2); + + /* Offset exponent. */ + lp = words; + + /* Word 1. Sign, exponent and perhaps high bits. */ + word1 = ((generic_floating_point_number.sign == '+') + ? 0 + : (1 << (LITTLENUM_NUMBER_OF_BITS - 1))); + + /* Assume 2's complement integers. */ + if (exponent_4 <= 0) + { + int prec_bits; + int num_bits; + + unget_bits (1); + num_bits = -exponent_4; + prec_bits = + LITTLENUM_NUMBER_OF_BITS * precision - (exponent_bits + 1 + num_bits); +#ifdef TC_I386 + if (precision == X_PRECISION && exponent_bits == 15) + { + /* On the i386 a denormalized extended precision float is + shifted down by one, effectively decreasing the exponent + bias by one. */ + prec_bits -= 1; + num_bits += 1; + } +#endif + + if (num_bits >= LITTLENUM_NUMBER_OF_BITS - exponent_bits) + { + /* Bigger than one littlenum. */ + num_bits -= (LITTLENUM_NUMBER_OF_BITS - 1) - exponent_bits; + *lp++ = word1; + if (num_bits + exponent_bits + 1 + > precision * LITTLENUM_NUMBER_OF_BITS) + { + /* Exponent overflow. */ + make_invalid_floating_point_number (words); + return return_value; + } +#ifdef TC_M68K + if (precision == X_PRECISION && exponent_bits == 15) + *lp++ = 0; +#endif + while (num_bits >= LITTLENUM_NUMBER_OF_BITS) + { + num_bits -= LITTLENUM_NUMBER_OF_BITS; + *lp++ = 0; + } + if (num_bits) + *lp++ = next_bits (LITTLENUM_NUMBER_OF_BITS - (num_bits)); + } + else + { + if (precision == X_PRECISION && exponent_bits == 15) + { + *lp++ = word1; +#ifdef TC_M68K + *lp++ = 0; +#endif + *lp++ = next_bits (LITTLENUM_NUMBER_OF_BITS - num_bits); + } + else + { + word1 |= next_bits ((LITTLENUM_NUMBER_OF_BITS - 1) + - (exponent_bits + num_bits)); + *lp++ = word1; + } + } + while (lp < words_end) + *lp++ = next_bits (LITTLENUM_NUMBER_OF_BITS); + + /* Round the mantissa up, but don't change the number. */ + if (next_bits (1)) + { + --lp; + if (prec_bits >= LITTLENUM_NUMBER_OF_BITS) + { + int n = 0; + int tmp_bits; + + n = 0; + tmp_bits = prec_bits; + while (tmp_bits > LITTLENUM_NUMBER_OF_BITS) + { + if (lp[n] != (LITTLENUM_TYPE) - 1) + break; + --n; + tmp_bits -= LITTLENUM_NUMBER_OF_BITS; + } + if (tmp_bits > LITTLENUM_NUMBER_OF_BITS + || (lp[n] & mask[tmp_bits]) != mask[tmp_bits] + || (prec_bits != (precision * LITTLENUM_NUMBER_OF_BITS + - exponent_bits - 1) +#ifdef TC_I386 + /* An extended precision float with only the integer + bit set would be invalid. That must be converted + to the smallest normalized number. */ + && !(precision == X_PRECISION + && prec_bits == (precision * LITTLENUM_NUMBER_OF_BITS + - exponent_bits - 2)) +#endif + )) + { + unsigned long carry; + + for (carry = 1; carry && (lp >= words); lp--) + { + carry = *lp + carry; + *lp = carry; + carry >>= LITTLENUM_NUMBER_OF_BITS; + } + } + else + { + /* This is an overflow of the denormal numbers. We + need to forget what we have produced, and instead + generate the smallest normalized number. */ + lp = words; + word1 = ((generic_floating_point_number.sign == '+') + ? 0 + : (1 << (LITTLENUM_NUMBER_OF_BITS - 1))); + word1 |= (1 + << ((LITTLENUM_NUMBER_OF_BITS - 1) + - exponent_bits)); + *lp++ = word1; +#ifdef TC_I386 + /* Set the integer bit in the extended precision format. + This cannot happen on the m68k where the mantissa + just overflows into the integer bit above. */ + if (precision == X_PRECISION) + *lp++ = 1 << (LITTLENUM_NUMBER_OF_BITS - 1); +#endif + while (lp < words_end) + *lp++ = 0; + } + } + else + *lp += 1; + } + + return return_value; + } + else if ((unsigned long) exponent_4 > mask[exponent_bits] + || (! TC_LARGEST_EXPONENT_IS_NORMAL (precision) + && (unsigned long) exponent_4 == mask[exponent_bits])) + { + /* Exponent overflow. Lose immediately. */ + + /* We leave return_value alone: admit we read the + number, but return a floating exception + because we can't encode the number. */ + make_invalid_floating_point_number (words); + return return_value; + } + else + { + word1 |= (exponent_4 << ((LITTLENUM_NUMBER_OF_BITS - 1) - exponent_bits)) + | next_bits ((LITTLENUM_NUMBER_OF_BITS - 1) - exponent_bits); + } + + *lp++ = word1; + + /* X_PRECISION is special: on the 68k, it has 16 bits of zero in the + middle. Either way, it is then followed by a 1 bit. */ + if (exponent_bits == 15 && precision == X_PRECISION) + { +#ifdef TC_M68K + *lp++ = 0; +#endif + *lp++ = (1 << (LITTLENUM_NUMBER_OF_BITS - 1) + | next_bits (LITTLENUM_NUMBER_OF_BITS - 1)); + } + + /* The rest of the words are just mantissa bits. */ + while (lp < words_end) + *lp++ = next_bits (LITTLENUM_NUMBER_OF_BITS); + + if (next_bits (1)) + { + unsigned long carry; + /* Since the NEXT bit is a 1, round UP the mantissa. + The cunning design of these hidden-1 floats permits + us to let the mantissa overflow into the exponent, and + it 'does the right thing'. However, we lose if the + highest-order bit of the lowest-order word flips. + Is that clear? */ + + /* #if (sizeof(carry)) < ((sizeof(bits[0]) * BITS_PER_CHAR) + 2) + Please allow at least 1 more bit in carry than is in a LITTLENUM. + We need that extra bit to hold a carry during a LITTLENUM carry + propagation. Another extra bit (kept 0) will assure us that we + don't get a sticky sign bit after shifting right, and that + permits us to propagate the carry without any masking of bits. + #endif */ + for (carry = 1, lp--; carry; lp--) + { + carry = *lp + carry; + *lp = carry; + carry >>= LITTLENUM_NUMBER_OF_BITS; + if (lp == words) + break; + } + if (precision == X_PRECISION && exponent_bits == 15) + { + /* Extended precision numbers have an explicit integer bit + that we may have to restore. */ + if (lp == words) + { +#ifdef TC_M68K + /* On the m68k there is a gap of 16 bits. We must + explicitly propagate the carry into the exponent. */ + words[0] += words[1]; + words[1] = 0; + lp++; +#endif + /* Put back the integer bit. */ + lp[1] |= 1 << (LITTLENUM_NUMBER_OF_BITS - 1); + } + } + if ((word1 ^ *words) & (1 << (LITTLENUM_NUMBER_OF_BITS - 1))) + { + /* We leave return_value alone: admit we read the number, + but return a floating exception because we can't encode + the number. */ + *words &= ~(1 << (LITTLENUM_NUMBER_OF_BITS - 1)); + } + } + return return_value; +} + +#ifdef TEST +char * +print_gen (gen) + FLONUM_TYPE *gen; +{ + FLONUM_TYPE f; + LITTLENUM_TYPE arr[10]; + double dv; + float fv; + static char sbuf[40]; + + if (gen) + { + f = generic_floating_point_number; + generic_floating_point_number = *gen; + } + gen_to_words (&arr[0], 4, 11); + memcpy (&dv, &arr[0], sizeof (double)); + sprintf (sbuf, "%x %x %x %x %.14G ", arr[0], arr[1], arr[2], arr[3], dv); + gen_to_words (&arr[0], 2, 8); + memcpy (&fv, &arr[0], sizeof (float)); + sprintf (sbuf + strlen (sbuf), "%x %x %.12g\n", arr[0], arr[1], fv); + + if (gen) + generic_floating_point_number = f; + + return (sbuf); +} +#endif + +extern const char FLT_CHARS[]; +#define MAX_LITTLENUMS 6 + +/* This is a utility function called from various tc-*.c files. It + is here in order to reduce code duplication. + + Turn a string at input_line_pointer into a floating point constant + of type TYPE (a character found in the FLT_CHARS macro), and store + it as LITTLENUMS in the bytes buffer LITP. The number of chars + emitted is stored in *SIZEP. BIG_WORDIAN is TRUE if the littlenums + should be emitted most significant littlenum first. + + An error message is returned, or a NULL pointer if everything went OK. */ + +char * +ieee_md_atof (int type, + char *litP, + int *sizeP, + bfd_boolean big_wordian) +{ + LITTLENUM_TYPE words[MAX_LITTLENUMS]; + LITTLENUM_TYPE *wordP; + char *t; + int prec = 0; + + if (strchr (FLT_CHARS, type) != NULL) + { + switch (type) + { + case 'f': + case 'F': + case 's': + case 'S': + prec = F_PRECISION; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + prec = D_PRECISION; + break; + + case 't': + case 'T': + prec = X_PRECISION; + type = 'x'; /* This is what atof_ieee() understands. */ + break; + + case 'x': + case 'X': + case 'p': + case 'P': +#ifdef TC_M68K + /* Note: on the m68k there is a gap of 16 bits (one littlenum) + between the exponent and mantissa. Hence the precision is + 6 and not 5. */ + prec = P_PRECISION + 1; +#else + prec = P_PRECISION; +#endif + break; + + default: + break; + } + } + /* The 'f' and 'd' types are always recognised, even if the target has + not put them into the FLT_CHARS macro. This is because the 'f' type + can come from the .dc.s, .dcb.s, .float or .single pseudo-ops and the + 'd' type from the .dc.d, .dbc.d or .double pseudo-ops. + + The 'x' type is not implicitly recongised however, even though it can + be generated by the .dc.x and .dbc.x pseudo-ops because not all targets + can support floating point values that big. ie the target has to + explicitly allow them by putting them into FLT_CHARS. */ + else if (type == 'f') + prec = F_PRECISION; + else if (type == 'd') + prec = D_PRECISION; + + if (prec == 0) + { + *sizeP = 0; + return _("Unrecognized or unsupported floating point constant"); + } + + gas_assert (prec <= MAX_LITTLENUMS); + + t = atof_ieee (input_line_pointer, type, words); + if (t) + input_line_pointer = t; + + *sizeP = prec * sizeof (LITTLENUM_TYPE); + + if (big_wordian) + { + for (wordP = words; prec --;) + { + md_number_to_chars (litP, (valueT) (* wordP ++), sizeof (LITTLENUM_TYPE)); + litP += sizeof (LITTLENUM_TYPE); + } + } + else + { + for (wordP = words + prec; prec --;) + { + md_number_to_chars (litP, (valueT) (* -- wordP), sizeof (LITTLENUM_TYPE)); + litP += sizeof (LITTLENUM_TYPE); + } + } + + return NULL; +} diff --git a/contrib/toolchain/binutils/gas/config/obj-coff-seh.c b/contrib/toolchain/binutils/gas/config/obj-coff-seh.c new file mode 100644 index 0000000000..83e8cb669c --- /dev/null +++ b/contrib/toolchain/binutils/gas/config/obj-coff-seh.c @@ -0,0 +1,1018 @@ +/* seh pdata/xdata coff object file format + Copyright 2009, 2010 + Free Software Foundation, Inc. + + This file is part of GAS. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "obj-coff-seh.h" + + +/* Private segment collection list. */ +struct seh_seg_list { + segT seg; + int subseg; + char *seg_name; +}; + +/* Local data. */ +static seh_context *seh_ctx_cur = NULL; + +static struct hash_control *seh_hash; + +static struct seh_seg_list *x_segcur = NULL; +static struct seh_seg_list *p_segcur = NULL; + +static void write_function_xdata (seh_context *); +static void write_function_pdata (seh_context *); + + +/* Build based on segment the derived .pdata/.xdata + segment name containing origin segment's postfix name part. */ +static char * +get_pxdata_name (segT seg, const char *base_name) +{ + const char *name,*dollar, *dot; + char *sname; + + name = bfd_get_section_name (stdoutput, seg); + + dollar = strchr (name, '$'); + dot = strchr (name + 1, '.'); + + if (!dollar && !dot) + name = ""; + else if (!dollar) + name = dot; + else if (!dot) + name = dollar; + else if (dot < dollar) + name = dot; + else + name = dollar; + + sname = concat (base_name, name, NULL); + + return sname; +} + +/* Allocate a seh_seg_list structure. */ +static struct seh_seg_list * +alloc_pxdata_item (segT seg, int subseg, char *name) +{ + struct seh_seg_list *r; + + r = (struct seh_seg_list *) + xmalloc (sizeof (struct seh_seg_list) + strlen (name)); + r->seg = seg; + r->subseg = subseg; + r->seg_name = name; + return r; +} + +/* Generate pdata/xdata segment with same linkonce properties + of based segment. */ +static segT +make_pxdata_seg (segT cseg, char *name) +{ + segT save_seg = now_seg; + int save_subseg = now_subseg; + segT r; + flagword flags; + + r = subseg_new (name, 0); + /* Check if code segment is marked as linked once. */ + flags = bfd_get_section_flags (stdoutput, cseg) + & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD + | SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE + | SEC_LINK_DUPLICATES_SAME_CONTENTS); + + /* Add standard section flags. */ + flags |= SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA; + + /* Apply possibly linked once flags to new generated segment, too. */ + if (!bfd_set_section_flags (stdoutput, r, flags)) + as_bad (_("bfd_set_section_flags: %s"), + bfd_errmsg (bfd_get_error ())); + + /* Restore to previous segment. */ + subseg_set (save_seg, save_subseg); + return r; +} + +static void +seh_hash_insert (const char *name, struct seh_seg_list *item) +{ + const char *error_string; + + if ((error_string = hash_jam (seh_hash, name, (char *) item))) + as_fatal (_("Inserting \"%s\" into structure table failed: %s"), + name, error_string); +} + +static struct seh_seg_list * +seh_hash_find (char *name) +{ + return (struct seh_seg_list *) hash_find (seh_hash, name); +} + +static struct seh_seg_list * +seh_hash_find_or_make (segT cseg, const char *base_name) +{ + struct seh_seg_list *item; + char *name; + + /* Initialize seh_hash once. */ + if (!seh_hash) + seh_hash = hash_new (); + + name = get_pxdata_name (cseg, base_name); + + item = seh_hash_find (name); + if (!item) + { + item = alloc_pxdata_item (make_pxdata_seg (cseg, name), 0, name); + + seh_hash_insert (item->seg_name, item); + } + else + free (name); + + return item; +} + +/* Check if current segment has same name. */ +static int +seh_validate_seg (const char *directive) +{ + const char *cseg_name, *nseg_name; + if (seh_ctx_cur->code_seg == now_seg) + return 1; + cseg_name = bfd_get_section_name (stdoutput, seh_ctx_cur->code_seg); + nseg_name = bfd_get_section_name (stdoutput, now_seg); + as_bad (_("%s used in segment '%s' instead of expected '%s'"), + directive, nseg_name, cseg_name); + ignore_rest_of_line (); + return 0; +} + +static void +switch_xdata (int subseg, segT code_seg) +{ + x_segcur = seh_hash_find_or_make (code_seg, ".xdata"); + + subseg_set (x_segcur->seg, subseg); +} + +static void +switch_pdata (segT code_seg) +{ + p_segcur = seh_hash_find_or_make (code_seg, ".pdata"); + + subseg_set (p_segcur->seg, p_segcur->subseg); +} + +/* Parsing routines. */ + +/* Return the style of SEH unwind info to generate. */ + +static seh_kind +seh_get_target_kind (void) +{ + if (!stdoutput) + return seh_kind_unknown; + switch (bfd_get_arch (stdoutput)) + { + case bfd_arch_arm: + case bfd_arch_powerpc: + case bfd_arch_sh: + return seh_kind_arm; + case bfd_arch_i386: + switch (bfd_get_mach (stdoutput)) + { + case bfd_mach_x86_64: + case bfd_mach_x86_64_intel_syntax: + return seh_kind_x64; + default: + break; + } + /* FALL THROUGH. */ + case bfd_arch_mips: + return seh_kind_mips; + case bfd_arch_ia64: + /* Should return seh_kind_x64. But not implemented yet. */ + return seh_kind_unknown; + default: + break; + } + return seh_kind_unknown; +} + +/* Verify that we're in the context of a seh_proc. */ + +static int +verify_context (const char *directive) +{ + if (seh_ctx_cur == NULL) + { + as_bad (_("%s used outside of .seh_proc block"), directive); + ignore_rest_of_line (); + return 0; + } + return 1; +} + +/* Similar, except we also verify the appropriate target. */ + +static int +verify_context_and_target (const char *directive, seh_kind target) +{ + if (seh_get_target_kind () != target) + { + as_warn (_("%s ignored for this target"), directive); + ignore_rest_of_line (); + return 0; + } + return verify_context (directive); +} + +/* Skip whitespace and a comma. Error if the comma is not seen. */ + +static int +skip_whitespace_and_comma (int required) +{ + SKIP_WHITESPACE (); + if (*input_line_pointer == ',') + { + input_line_pointer++; + SKIP_WHITESPACE (); + return 1; + } + else if (required) + { + as_bad (_("missing separator")); + ignore_rest_of_line (); + } + else + demand_empty_rest_of_line (); + return 0; +} + +/* Mark current context to use 32-bit instruction (arm). */ + +static void +obj_coff_seh_32 (int what) +{ + if (!verify_context_and_target ((what ? ".seh_32" : ".seh_no32"), + seh_kind_arm)) + return; + + seh_ctx_cur->use_instruction_32 = (what ? 1 : 0); + demand_empty_rest_of_line (); +} + +/* Set for current context the handler and optional data (arm). */ + +static void +obj_coff_seh_eh (int what ATTRIBUTE_UNUSED) +{ + if (!verify_context_and_target (".seh_eh", seh_kind_arm)) + return; + + /* Write block to .text if exception handler is set. */ + seh_ctx_cur->handler_written = 1; + emit_expr (&seh_ctx_cur->handler, 4); + emit_expr (&seh_ctx_cur->handler_data, 4); + + demand_empty_rest_of_line (); +} + +/* Set for current context the default handler (x64). */ + +static void +obj_coff_seh_handler (int what ATTRIBUTE_UNUSED) +{ + char *symbol_name; + char name_end; + + if (!verify_context (".seh_handler")) + return; + + if (*input_line_pointer == 0 || *input_line_pointer == '\n') + { + as_bad (_(".seh_handler requires a handler")); + demand_empty_rest_of_line (); + return; + } + + SKIP_WHITESPACE (); + + if (*input_line_pointer == '@') + { + symbol_name = input_line_pointer; + name_end = get_symbol_end (); + + seh_ctx_cur->handler.X_op = O_constant; + seh_ctx_cur->handler.X_add_number = 0; + + if (strcasecmp (symbol_name, "@0") == 0 + || strcasecmp (symbol_name, "@null") == 0) + ; + else if (strcasecmp (symbol_name, "@1") == 0) + seh_ctx_cur->handler.X_add_number = 1; + else + as_bad (_("unknown constant value '%s' for handler"), symbol_name); + + *input_line_pointer = name_end; + } + else + expression (&seh_ctx_cur->handler); + + seh_ctx_cur->handler_data.X_op = O_constant; + seh_ctx_cur->handler_data.X_add_number = 0; + seh_ctx_cur->handler_flags = 0; + + if (!skip_whitespace_and_comma (0)) + return; + + if (seh_get_target_kind () == seh_kind_x64) + { + do + { + symbol_name = input_line_pointer; + name_end = get_symbol_end (); + + if (strcasecmp (symbol_name, "@unwind") == 0) + seh_ctx_cur->handler_flags |= UNW_FLAG_UHANDLER; + else if (strcasecmp (symbol_name, "@except") == 0) + seh_ctx_cur->handler_flags |= UNW_FLAG_EHANDLER; + else + as_bad (_(".seh_handler constant '%s' unknown"), symbol_name); + + *input_line_pointer = name_end; + } + while (skip_whitespace_and_comma (0)); + } + else + { + expression (&seh_ctx_cur->handler_data); + demand_empty_rest_of_line (); + + if (seh_ctx_cur->handler_written) + as_warn (_(".seh_handler after .seh_eh is ignored")); + } +} + +/* Switch to subsection for handler data for exception region (x64). */ + +static void +obj_coff_seh_handlerdata (int what ATTRIBUTE_UNUSED) +{ + if (!verify_context_and_target (".seh_handlerdata", seh_kind_x64)) + return; + demand_empty_rest_of_line (); + + switch_xdata (seh_ctx_cur->subsection + 1, seh_ctx_cur->code_seg); +} + +/* Mark end of current context. */ + +static void +do_seh_endproc (void) +{ + seh_ctx_cur->end_addr = symbol_temp_new_now (); + + write_function_xdata (seh_ctx_cur); + write_function_pdata (seh_ctx_cur); + seh_ctx_cur = NULL; +} + +static void +obj_coff_seh_endproc (int what ATTRIBUTE_UNUSED) +{ + demand_empty_rest_of_line (); + if (seh_ctx_cur == NULL) + { + as_bad (_(".seh_endproc used without .seh_proc")); + return; + } + seh_validate_seg (".seh_endproc"); + do_seh_endproc (); +} + +/* Mark begin of new context. */ + +static void +obj_coff_seh_proc (int what ATTRIBUTE_UNUSED) +{ + char *symbol_name; + char name_end; + + if (seh_ctx_cur != NULL) + { + as_bad (_("previous SEH entry not closed (missing .seh_endproc)")); + do_seh_endproc (); + } + + if (*input_line_pointer == 0 || *input_line_pointer == '\n') + { + as_bad (_(".seh_proc requires function label name")); + demand_empty_rest_of_line (); + return; + } + + seh_ctx_cur = XCNEW (seh_context); + + seh_ctx_cur->code_seg = now_seg; + + if (seh_get_target_kind () == seh_kind_x64) + { + x_segcur = seh_hash_find_or_make (seh_ctx_cur->code_seg, ".xdata"); + seh_ctx_cur->subsection = x_segcur->subseg; + x_segcur->subseg += 2; + } + + SKIP_WHITESPACE (); + + symbol_name = input_line_pointer; + name_end = get_symbol_end (); + seh_ctx_cur->func_name = xstrdup (symbol_name); + *input_line_pointer = name_end; + + demand_empty_rest_of_line (); + + seh_ctx_cur->start_addr = symbol_temp_new_now (); +} + +/* Mark end of prologue for current context. */ + +static void +obj_coff_seh_endprologue (int what ATTRIBUTE_UNUSED) +{ + if (!verify_context (".seh_endprologue") + || !seh_validate_seg (".seh_endprologue")) + return; + demand_empty_rest_of_line (); + + if (seh_ctx_cur->endprologue_addr != NULL) + as_warn (_("duplicate .seh_endprologue in .seh_proc block")); + else + seh_ctx_cur->endprologue_addr = symbol_temp_new_now (); +} + +/* End-of-file hook. */ + +void +obj_coff_seh_do_final (void) +{ + if (seh_ctx_cur != NULL) + { + as_bad (_("open SEH entry at end of file (missing .cfi_endproc)")); + do_seh_endproc (); + } +} + +/* Enter a prologue element into current context (x64). */ + +static void +seh_x64_make_prologue_element (int code, int info, offsetT off) +{ + seh_prologue_element *n; + + if (seh_ctx_cur == NULL) + return; + if (seh_ctx_cur->elems_count == seh_ctx_cur->elems_max) + { + seh_ctx_cur->elems_max += 8; + seh_ctx_cur->elems = XRESIZEVEC (seh_prologue_element, + seh_ctx_cur->elems, + seh_ctx_cur->elems_max); + } + + n = &seh_ctx_cur->elems[seh_ctx_cur->elems_count++]; + n->code = code; + n->info = info; + n->off = off; + n->pc_addr = symbol_temp_new_now (); +} + +/* Helper to read a register name from input stream (x64). */ + +static int +seh_x64_read_reg (const char *directive, int kind) +{ + static const char * const int_regs[16] = + { "rax", "rcx", "rdx", "rbx", "rsp", "rbp","rsi","rdi", + "r8","r9","r10","r11","r12","r13","r14","r15" }; + static const char * const xmm_regs[16] = + { "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", + "xmm8", "xmm9", "xmm10","xmm11","xmm12","xmm13","xmm14","xmm15" }; + + const char * const *regs = NULL; + char name_end; + char *symbol_name = NULL; + int i; + + switch (kind) + { + case 0: + case 1: + regs = int_regs; + break; + case 2: + regs = xmm_regs; + break; + default: + abort (); + } + + SKIP_WHITESPACE (); + if (*input_line_pointer == '%') + ++input_line_pointer; + symbol_name = input_line_pointer; + name_end = get_symbol_end (); + + for (i = 0; i < 16; i++) + if (! strcasecmp (regs[i], symbol_name)) + break; + + *input_line_pointer = name_end; + + /* Error if register not found, or EAX used as a frame pointer. */ + if (i == 16 || (kind == 0 && i == 0)) + { + as_bad (_("invalid register for %s"), directive); + return -1; + } + + return i; +} + +/* Add a register push-unwind token to the current context. */ + +static void +obj_coff_seh_pushreg (int what ATTRIBUTE_UNUSED) +{ + int reg; + + if (!verify_context_and_target (".seh_pushreg", seh_kind_x64) + || !seh_validate_seg (".seh_pushreg")) + return; + + reg = seh_x64_read_reg (".seh_pushreg", 1); + demand_empty_rest_of_line (); + + if (reg < 0) + return; + + seh_x64_make_prologue_element (UWOP_PUSH_NONVOL, reg, 0); +} + +/* Add a register frame-unwind token to the current context. */ + +static void +obj_coff_seh_pushframe (int what ATTRIBUTE_UNUSED) +{ + if (!verify_context_and_target (".seh_pushframe", seh_kind_x64) + || !seh_validate_seg (".seh_pushframe")) + return; + demand_empty_rest_of_line (); + + seh_x64_make_prologue_element (UWOP_PUSH_MACHFRAME, 0, 0); +} + +/* Add a register save-unwind token to current context. */ + +static void +obj_coff_seh_save (int what) +{ + const char *directive = (what == 1 ? ".seh_savereg" : ".seh_savexmm"); + int code, reg, scale; + offsetT off; + + if (!verify_context_and_target (directive, seh_kind_x64) + || !seh_validate_seg (directive)) + return; + + reg = seh_x64_read_reg (directive, what); + + if (!skip_whitespace_and_comma (1)) + return; + + off = get_absolute_expression (); + demand_empty_rest_of_line (); + + if (reg < 0) + return; + if (off < 0) + { + as_bad (_("%s offset is negative"), directive); + return; + } + + scale = (what == 1 ? 8 : 16); + + if ((off & (scale - 1)) == 0 && off <= (offsetT) (0xffff * scale)) + { + code = (what == 1 ? UWOP_SAVE_NONVOL : UWOP_SAVE_XMM128); + off /= scale; + } + else if (off < (offsetT) 0xffffffff) + code = (what == 1 ? UWOP_SAVE_NONVOL_FAR : UWOP_SAVE_XMM128_FAR); + else + { + as_bad (_("%s offset out of range"), directive); + return; + } + + seh_x64_make_prologue_element (code, reg, off); +} + +/* Add a stack-allocation token to current context. */ + +static void +obj_coff_seh_stackalloc (int what ATTRIBUTE_UNUSED) +{ + offsetT off; + int code, info; + + if (!verify_context_and_target (".seh_stackalloc", seh_kind_x64) + || !seh_validate_seg (".seh_stackalloc")) + return; + + off = get_absolute_expression (); + demand_empty_rest_of_line (); + + if (off == 0) + return; + if (off < 0) + { + as_bad (_(".seh_stackalloc offset is negative")); + return; + } + + if ((off & 7) == 0 && off <= 128) + code = UWOP_ALLOC_SMALL, info = (off - 8) >> 3, off = 0; + else if ((off & 7) == 0 && off <= (offsetT) (0xffff * 8)) + code = UWOP_ALLOC_LARGE, info = 0, off >>= 3; + else if (off <= (offsetT) 0xffffffff) + code = UWOP_ALLOC_LARGE, info = 1; + else + { + as_bad (_(".seh_stackalloc offset out of range")); + return; + } + + seh_x64_make_prologue_element (code, info, off); +} + +/* Add a frame-pointer token to current context. */ + +static void +obj_coff_seh_setframe (int what ATTRIBUTE_UNUSED) +{ + offsetT off; + int reg; + + if (!verify_context_and_target (".seh_setframe", seh_kind_x64) + || !seh_validate_seg (".seh_setframe")) + return; + + reg = seh_x64_read_reg (".seh_setframe", 0); + + if (!skip_whitespace_and_comma (1)) + return; + + off = get_absolute_expression (); + demand_empty_rest_of_line (); + + if (reg < 0) + return; + if (off < 0) + as_bad (_(".seh_setframe offset is negative")); + else if (off > 240) + as_bad (_(".seh_setframe offset out of range")); + else if (off & 15) + as_bad (_(".seh_setframe offset not a multiple of 16")); + else if (seh_ctx_cur->framereg != 0) + as_bad (_("duplicate .seh_setframe in current .seh_proc")); + else + { + seh_ctx_cur->framereg = reg; + seh_ctx_cur->frameoff = off; + seh_x64_make_prologue_element (UWOP_SET_FPREG, 0, 0); + } +} + +/* Data writing routines. */ + +/* Output raw integers in 1, 2, or 4 bytes. */ + +static inline void +out_one (int byte) +{ + FRAG_APPEND_1_CHAR (byte); +} + +static inline void +out_two (int data) +{ + md_number_to_chars (frag_more (2), data, 2); +} + +static inline void +out_four (int data) +{ + md_number_to_chars (frag_more (4), data, 4); +} + +/* Write out prologue data for x64. */ + +static void +seh_x64_write_prologue_data (const seh_context *c) +{ + int i; + + /* We have to store in reverse order. */ + for (i = c->elems_count - 1; i >= 0; --i) + { + const seh_prologue_element *e = c->elems + i; + expressionS exp; + + /* First comes byte offset in code. */ + exp.X_op = O_subtract; + exp.X_add_symbol = e->pc_addr; + exp.X_op_symbol = c->start_addr; + exp.X_add_number = 0; + emit_expr (&exp, 1); + + /* Second comes code+info packed into a byte. */ + out_one ((e->info << 4) | e->code); + + switch (e->code) + { + case UWOP_PUSH_NONVOL: + case UWOP_ALLOC_SMALL: + case UWOP_SET_FPREG: + case UWOP_PUSH_MACHFRAME: + /* These have no extra data. */ + break; + + case UWOP_ALLOC_LARGE: + if (e->info) + { + case UWOP_SAVE_NONVOL_FAR: + case UWOP_SAVE_XMM128_FAR: + /* An unscaled 4 byte offset. */ + out_four (e->off); + break; + } + /* FALLTHRU */ + + case UWOP_SAVE_NONVOL: + case UWOP_SAVE_XMM128: + /* A scaled 2 byte offset. */ + out_two (e->off); + break; + + default: + abort (); + } + } +} + +static int +seh_x64_size_prologue_data (const seh_context *c) +{ + int i, ret = 0; + + for (i = c->elems_count - 1; i >= 0; --i) + switch (c->elems[i].code) + { + case UWOP_PUSH_NONVOL: + case UWOP_ALLOC_SMALL: + case UWOP_SET_FPREG: + case UWOP_PUSH_MACHFRAME: + ret += 1; + break; + + case UWOP_SAVE_NONVOL: + case UWOP_SAVE_XMM128: + ret += 2; + break; + + case UWOP_SAVE_NONVOL_FAR: + case UWOP_SAVE_XMM128_FAR: + ret += 3; + break; + + case UWOP_ALLOC_LARGE: + ret += (c->elems[i].info ? 3 : 2); + break; + + default: + abort (); + } + + return ret; +} + +/* Write out the xdata information for one function (x64). */ + +static void +seh_x64_write_function_xdata (seh_context *c) +{ + int flags, count_unwind_codes; + expressionS exp; + + /* Set 4-byte alignment. */ + frag_align (2, 0, 0); + + c->xdata_addr = symbol_temp_new_now (); + flags = c->handler_flags; + count_unwind_codes = seh_x64_size_prologue_data (c); + + /* ubyte:3 version, ubyte:5 flags. */ + out_one ((flags << 3) | 1); + + /* Size of prologue. */ + if (c->endprologue_addr) + { + exp.X_op = O_subtract; + exp.X_add_symbol = c->endprologue_addr; + exp.X_op_symbol = c->start_addr; + exp.X_add_number = 0; + emit_expr (&exp, 1); + } + else + out_one (0); + + /* Number of slots (i.e. shorts) in the unwind codes array. */ + if (count_unwind_codes > 255) + as_fatal (_("too much unwind data in this .seh_proc")); + out_one (count_unwind_codes); + + /* ubyte:4 frame-reg, ubyte:4 frame-reg-offset. */ + /* Note that frameoff is already a multiple of 16, and therefore + the offset is already both scaled and shifted into place. */ + out_one (c->frameoff | c->framereg); + + seh_x64_write_prologue_data (c); + + /* We need to align prologue data. */ + if (count_unwind_codes & 1) + out_two (0); + + if (flags & (UNW_FLAG_EHANDLER | UNW_FLAG_UHANDLER)) + { + /* Force the use of segment-relative relocations instead of absolute + valued expressions. Don't adjust for constants (e.g. NULL). */ + if (c->handler.X_op == O_symbol) + c->handler.X_op = O_symbol_rva; + emit_expr (&c->handler, 4); + } + + /* Handler data will be tacked in here by subsections. */ +} + +/* Write out xdata for one function. */ + +static void +write_function_xdata (seh_context *c) +{ + segT save_seg = now_seg; + int save_subseg = now_subseg; + + /* MIPS, SH, ARM don't have xdata. */ + if (seh_get_target_kind () != seh_kind_x64) + return; + + switch_xdata (c->subsection, c->code_seg); + + seh_x64_write_function_xdata (c); + + subseg_set (save_seg, save_subseg); +} + +/* Write pdata section data for one function (arm). */ + +static void +seh_arm_write_function_pdata (seh_context *c) +{ + expressionS exp; + unsigned int prol_len = 0, func_len = 0; + unsigned int val; + + /* Start address of the function. */ + exp.X_op = O_symbol; + exp.X_add_symbol = c->start_addr; + exp.X_add_number = 0; + emit_expr (&exp, 4); + + exp.X_op = O_subtract; + exp.X_add_symbol = c->end_addr; + exp.X_op_symbol = c->start_addr; + exp.X_add_number = 0; + if (resolve_expression (&exp) && exp.X_op == O_constant) + func_len = exp.X_add_number; + else + as_bad (_(".seh_endproc in a different section from .seh_proc")); + + if (c->endprologue_addr) + { + exp.X_op = O_subtract; + exp.X_add_symbol = c->endprologue_addr; + exp.X_op_symbol = c->start_addr; + exp.X_add_number = 0; + + if (resolve_expression (&exp) && exp.X_op == O_constant) + prol_len = exp.X_add_number; + else + as_bad (_(".seh_endprologue in a different section from .seh_proc")); + } + + /* Both function and prologue are in units of instructions. */ + func_len >>= (c->use_instruction_32 ? 2 : 1); + prol_len >>= (c->use_instruction_32 ? 2 : 1); + + /* Assemble the second word of the pdata. */ + val = prol_len & 0xff; + val |= (func_len & 0x3fffff) << 8; + if (c->use_instruction_32) + val |= 0x40000000U; + if (c->handler_written) + val |= 0x80000000U; + out_four (val); +} + +/* Write out pdata for one function. */ + +static void +write_function_pdata (seh_context *c) +{ + expressionS exp; + segT save_seg = now_seg; + int save_subseg = now_subseg; + memset (&exp, 0, sizeof (expressionS)); + switch_pdata (c->code_seg); + + switch (seh_get_target_kind ()) + { + case seh_kind_x64: + exp.X_op = O_symbol_rva; + exp.X_add_number = 0; + + exp.X_add_symbol = c->start_addr; + emit_expr (&exp, 4); + exp.X_op = O_symbol_rva; + exp.X_add_number = 0; + exp.X_add_symbol = c->end_addr; + emit_expr (&exp, 4); + exp.X_op = O_symbol_rva; + exp.X_add_number = 0; + exp.X_add_symbol = c->xdata_addr; + emit_expr (&exp, 4); + break; + + case seh_kind_mips: + exp.X_op = O_symbol; + exp.X_add_number = 0; + + exp.X_add_symbol = c->start_addr; + emit_expr (&exp, 4); + exp.X_add_symbol = c->end_addr; + emit_expr (&exp, 4); + + emit_expr (&c->handler, 4); + emit_expr (&c->handler_data, 4); + + exp.X_add_symbol = (c->endprologue_addr + ? c->endprologue_addr + : c->start_addr); + emit_expr (&exp, 4); + break; + + case seh_kind_arm: + seh_arm_write_function_pdata (c); + break; + + default: + abort (); + } + + subseg_set (save_seg, save_subseg); +} diff --git a/contrib/toolchain/binutils/gas/config/obj-coff-seh.h b/contrib/toolchain/binutils/gas/config/obj-coff-seh.h new file mode 100644 index 0000000000..71c803f257 --- /dev/null +++ b/contrib/toolchain/binutils/gas/config/obj-coff-seh.h @@ -0,0 +1,205 @@ +/* seh pdata/xdata coff object file format + Copyright 2009, 2010, 2012 + Free Software Foundation, Inc. + + This file is part of GAS. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* Short overview: + There are at the moment three different function entry formats preset. + The first is the MIPS one. The second version + is for ARM, PPC, SH3, and SH4 mainly for Windows CE. + The third is the IA64 and x64 version. Note, the IA64 isn't implemented yet, + but to find information about it, please see specification about IA64 on + http://download.intel.com/design/Itanium/Downloads/245358.pdf file. + + The first version has just entries in the pdata section: BeginAddress, + EndAddress, ExceptionHandler, HandlerData, and PrologueEndAddress. Each + value is a pointer to the corresponding data and has size of 4 bytes. + + The second variant has the following entries in the pdata section. + BeginAddress, PrologueLength (8 bits), EndAddress (22 bits), + Use-32-bit-instruction (1 bit), and Exception-Handler-Exists (1 bit). + If the FunctionLength is zero, or the Exception-Handler-Exists bit + is true, a PDATA_EH block is placed directly before function entry. + + The third version has a function entry block of BeginAddress (RVA), + EndAddress (RVA), and UnwindData (RVA). The description of the + prologue, excepetion-handler, and additional SEH data is stored + within the UNWIND_DATA field in the xdata section. + + The pseudos: + .seh_proc + .seh_endprologue + .seh_handler [,@unwind][,@except] (x64) + .seh_handler [,] (others) + .seh_handlerdata + .seh_eh + .seh_32/.seh_no32 + .seh_endproc + .seh_setframe , + .seh_stackalloc + .seh_pushreg + .seh_savereg + .seh_savexmm + .seh_pushframe +*/ + +/* architecture specific pdata/xdata handling. */ +#define SEH_CMDS \ + {"seh_proc", obj_coff_seh_proc, 0}, \ + {"seh_endproc", obj_coff_seh_endproc, 0}, \ + {"seh_pushreg", obj_coff_seh_pushreg, 0}, \ + {"seh_savereg", obj_coff_seh_save, 1}, \ + {"seh_savexmm", obj_coff_seh_save, 2}, \ + {"seh_pushframe", obj_coff_seh_pushframe, 0}, \ + {"seh_endprologue", obj_coff_seh_endprologue, 0}, \ + {"seh_setframe", obj_coff_seh_setframe, 0}, \ + {"seh_stackalloc", obj_coff_seh_stackalloc, 0}, \ + {"seh_eh", obj_coff_seh_eh, 0}, \ + {"seh_32", obj_coff_seh_32, 1}, \ + {"seh_no32", obj_coff_seh_32, 0}, \ + {"seh_handler", obj_coff_seh_handler, 0}, \ + {"seh_handlerdata", obj_coff_seh_handlerdata, 0}, + +/* Type definitions. */ + +typedef struct seh_prologue_element +{ + int code; + int info; + offsetT off; + symbolS *pc_addr; +} seh_prologue_element; + +typedef struct seh_context +{ + struct seh_context *next; + + /* Initial code-segment. */ + segT code_seg; + /* Function name. */ + char *func_name; + /* BeginAddress. */ + symbolS *start_addr; + /* EndAddress. */ + symbolS *end_addr; + /* Unwind data. */ + symbolS *xdata_addr; + /* PrologueEnd. */ + symbolS *endprologue_addr; + /* ExceptionHandler. */ + expressionS handler; + /* ExceptionHandlerData. (arm, mips) */ + expressionS handler_data; + + /* ARM .seh_eh directive seen. */ + int handler_written; + + /* WinCE specific data. */ + int use_instruction_32; + /* Was record already processed. */ + int done; + + /* x64 flags for the xdata header. */ + int handler_flags; + int subsection; + + /* x64 framereg and frame offset information. */ + int framereg; + int frameoff; + + /* Information about x64 specific unwind data fields. */ + int elems_count; + int elems_max; + seh_prologue_element *elems; +} seh_context; + +typedef enum seh_kind { + seh_kind_unknown = 0, + seh_kind_mips = 1, /* Used for MIPS and x86 pdata generation. */ + seh_kind_arm = 2, /* Used for ARM, PPC, SH3, and SH4 pdata (PDATA_EH) generation. */ + seh_kind_x64 = 3 /* Used for IA64 and x64 pdata/xdata generation. */ +} seh_kind; + +/* Forward declarations. */ +static void obj_coff_seh_stackalloc (int); +static void obj_coff_seh_setframe (int); +static void obj_coff_seh_endprologue (int); +static void obj_coff_seh_save (int); +static void obj_coff_seh_pushreg (int); +static void obj_coff_seh_pushframe (int); +static void obj_coff_seh_endproc (int); +static void obj_coff_seh_eh (int); +static void obj_coff_seh_32 (int); +static void obj_coff_seh_proc (int); +static void obj_coff_seh_handler (int); +static void obj_coff_seh_handlerdata (int); + +#define UNDSEC bfd_und_section_ptr + +/* Check if x64 UNW_... macros are already defined. */ +#ifndef PEX64_FLAG_NHANDLER +/* We can't include here coff/pe.h header. So we have to copy macros + from coff/pe.h here. */ +#define PEX64_UNWCODE_CODE(VAL) ((VAL) & 0xf) +#define PEX64_UNWCODE_INFO(VAL) (((VAL) >> 4) & 0xf) + +/* The unwind info. */ +#define UNW_FLAG_NHANDLER 0 +#define UNW_FLAG_EHANDLER 1 +#define UNW_FLAG_UHANDLER 2 +#define UNW_FLAG_FHANDLER 3 +#define UNW_FLAG_CHAININFO 4 + +#define UNW_FLAG_MASK 0x1f + +/* The unwind codes. */ +#define UWOP_PUSH_NONVOL 0 +#define UWOP_ALLOC_LARGE 1 +#define UWOP_ALLOC_SMALL 2 +#define UWOP_SET_FPREG 3 +#define UWOP_SAVE_NONVOL 4 +#define UWOP_SAVE_NONVOL_FAR 5 +#define UWOP_SAVE_XMM 6 +#define UWOP_SAVE_XMM_FAR 7 +#define UWOP_SAVE_XMM128 8 +#define UWOP_SAVE_XMM128_FAR 9 +#define UWOP_PUSH_MACHFRAME 10 + +#define PEX64_UWI_VERSION(VAL) ((VAL) & 7) +#define PEX64_UWI_FLAGS(VAL) (((VAL) >> 3) & 0x1f) +#define PEX64_UWI_FRAMEREG(VAL) ((VAL) & 0xf) +#define PEX64_UWI_FRAMEOFF(VAL) (((VAL) >> 4) & 0xf) +#define PEX64_UWI_SIZEOF_UWCODE_ARRAY(VAL) \ + ((((VAL) + 1) & ~1) * 2) + +#define PEX64_OFFSET_TO_UNWIND_CODE 0x4 + +#define PEX64_OFFSET_TO_HANDLER_RVA (COUNTOFUNWINDCODES) \ + (PEX64_OFFSET_TO_UNWIND_CODE + \ + PEX64_UWI_SIZEOF_UWCODE_ARRAY(COUNTOFUNWINDCODES)) + +#define PEX64_OFFSET_TO_SCOPE_COUNT(COUNTOFUNWINDCODES) \ + (PEX64_OFFSET_TO_HANDLER_RVA(COUNTOFUNWINDCODES) + 4) + +#define PEX64_SCOPE_ENTRY(COUNTOFUNWINDCODES, IDX) \ + (PEX64_OFFSET_TO_SCOPE_COUNT(COUNTOFUNWINDCODES) + \ + PEX64_SCOPE_ENTRY_SIZE * (IDX)) + +#endif + diff --git a/contrib/toolchain/binutils/gas/config/obj-coff.c b/contrib/toolchain/binutils/gas/config/obj-coff.c new file mode 100644 index 0000000000..dbe2f079e8 --- /dev/null +++ b/contrib/toolchain/binutils/gas/config/obj-coff.c @@ -0,0 +1,1957 @@ +/* coff object file format + Copyright 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010 + Free Software Foundation, Inc. + + This file is part of GAS. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#define OBJ_HEADER "obj-coff.h" + +#include "as.h" +#include "safe-ctype.h" +#include "obstack.h" +#include "subsegs.h" +#include "struc-symbol.h" + +#ifdef TE_PE +#include "coff/pe.h" +#endif + +#ifdef OBJ_XCOFF +#include "coff/xcoff.h" +#endif + +#define streq(a,b) (strcmp ((a), (b)) == 0) +#define strneq(a,b,n) (strncmp ((a), (b), (n)) == 0) + +/* I think this is probably always correct. */ +#ifndef KEEP_RELOC_INFO +#define KEEP_RELOC_INFO +#endif + +/* obj_coff_section will use this macro to set a new section's + attributes when a directive has no valid flags or the "w" flag is + used. This default should be appropriate for most. */ +#ifndef TC_COFF_SECTION_DEFAULT_ATTRIBUTES +#define TC_COFF_SECTION_DEFAULT_ATTRIBUTES (SEC_LOAD | SEC_DATA) +#endif + +/* This is used to hold the symbol built by a sequence of pseudo-ops + from .def and .endef. */ +static symbolS *def_symbol_in_progress; +#ifdef TE_PE +/* PE weak alternate symbols begin with this string. */ +static const char weak_altprefix[] = ".weak."; +#endif /* TE_PE */ + +#include "obj-coff-seh.c" + +typedef struct + { + unsigned long chunk_size; + unsigned long element_size; + unsigned long size; + char *data; + unsigned long pointer; + } +stack; + + +/* Stack stuff. */ + +static stack * +stack_init (unsigned long chunk_size, + unsigned long element_size) +{ + stack *st; + + st = malloc (sizeof (* st)); + if (!st) + return NULL; + st->data = malloc (chunk_size); + if (!st->data) + { + free (st); + return NULL; + } + st->pointer = 0; + st->size = chunk_size; + st->chunk_size = chunk_size; + st->element_size = element_size; + return st; +} + +static char * +stack_push (stack *st, char *element) +{ + if (st->pointer + st->element_size >= st->size) + { + st->size += st->chunk_size; + if ((st->data = xrealloc (st->data, st->size)) == NULL) + return NULL; + } + memcpy (st->data + st->pointer, element, st->element_size); + st->pointer += st->element_size; + return st->data + st->pointer; +} + +static char * +stack_pop (stack *st) +{ + if (st->pointer < st->element_size) + { + st->pointer = 0; + return NULL; + } + st->pointer -= st->element_size; + return st->data + st->pointer; +} + +/* Maintain a list of the tagnames of the structures. */ + +static struct hash_control *tag_hash; + +static void +tag_init (void) +{ + tag_hash = hash_new (); +} + +static void +tag_insert (const char *name, symbolS *symbolP) +{ + const char *error_string; + + if ((error_string = hash_jam (tag_hash, name, (char *) symbolP))) + as_fatal (_("Inserting \"%s\" into structure table failed: %s"), + name, error_string); +} + +static symbolS * +tag_find (char *name) +{ + return (symbolS *) hash_find (tag_hash, name); +} + +static symbolS * +tag_find_or_make (char *name) +{ + symbolS *symbolP; + + if ((symbolP = tag_find (name)) == NULL) + { + symbolP = symbol_new (name, undefined_section, + 0, &zero_address_frag); + + tag_insert (S_GET_NAME (symbolP), symbolP); + symbol_table_insert (symbolP); + } + + return symbolP; +} + +/* We accept the .bss directive to set the section for backward + compatibility with earlier versions of gas. */ + +static void +obj_coff_bss (int ignore ATTRIBUTE_UNUSED) +{ + if (*input_line_pointer == '\n') + subseg_new (".bss", get_absolute_expression ()); + else + s_lcomm (0); +} + +#ifdef TE_PE +/* Called from read.c:s_comm after we've parsed .comm symbol, size. + Parse a possible alignment value. */ + +static symbolS * +obj_coff_common_parse (int ignore ATTRIBUTE_UNUSED, symbolS *symbolP, addressT size) +{ + addressT align = 0; + + if (*input_line_pointer == ',') + { + align = parse_align (0); + if (align == (addressT) -1) + return NULL; + } + + S_SET_VALUE (symbolP, size); + S_SET_EXTERNAL (symbolP); + S_SET_SEGMENT (symbolP, bfd_com_section_ptr); + + symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT; + + /* There is no S_SET_ALIGN (symbolP, align) in COFF/PE. + Instead we must add a note to the .drectve section. */ + if (align) + { + segT current_seg = now_seg; + subsegT current_subseg = now_subseg; + flagword oldflags; + asection *sec; + size_t pfxlen, numlen; + char *frag; + char numbuff[20]; + + sec = subseg_new (".drectve", 0); + oldflags = bfd_get_section_flags (stdoutput, sec); + if (oldflags == SEC_NO_FLAGS) + { + if (!bfd_set_section_flags (stdoutput, sec, + TC_COFF_SECTION_DEFAULT_ATTRIBUTES)) + as_warn (_("error setting flags for \"%s\": %s"), + bfd_section_name (stdoutput, sec), + bfd_errmsg (bfd_get_error ())); + } + + /* Emit a string. Note no NUL-termination. */ + pfxlen = strlen (" -aligncomm:") + 2 + strlen (S_GET_NAME (symbolP)) + 1; + numlen = snprintf (numbuff, sizeof (numbuff), "%d", (int) align); + frag = frag_more (pfxlen + numlen); + (void) sprintf (frag, " -aligncomm:\"%s\",", S_GET_NAME (symbolP)); + memcpy (frag + pfxlen, numbuff, numlen); + /* Restore original subseg. */ + subseg_set (current_seg, current_subseg); + } + + return symbolP; +} + +static void +obj_coff_comm (int ignore ATTRIBUTE_UNUSED) +{ + s_comm_internal (ignore, obj_coff_common_parse); +} +#endif /* TE_PE */ + +#define GET_FILENAME_STRING(X) \ + ((char *) (&((X)->sy_symbol.ost_auxent->x_file.x_n.x_offset))[1]) + +/* @@ Ick. */ +static segT +fetch_coff_debug_section (void) +{ + static segT debug_section; + + if (!debug_section) + { + const asymbol *s; + + s = bfd_make_debug_symbol (stdoutput, NULL, 0); + gas_assert (s != 0); + debug_section = s->section; + } + return debug_section; +} + +void +SA_SET_SYM_ENDNDX (symbolS *sym, symbolS *val) +{ + combined_entry_type *entry, *p; + + entry = &coffsymbol (symbol_get_bfdsym (sym))->native[1]; + p = coffsymbol (symbol_get_bfdsym (val))->native; + entry->u.auxent.x_sym.x_fcnary.x_fcn.x_endndx.p = p; + entry->fix_end = 1; +} + +static void +SA_SET_SYM_TAGNDX (symbolS *sym, symbolS *val) +{ + combined_entry_type *entry, *p; + + entry = &coffsymbol (symbol_get_bfdsym (sym))->native[1]; + p = coffsymbol (symbol_get_bfdsym (val))->native; + entry->u.auxent.x_sym.x_tagndx.p = p; + entry->fix_tag = 1; +} + +static int +S_GET_DATA_TYPE (symbolS *sym) +{ + return coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_type; +} + +int +S_SET_DATA_TYPE (symbolS *sym, int val) +{ + coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_type = val; + return val; +} + +int +S_GET_STORAGE_CLASS (symbolS *sym) +{ + return coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_sclass; +} + +int +S_SET_STORAGE_CLASS (symbolS *sym, int val) +{ + coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_sclass = val; + return val; +} + +/* Merge a debug symbol containing debug information into a normal symbol. */ + +static void +c_symbol_merge (symbolS *debug, symbolS *normal) +{ + S_SET_DATA_TYPE (normal, S_GET_DATA_TYPE (debug)); + S_SET_STORAGE_CLASS (normal, S_GET_STORAGE_CLASS (debug)); + + if (S_GET_NUMBER_AUXILIARY (debug) > S_GET_NUMBER_AUXILIARY (normal)) + /* Take the most we have. */ + S_SET_NUMBER_AUXILIARY (normal, S_GET_NUMBER_AUXILIARY (debug)); + + if (S_GET_NUMBER_AUXILIARY (debug) > 0) + /* Move all the auxiliary information. */ + memcpy (SYM_AUXINFO (normal), SYM_AUXINFO (debug), + (S_GET_NUMBER_AUXILIARY (debug) + * sizeof (*SYM_AUXINFO (debug)))); + + /* Move the debug flags. */ + SF_SET_DEBUG_FIELD (normal, SF_GET_DEBUG_FIELD (debug)); +} + +void +c_dot_file_symbol (const char *filename, int appfile ATTRIBUTE_UNUSED) +{ + symbolS *symbolP; + + /* BFD converts filename to a .file symbol with an aux entry. It + also handles chaining. */ + symbolP = symbol_new (filename, bfd_abs_section_ptr, 0, &zero_address_frag); + + S_SET_STORAGE_CLASS (symbolP, C_FILE); + S_SET_NUMBER_AUXILIARY (symbolP, 1); + + symbol_get_bfdsym (symbolP)->flags = BSF_DEBUGGING; + +#ifndef NO_LISTING + { + extern int listing; + + if (listing) + listing_source_file (filename); + } +#endif + + /* Make sure that the symbol is first on the symbol chain. */ + if (symbol_rootP != symbolP) + { + symbol_remove (symbolP, &symbol_rootP, &symbol_lastP); + symbol_insert (symbolP, symbol_rootP, &symbol_rootP, &symbol_lastP); + } +} + +/* Line number handling. */ + +struct line_no +{ + struct line_no *next; + fragS *frag; + alent l; +}; + +int coff_line_base; + +/* Symbol of last function, which we should hang line#s off of. */ +static symbolS *line_fsym; + +#define in_function() (line_fsym != 0) +#define clear_function() (line_fsym = 0) +#define set_function(F) (line_fsym = (F), coff_add_linesym (F)) + + +void +coff_obj_symbol_new_hook (symbolS *symbolP) +{ + long sz = (OBJ_COFF_MAX_AUXENTRIES + 1) * sizeof (combined_entry_type); + char * s = xmalloc (sz); + + memset (s, 0, sz); + coffsymbol (symbol_get_bfdsym (symbolP))->native = (combined_entry_type *) s; + + S_SET_DATA_TYPE (symbolP, T_NULL); + S_SET_STORAGE_CLASS (symbolP, 0); + S_SET_NUMBER_AUXILIARY (symbolP, 0); + + if (S_IS_STRING (symbolP)) + SF_SET_STRING (symbolP); + + if (S_IS_LOCAL (symbolP)) + SF_SET_LOCAL (symbolP); +} + +void +coff_obj_symbol_clone_hook (symbolS *newsymP, symbolS *orgsymP) +{ + long sz = (OBJ_COFF_MAX_AUXENTRIES + 1) * sizeof (combined_entry_type); + combined_entry_type * s = xmalloc (sz); + + memcpy (s, coffsymbol (symbol_get_bfdsym (orgsymP))->native, sz); + coffsymbol (symbol_get_bfdsym (newsymP))->native = s; + + SF_SET (newsymP, SF_GET (orgsymP)); +} + + +/* Handle .ln directives. */ + +static symbolS *current_lineno_sym; +static struct line_no *line_nos; +/* FIXME: Blindly assume all .ln directives will be in the .text section. */ +int coff_n_line_nos; + +static void +add_lineno (fragS * frag, addressT offset, int num) +{ + struct line_no * new_line = xmalloc (sizeof (* new_line)); + + if (!current_lineno_sym) + abort (); + +#ifndef OBJ_XCOFF + /* The native aix assembler accepts negative line number. */ + + if (num <= 0) + { + /* Zero is used as an end marker in the file. */ + as_warn (_("Line numbers must be positive integers\n")); + num = 1; + } +#endif /* OBJ_XCOFF */ + new_line->next = line_nos; + new_line->frag = frag; + new_line->l.line_number = num; + new_line->l.u.offset = offset; + line_nos = new_line; + coff_n_line_nos++; +} + +void +coff_add_linesym (symbolS *sym) +{ + if (line_nos) + { + coffsymbol (symbol_get_bfdsym (current_lineno_sym))->lineno = + (alent *) line_nos; + coff_n_line_nos++; + line_nos = 0; + } + current_lineno_sym = sym; +} + +static void +obj_coff_ln (int appline) +{ + int l; + + if (! appline && def_symbol_in_progress != NULL) + { + as_warn (_(".ln pseudo-op inside .def/.endef: ignored.")); + demand_empty_rest_of_line (); + return; + } + + l = get_absolute_expression (); + + /* If there is no lineno symbol, treat a .ln + directive as if it were a .appline directive. */ + if (appline || current_lineno_sym == NULL) + new_logical_line ((char *) NULL, l - 1); + else + add_lineno (frag_now, frag_now_fix (), l); + +#ifndef NO_LISTING + { + extern int listing; + + if (listing) + { + if (! appline) + l += coff_line_base - 1; + listing_source_line (l); + } + } +#endif + + demand_empty_rest_of_line (); +} + +/* .loc is essentially the same as .ln; parse it for assembler + compatibility. */ + +static void +obj_coff_loc (int ignore ATTRIBUTE_UNUSED) +{ + int lineno; + + /* FIXME: Why do we need this check? We need it for ECOFF, but why + do we need it for COFF? */ + if (now_seg != text_section) + { + as_warn (_(".loc outside of .text")); + demand_empty_rest_of_line (); + return; + } + + if (def_symbol_in_progress != NULL) + { + as_warn (_(".loc pseudo-op inside .def/.endef: ignored.")); + demand_empty_rest_of_line (); + return; + } + + /* Skip the file number. */ + SKIP_WHITESPACE (); + get_absolute_expression (); + SKIP_WHITESPACE (); + + lineno = get_absolute_expression (); + +#ifndef NO_LISTING + { + extern int listing; + + if (listing) + { + lineno += coff_line_base - 1; + listing_source_line (lineno); + } + } +#endif + + demand_empty_rest_of_line (); + + add_lineno (frag_now, frag_now_fix (), lineno); +} + +/* Handle the .ident pseudo-op. */ + +static void +obj_coff_ident (int ignore ATTRIBUTE_UNUSED) +{ + segT current_seg = now_seg; + subsegT current_subseg = now_subseg; + +#ifdef TE_PE + { + segT sec; + + /* We could put it in .comment, but that creates an extra section + that shouldn't be loaded into memory, which requires linker + changes... For now, until proven otherwise, use .rdata. */ + sec = subseg_new (".rdata$zzz", 0); + bfd_set_section_flags (stdoutput, sec, + ((SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_DATA) + & bfd_applicable_section_flags (stdoutput))); + } +#else + subseg_new (".comment", 0); +#endif + + stringer (8 + 1); + subseg_set (current_seg, current_subseg); +} + +/* Handle .def directives. + + One might ask : why can't we symbol_new if the symbol does not + already exist and fill it with debug information. Because of + the C_EFCN special symbol. It would clobber the value of the + function symbol before we have a chance to notice that it is + a C_EFCN. And a second reason is that the code is more clear this + way. (at least I think it is :-). */ + +#define SKIP_SEMI_COLON() while (*input_line_pointer++ != ';') +#define SKIP_WHITESPACES() while (*input_line_pointer == ' ' || \ + *input_line_pointer == '\t') \ + input_line_pointer++; + +static void +obj_coff_def (int what ATTRIBUTE_UNUSED) +{ + char name_end; /* Char after the end of name. */ + char *symbol_name; /* Name of the debug symbol. */ + char *symbol_name_copy; /* Temporary copy of the name. */ + unsigned int symbol_name_length; + + if (def_symbol_in_progress != NULL) + { + as_warn (_(".def pseudo-op used inside of .def/.endef: ignored.")); + demand_empty_rest_of_line (); + return; + } + + SKIP_WHITESPACES (); + + symbol_name = input_line_pointer; + name_end = get_symbol_end (); + symbol_name_length = strlen (symbol_name); + symbol_name_copy = xmalloc (symbol_name_length + 1); + strcpy (symbol_name_copy, symbol_name); +#ifdef tc_canonicalize_symbol_name + symbol_name_copy = tc_canonicalize_symbol_name (symbol_name_copy); +#endif + + /* Initialize the new symbol. */ + def_symbol_in_progress = symbol_make (symbol_name_copy); + symbol_set_frag (def_symbol_in_progress, &zero_address_frag); + S_SET_VALUE (def_symbol_in_progress, 0); + + if (S_IS_STRING (def_symbol_in_progress)) + SF_SET_STRING (def_symbol_in_progress); + + *input_line_pointer = name_end; + + demand_empty_rest_of_line (); +} + +static void +obj_coff_endef (int ignore ATTRIBUTE_UNUSED) +{ + symbolS *symbolP = NULL; + + if (def_symbol_in_progress == NULL) + { + as_warn (_(".endef pseudo-op used outside of .def/.endef: ignored.")); + demand_empty_rest_of_line (); + return; + } + + /* Set the section number according to storage class. */ + switch (S_GET_STORAGE_CLASS (def_symbol_in_progress)) + { + case C_STRTAG: + case C_ENTAG: + case C_UNTAG: + SF_SET_TAG (def_symbol_in_progress); + /* Fall through. */ + case C_FILE: + case C_TPDEF: + SF_SET_DEBUG (def_symbol_in_progress); + S_SET_SEGMENT (def_symbol_in_progress, fetch_coff_debug_section ()); + break; + + case C_EFCN: + SF_SET_LOCAL (def_symbol_in_progress); /* Do not emit this symbol. */ + /* Fall through. */ + case C_BLOCK: + SF_SET_PROCESS (def_symbol_in_progress); /* Will need processing before writing. */ + /* Fall through. */ + case C_FCN: + { + const char *name; + + S_SET_SEGMENT (def_symbol_in_progress, text_section); + + name = S_GET_NAME (def_symbol_in_progress); + if (name[0] == '.' && name[2] == 'f' && name[3] == '\0') + { + switch (name[1]) + { + case 'b': + /* .bf */ + if (! in_function ()) + as_warn (_("`%s' symbol without preceding function"), name); + /* Will need relocating. */ + SF_SET_PROCESS (def_symbol_in_progress); + clear_function (); + break; +#ifdef TE_PE + case 'e': + /* .ef */ + /* The MS compilers output the actual endline, not the + function-relative one... we want to match without + changing the assembler input. */ + SA_SET_SYM_LNNO (def_symbol_in_progress, + (SA_GET_SYM_LNNO (def_symbol_in_progress) + + coff_line_base)); + break; +#endif + } + } + } + break; + +#ifdef C_AUTOARG + case C_AUTOARG: +#endif /* C_AUTOARG */ + case C_AUTO: + case C_REG: + case C_ARG: + case C_REGPARM: + case C_FIELD: + + /* According to the COFF documentation: + + http://osr5doc.sco.com:1996/topics/COFF_SectNumFld.html + + A special section number (-2) marks symbolic debugging symbols, + including structure/union/enumeration tag names, typedefs, and + the name of the file. A section number of -1 indicates that the + symbol has a value but is not relocatable. Examples of + absolute-valued symbols include automatic and register variables, + function arguments, and .eos symbols. + + But from Ian Lance Taylor: + + http://sources.redhat.com/ml/binutils/2000-08/msg00202.html + + the actual tools all marked them as section -1. So the GNU COFF + assembler follows historical COFF assemblers. + + However, it causes problems for djgpp + + http://sources.redhat.com/ml/binutils/2000-08/msg00210.html + + By defining STRICTCOFF, a COFF port can make the assembler to + follow the documented behavior. */ +#ifdef STRICTCOFF + case C_MOS: + case C_MOE: + case C_MOU: + case C_EOS: +#endif + SF_SET_DEBUG (def_symbol_in_progress); + S_SET_SEGMENT (def_symbol_in_progress, absolute_section); + break; + +#ifndef STRICTCOFF + case C_MOS: + case C_MOE: + case C_MOU: + case C_EOS: + S_SET_SEGMENT (def_symbol_in_progress, absolute_section); + break; +#endif + + case C_EXT: + case C_WEAKEXT: +#ifdef TE_PE + case C_NT_WEAK: +#endif + case C_STAT: + case C_LABEL: + /* Valid but set somewhere else (s_comm, s_lcomm, colon). */ + break; + + default: + case C_USTATIC: + case C_EXTDEF: + case C_ULABEL: + as_warn (_("unexpected storage class %d"), + S_GET_STORAGE_CLASS (def_symbol_in_progress)); + break; + } + + /* Now that we have built a debug symbol, try to find if we should + merge with an existing symbol or not. If a symbol is C_EFCN or + absolute_section or untagged SEG_DEBUG it never merges. We also + don't merge labels, which are in a different namespace, nor + symbols which have not yet been defined since they are typically + unique, nor do we merge tags with non-tags. */ + + /* Two cases for functions. Either debug followed by definition or + definition followed by debug. For definition first, we will + merge the debug symbol into the definition. For debug first, the + lineno entry MUST point to the definition function or else it + will point off into space when obj_crawl_symbol_chain() merges + the debug symbol into the real symbol. Therefor, let's presume + the debug symbol is a real function reference. */ + + /* FIXME-SOON If for some reason the definition label/symbol is + never seen, this will probably leave an undefined symbol at link + time. */ + + if (S_GET_STORAGE_CLASS (def_symbol_in_progress) == C_EFCN + || S_GET_STORAGE_CLASS (def_symbol_in_progress) == C_LABEL + || (streq (bfd_get_section_name (stdoutput, + S_GET_SEGMENT (def_symbol_in_progress)), + "*DEBUG*") + && !SF_GET_TAG (def_symbol_in_progress)) + || S_GET_SEGMENT (def_symbol_in_progress) == absolute_section + || ! symbol_constant_p (def_symbol_in_progress) + || (symbolP = symbol_find (S_GET_NAME (def_symbol_in_progress))) == NULL + || SF_GET_TAG (def_symbol_in_progress) != SF_GET_TAG (symbolP)) + { + /* If it already is at the end of the symbol list, do nothing */ + if (def_symbol_in_progress != symbol_lastP) + { + symbol_remove (def_symbol_in_progress, &symbol_rootP, &symbol_lastP); + symbol_append (def_symbol_in_progress, symbol_lastP, &symbol_rootP, + &symbol_lastP); + } + } + else + { + /* This symbol already exists, merge the newly created symbol + into the old one. This is not mandatory. The linker can + handle duplicate symbols correctly. But I guess that it save + a *lot* of space if the assembly file defines a lot of + symbols. [loic] */ + + /* The debug entry (def_symbol_in_progress) is merged into the + previous definition. */ + + c_symbol_merge (def_symbol_in_progress, symbolP); + symbol_remove (def_symbol_in_progress, &symbol_rootP, &symbol_lastP); + + def_symbol_in_progress = symbolP; + + if (SF_GET_FUNCTION (def_symbol_in_progress) + || SF_GET_TAG (def_symbol_in_progress) + || S_GET_STORAGE_CLASS (def_symbol_in_progress) == C_STAT) + { + /* For functions, and tags, and static symbols, the symbol + *must* be where the debug symbol appears. Move the + existing symbol to the current place. */ + /* If it already is at the end of the symbol list, do nothing. */ + if (def_symbol_in_progress != symbol_lastP) + { + symbol_remove (def_symbol_in_progress, &symbol_rootP, &symbol_lastP); + symbol_append (def_symbol_in_progress, symbol_lastP, &symbol_rootP, &symbol_lastP); + } + } + } + + if (SF_GET_TAG (def_symbol_in_progress)) + { + symbolS *oldtag; + + oldtag = symbol_find (S_GET_NAME (def_symbol_in_progress)); + if (oldtag == NULL || ! SF_GET_TAG (oldtag)) + tag_insert (S_GET_NAME (def_symbol_in_progress), + def_symbol_in_progress); + } + + if (SF_GET_FUNCTION (def_symbol_in_progress)) + { + set_function (def_symbol_in_progress); + SF_SET_PROCESS (def_symbol_in_progress); + + if (symbolP == NULL) + /* That is, if this is the first time we've seen the + function. */ + symbol_table_insert (def_symbol_in_progress); + + } + + def_symbol_in_progress = NULL; + demand_empty_rest_of_line (); +} + +static void +obj_coff_dim (int ignore ATTRIBUTE_UNUSED) +{ + int d_index; + + if (def_symbol_in_progress == NULL) + { + as_warn (_(".dim pseudo-op used outside of .def/.endef: ignored.")); + demand_empty_rest_of_line (); + return; + } + + S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1); + + for (d_index = 0; d_index < DIMNUM; d_index++) + { + SKIP_WHITESPACES (); + SA_SET_SYM_DIMEN (def_symbol_in_progress, d_index, + get_absolute_expression ()); + + switch (*input_line_pointer) + { + case ',': + input_line_pointer++; + break; + + default: + as_warn (_("badly formed .dim directive ignored")); + /* Fall through. */ + case '\n': + case ';': + d_index = DIMNUM; + break; + } + } + + demand_empty_rest_of_line (); +} + +static void +obj_coff_line (int ignore ATTRIBUTE_UNUSED) +{ + int this_base; + + if (def_symbol_in_progress == NULL) + { + /* Probably stabs-style line? */ + obj_coff_ln (0); + return; + } + + this_base = get_absolute_expression (); + if (streq (".bf", S_GET_NAME (def_symbol_in_progress))) + coff_line_base = this_base; + + S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1); + SA_SET_SYM_LNNO (def_symbol_in_progress, this_base); + + demand_empty_rest_of_line (); + +#ifndef NO_LISTING + if (streq (".bf", S_GET_NAME (def_symbol_in_progress))) + { + extern int listing; + + if (listing) + listing_source_line ((unsigned int) this_base); + } +#endif +} + +static void +obj_coff_size (int ignore ATTRIBUTE_UNUSED) +{ + if (def_symbol_in_progress == NULL) + { + as_warn (_(".size pseudo-op used outside of .def/.endef ignored.")); + demand_empty_rest_of_line (); + return; + } + + S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1); + SA_SET_SYM_SIZE (def_symbol_in_progress, get_absolute_expression ()); + demand_empty_rest_of_line (); +} + +static void +obj_coff_scl (int ignore ATTRIBUTE_UNUSED) +{ + if (def_symbol_in_progress == NULL) + { + as_warn (_(".scl pseudo-op used outside of .def/.endef ignored.")); + demand_empty_rest_of_line (); + return; + } + + S_SET_STORAGE_CLASS (def_symbol_in_progress, get_absolute_expression ()); + demand_empty_rest_of_line (); +} + +static void +obj_coff_tag (int ignore ATTRIBUTE_UNUSED) +{ + char *symbol_name; + char name_end; + + if (def_symbol_in_progress == NULL) + { + as_warn (_(".tag pseudo-op used outside of .def/.endef ignored.")); + demand_empty_rest_of_line (); + return; + } + + S_SET_NUMBER_AUXILIARY (def_symbol_in_progress, 1); + symbol_name = input_line_pointer; + name_end = get_symbol_end (); + +#ifdef tc_canonicalize_symbol_name + symbol_name = tc_canonicalize_symbol_name (symbol_name); +#endif + + /* Assume that the symbol referred to by .tag is always defined. + This was a bad assumption. I've added find_or_make. xoxorich. */ + SA_SET_SYM_TAGNDX (def_symbol_in_progress, + tag_find_or_make (symbol_name)); + if (SA_GET_SYM_TAGNDX (def_symbol_in_progress) == 0L) + as_warn (_("tag not found for .tag %s"), symbol_name); + + SF_SET_TAGGED (def_symbol_in_progress); + *input_line_pointer = name_end; + + demand_empty_rest_of_line (); +} + +static void +obj_coff_type (int ignore ATTRIBUTE_UNUSED) +{ + if (def_symbol_in_progress == NULL) + { + as_warn (_(".type pseudo-op used outside of .def/.endef ignored.")); + demand_empty_rest_of_line (); + return; + } + + S_SET_DATA_TYPE (def_symbol_in_progress, get_absolute_expression ()); + + if (ISFCN (S_GET_DATA_TYPE (def_symbol_in_progress)) && + S_GET_STORAGE_CLASS (def_symbol_in_progress) != C_TPDEF) + SF_SET_FUNCTION (def_symbol_in_progress); + + demand_empty_rest_of_line (); +} + +static void +obj_coff_val (int ignore ATTRIBUTE_UNUSED) +{ + if (def_symbol_in_progress == NULL) + { + as_warn (_(".val pseudo-op used outside of .def/.endef ignored.")); + demand_empty_rest_of_line (); + return; + } + + if (is_name_beginner (*input_line_pointer)) + { + char *symbol_name = input_line_pointer; + char name_end = get_symbol_end (); + +#ifdef tc_canonicalize_symbol_name + symbol_name = tc_canonicalize_symbol_name (symbol_name); +#endif + if (streq (symbol_name, ".")) + { + /* If the .val is != from the .def (e.g. statics). */ + symbol_set_frag (def_symbol_in_progress, frag_now); + S_SET_VALUE (def_symbol_in_progress, (valueT) frag_now_fix ()); + } + else if (! streq (S_GET_NAME (def_symbol_in_progress), symbol_name)) + { + expressionS exp; + + exp.X_op = O_symbol; + exp.X_add_symbol = symbol_find_or_make (symbol_name); + exp.X_op_symbol = NULL; + exp.X_add_number = 0; + symbol_set_value_expression (def_symbol_in_progress, &exp); + + /* If the segment is undefined when the forward reference is + resolved, then copy the segment id from the forward + symbol. */ + SF_SET_GET_SEGMENT (def_symbol_in_progress); + + /* FIXME: gcc can generate address expressions here in + unusual cases (search for "obscure" in sdbout.c). We + just ignore the offset here, thus generating incorrect + debugging information. We ignore the rest of the line + just below. */ + } + /* Otherwise, it is the name of a non debug symbol and its value + will be calculated later. */ + *input_line_pointer = name_end; + } + else + { + S_SET_VALUE (def_symbol_in_progress, get_absolute_expression ()); + } + + demand_empty_rest_of_line (); +} + +#ifdef TE_PE + +/* Return nonzero if name begins with weak alternate symbol prefix. */ + +static int +weak_is_altname (const char * name) +{ + return strneq (name, weak_altprefix, sizeof (weak_altprefix) - 1); +} + +/* Return the name of the alternate symbol + name corresponding to a weak symbol's name. */ + +static const char * +weak_name2altname (const char * name) +{ + char *alt_name; + + alt_name = xmalloc (sizeof (weak_altprefix) + strlen (name)); + strcpy (alt_name, weak_altprefix); + return strcat (alt_name, name); +} + +/* Return the name of the weak symbol corresponding to an + alternate symbol. */ + +static const char * +weak_altname2name (const char * name) +{ + gas_assert (weak_is_altname (name)); + return xstrdup (name + 6); +} + +/* Make a weak symbol name unique by + appending the name of an external symbol. */ + +static const char * +weak_uniquify (const char * name) +{ + char *ret; + const char * unique = ""; + +#ifdef TE_PE + if (an_external_name != NULL) + unique = an_external_name; +#endif + gas_assert (weak_is_altname (name)); + + ret = xmalloc (strlen (name) + strlen (unique) + 2); + strcpy (ret, name); + strcat (ret, "."); + strcat (ret, unique); + return ret; +} + +void +pecoff_obj_set_weak_hook (symbolS *symbolP) +{ + symbolS *alternateP; + + /* See _Microsoft Portable Executable and Common Object + File Format Specification_, section 5.5.3. + Create a symbol representing the alternate value. + coff_frob_symbol will set the value of this symbol from + the value of the weak symbol itself. */ + S_SET_STORAGE_CLASS (symbolP, C_NT_WEAK); + S_SET_NUMBER_AUXILIARY (symbolP, 1); + SA_SET_SYM_FSIZE (symbolP, IMAGE_WEAK_EXTERN_SEARCH_NOLIBRARY); + + alternateP = symbol_find_or_make (weak_name2altname (S_GET_NAME (symbolP))); + S_SET_EXTERNAL (alternateP); + S_SET_STORAGE_CLASS (alternateP, C_NT_WEAK); + + SA_SET_SYM_TAGNDX (symbolP, alternateP); +} + +void +pecoff_obj_clear_weak_hook (symbolS *symbolP) +{ + symbolS *alternateP; + + S_SET_STORAGE_CLASS (symbolP, 0); + SA_SET_SYM_FSIZE (symbolP, 0); + + alternateP = symbol_find (weak_name2altname (S_GET_NAME (symbolP))); + S_CLEAR_EXTERNAL (alternateP); +} + +#endif /* TE_PE */ + +/* Handle .weak. This is a GNU extension in formats other than PE. */ + +static void +obj_coff_weak (int ignore ATTRIBUTE_UNUSED) +{ + char *name; + int c; + symbolS *symbolP; + + do + { + name = input_line_pointer; + c = get_symbol_end (); + if (*name == 0) + { + as_warn (_("badly formed .weak directive ignored")); + ignore_rest_of_line (); + return; + } + c = 0; + symbolP = symbol_find_or_make (name); + *input_line_pointer = c; + SKIP_WHITESPACE (); + S_SET_WEAK (symbolP); + + if (c == ',') + { + input_line_pointer++; + SKIP_WHITESPACE (); + if (*input_line_pointer == '\n') + c = '\n'; + } + + } + while (c == ','); + + demand_empty_rest_of_line (); +} + +void +coff_obj_read_begin_hook (void) +{ + /* These had better be the same. Usually 18 bytes. */ + know (sizeof (SYMENT) == sizeof (AUXENT)); + know (SYMESZ == AUXESZ); + tag_init (); +} + +symbolS *coff_last_function; +#ifndef OBJ_XCOFF +static symbolS *coff_last_bf; +#endif + +void +coff_frob_symbol (symbolS *symp, int *punt) +{ + static symbolS *last_tagP; + static stack *block_stack; + static symbolS *set_end; + symbolS *next_set_end = NULL; + + if (symp == &abs_symbol) + { + *punt = 1; + return; + } + + if (current_lineno_sym) + coff_add_linesym (NULL); + + if (!block_stack) + block_stack = stack_init (512, sizeof (symbolS*)); + +#ifdef TE_PE + if (S_GET_STORAGE_CLASS (symp) == C_NT_WEAK + && ! S_IS_WEAK (symp) + && weak_is_altname (S_GET_NAME (symp))) + { + /* This is a weak alternate symbol. All processing of + PECOFFweak symbols is done here, through the alternate. */ + symbolS *weakp = symbol_find_noref (weak_altname2name + (S_GET_NAME (symp)), 1); + + gas_assert (weakp); + gas_assert (S_GET_NUMBER_AUXILIARY (weakp) == 1); + + if (! S_IS_WEAK (weakp)) + { + /* The symbol was turned from weak to strong. Discard altname. */ + *punt = 1; + return; + } + else if (symbol_equated_p (weakp)) + { + /* The weak symbol has an alternate specified; symp is unneeded. */ + S_SET_STORAGE_CLASS (weakp, C_NT_WEAK); + SA_SET_SYM_TAGNDX (weakp, + symbol_get_value_expression (weakp)->X_add_symbol); + + S_CLEAR_EXTERNAL (symp); + *punt = 1; + return; + } + else + { + /* The weak symbol has been assigned an alternate value. + Copy this value to symp, and set symp as weakp's alternate. */ + if (S_GET_STORAGE_CLASS (weakp) != C_NT_WEAK) + { + S_SET_STORAGE_CLASS (symp, S_GET_STORAGE_CLASS (weakp)); + S_SET_STORAGE_CLASS (weakp, C_NT_WEAK); + } + + if (S_IS_DEFINED (weakp)) + { + /* This is a defined weak symbol. Copy value information + from the weak symbol itself to the alternate symbol. */ + symbol_set_value_expression (symp, + symbol_get_value_expression (weakp)); + symbol_set_frag (symp, symbol_get_frag (weakp)); + S_SET_SEGMENT (symp, S_GET_SEGMENT (weakp)); + } + else + { + /* This is an undefined weak symbol. + Define the alternate symbol to zero. */ + S_SET_VALUE (symp, 0); + S_SET_SEGMENT (symp, absolute_section); + } + + S_SET_NAME (symp, weak_uniquify (S_GET_NAME (symp))); + S_SET_STORAGE_CLASS (symp, C_EXT); + + S_SET_VALUE (weakp, 0); + S_SET_SEGMENT (weakp, undefined_section); + } + } +#else /* TE_PE */ + if (S_IS_WEAK (symp)) + S_SET_STORAGE_CLASS (symp, C_WEAKEXT); +#endif /* TE_PE */ + + if (!S_IS_DEFINED (symp) + && !S_IS_WEAK (symp) + && S_GET_STORAGE_CLASS (symp) != C_STAT) + S_SET_STORAGE_CLASS (symp, C_EXT); + + if (!SF_GET_DEBUG (symp)) + { + symbolS * real; + + if (!SF_GET_LOCAL (symp) + && !SF_GET_STATICS (symp) + && S_GET_STORAGE_CLASS (symp) != C_LABEL + && symbol_constant_p (symp) + && (real = symbol_find_noref (S_GET_NAME (symp), 1)) + && S_GET_STORAGE_CLASS (real) == C_NULL + && real != symp) + { + c_symbol_merge (symp, real); + *punt = 1; + return; + } + + if (!S_IS_DEFINED (symp) && !SF_GET_LOCAL (symp)) + { + gas_assert (S_GET_VALUE (symp) == 0); + if (S_IS_WEAKREFD (symp)) + *punt = 1; + else + S_SET_EXTERNAL (symp); + } + else if (S_GET_STORAGE_CLASS (symp) == C_NULL) + { + if (S_GET_SEGMENT (symp) == text_section + && symp != seg_info (text_section)->sym) + S_SET_STORAGE_CLASS (symp, C_LABEL); + else + S_SET_STORAGE_CLASS (symp, C_STAT); + } + + if (SF_GET_PROCESS (symp)) + { + if (S_GET_STORAGE_CLASS (symp) == C_BLOCK) + { + if (streq (S_GET_NAME (symp), ".bb")) + stack_push (block_stack, (char *) &symp); + else + { + symbolS *begin; + + begin = *(symbolS **) stack_pop (block_stack); + if (begin == 0) + as_warn (_("mismatched .eb")); + else + next_set_end = begin; + } + } + + if (coff_last_function == 0 && SF_GET_FUNCTION (symp) + && S_IS_DEFINED (symp)) + { + union internal_auxent *auxp; + + coff_last_function = symp; + if (S_GET_NUMBER_AUXILIARY (symp) < 1) + S_SET_NUMBER_AUXILIARY (symp, 1); + auxp = SYM_AUXENT (symp); + memset (auxp->x_sym.x_fcnary.x_ary.x_dimen, 0, + sizeof (auxp->x_sym.x_fcnary.x_ary.x_dimen)); + } + + if (S_GET_STORAGE_CLASS (symp) == C_EFCN + && S_IS_DEFINED (symp)) + { + if (coff_last_function == 0) + as_fatal (_("C_EFCN symbol for %s out of scope"), + S_GET_NAME (symp)); + SA_SET_SYM_FSIZE (coff_last_function, + (long) (S_GET_VALUE (symp) + - S_GET_VALUE (coff_last_function))); + next_set_end = coff_last_function; + coff_last_function = 0; + } + } + + if (S_IS_EXTERNAL (symp)) + S_SET_STORAGE_CLASS (symp, C_EXT); + else if (SF_GET_LOCAL (symp)) + *punt = 1; + + if (SF_GET_FUNCTION (symp)) + symbol_get_bfdsym (symp)->flags |= BSF_FUNCTION; + } + + /* Double check weak symbols. */ + if (S_IS_WEAK (symp) && S_IS_COMMON (symp)) + as_bad (_("Symbol `%s' can not be both weak and common"), + S_GET_NAME (symp)); + + if (SF_GET_TAG (symp)) + last_tagP = symp; + else if (S_GET_STORAGE_CLASS (symp) == C_EOS) + next_set_end = last_tagP; + +#ifdef OBJ_XCOFF + /* This is pretty horrible, but we have to set *punt correctly in + order to call SA_SET_SYM_ENDNDX correctly. */ + if (! symbol_used_in_reloc_p (symp) + && ((symbol_get_bfdsym (symp)->flags & BSF_SECTION_SYM) != 0 + || (! (S_IS_EXTERNAL (symp) || S_IS_WEAK (symp)) + && ! symbol_get_tc (symp)->output + && S_GET_STORAGE_CLASS (symp) != C_FILE))) + *punt = 1; +#endif + + if (set_end != (symbolS *) NULL + && ! *punt + && ((symbol_get_bfdsym (symp)->flags & BSF_NOT_AT_END) != 0 + || (S_IS_DEFINED (symp) + && ! S_IS_COMMON (symp) + && (! S_IS_EXTERNAL (symp) || SF_GET_FUNCTION (symp))))) + { + SA_SET_SYM_ENDNDX (set_end, symp); + set_end = NULL; + } + + if (next_set_end != NULL) + { + if (set_end != NULL) + as_warn (_("Warning: internal error: forgetting to set endndx of %s"), + S_GET_NAME (set_end)); + set_end = next_set_end; + } + +#ifndef OBJ_XCOFF + if (! *punt + && S_GET_STORAGE_CLASS (symp) == C_FCN + && streq (S_GET_NAME (symp), ".bf")) + { + if (coff_last_bf != NULL) + SA_SET_SYM_ENDNDX (coff_last_bf, symp); + coff_last_bf = symp; + } +#endif + if (coffsymbol (symbol_get_bfdsym (symp))->lineno) + { + int i; + struct line_no *lptr; + alent *l; + + lptr = (struct line_no *) coffsymbol (symbol_get_bfdsym (symp))->lineno; + for (i = 0; lptr; lptr = lptr->next) + i++; + lptr = (struct line_no *) coffsymbol (symbol_get_bfdsym (symp))->lineno; + + /* We need i entries for line numbers, plus 1 for the first + entry which BFD will override, plus 1 for the last zero + entry (a marker for BFD). */ + l = xmalloc ((i + 2) * sizeof (* l)); + coffsymbol (symbol_get_bfdsym (symp))->lineno = l; + l[i + 1].line_number = 0; + l[i + 1].u.sym = NULL; + for (; i > 0; i--) + { + if (lptr->frag) + lptr->l.u.offset += lptr->frag->fr_address / OCTETS_PER_BYTE; + l[i] = lptr->l; + lptr = lptr->next; + } + } +} + +void +coff_adjust_section_syms (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, + void * x ATTRIBUTE_UNUSED) +{ + symbolS *secsym; + segment_info_type *seginfo = seg_info (sec); + int nlnno, nrelocs = 0; + + /* RS/6000 gas creates a .debug section manually in ppc_frob_file in + tc-ppc.c. Do not get confused by it. */ + if (seginfo == NULL) + return; + + if (streq (sec->name, ".text")) + nlnno = coff_n_line_nos; + else + nlnno = 0; + { + /* @@ Hope that none of the fixups expand to more than one reloc + entry... */ + fixS *fixp = seginfo->fix_root; + while (fixp) + { + if (! fixp->fx_done) + nrelocs++; + fixp = fixp->fx_next; + } + } + if (bfd_get_section_size (sec) == 0 + && nrelocs == 0 + && nlnno == 0 + && sec != text_section + && sec != data_section + && sec != bss_section) + return; + + secsym = section_symbol (sec); + /* This is an estimate; we'll plug in the real value using + SET_SECTION_RELOCS later */ + SA_SET_SCN_NRELOC (secsym, nrelocs); + SA_SET_SCN_NLINNO (secsym, nlnno); +} + +void +coff_frob_file_after_relocs (void) +{ + bfd_map_over_sections (stdoutput, coff_adjust_section_syms, NULL); +} + +/* 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) + 'e' for exclude + 'x' for text + 'r' for read-only data + 's' for shared data (PE) + 'y' for noread + '0' - '9' for power-of-two alignment (GNU extension). + But if the argument is not a quoted string, treat it as a + subsegment number. + + Note the 'a' flag is silently ignored. This allows the same + .section directive to be parsed in both ELF and COFF formats. */ + +void +obj_coff_section (int ignore ATTRIBUTE_UNUSED) +{ + /* Strip out the section name. */ + char *section_name; + char c; + int alignment = -1; + char *name; + unsigned int exp; + flagword flags, oldflags; + asection *sec; + + if (flag_mri) + { + char type; + + s_mri_sect (&type); + return; + } + + section_name = input_line_pointer; + c = get_symbol_end (); + + name = xmalloc (input_line_pointer - section_name + 1); + strcpy (name, section_name); + + *input_line_pointer = c; + + SKIP_WHITESPACE (); + + exp = 0; + flags = SEC_NO_FLAGS; + + if (*input_line_pointer == ',') + { + ++input_line_pointer; + SKIP_WHITESPACE (); + if (*input_line_pointer != '"') + exp = get_absolute_expression (); + else + { + unsigned char attr; + int readonly_removed = 0; + int load_removed = 0; + + while (attr = *++input_line_pointer, + attr != '"' + && ! is_end_of_line[attr]) + { + if (ISDIGIT (attr)) + { + alignment = attr - '0'; + continue; + } + switch (attr) + { + case 'e': + /* Exclude section from linking. */ + flags |= SEC_EXCLUDE; + break; + + case 'b': + /* Uninitialised data section. */ + flags |= SEC_ALLOC; + flags &=~ SEC_LOAD; + break; + + case 'n': + /* Section not loaded. */ + flags &=~ SEC_LOAD; + flags |= SEC_NEVER_LOAD; + load_removed = 1; + break; + + case 's': + /* Shared section. */ + flags |= SEC_COFF_SHARED; + /* Fall through. */ + case 'd': + /* Data section. */ + flags |= SEC_DATA; + if (! load_removed) + flags |= SEC_LOAD; + flags &=~ SEC_READONLY; + break; + + case 'w': + /* Writable section. */ + flags &=~ SEC_READONLY; + readonly_removed = 1; + break; + + case 'a': + /* Ignore. Here for compatibility with ELF. */ + break; + + case 'r': /* Read-only section. Implies a data section. */ + readonly_removed = 0; + /* Fall through. */ + case 'x': /* Executable section. */ + /* If we are setting the 'x' attribute or if the 'r' + attribute is being used to restore the readonly status + of a code section (eg "wxr") then set the SEC_CODE flag, + otherwise set the SEC_DATA flag. */ + flags |= (attr == 'x' || (flags & SEC_CODE) ? SEC_CODE : SEC_DATA); + if (! load_removed) + flags |= SEC_LOAD; + /* Note - the READONLY flag is set here, even for the 'x' + attribute in order to be compatible with the MSVC + linker. */ + if (! readonly_removed) + flags |= SEC_READONLY; + break; + + case 'y': + flags |= SEC_COFF_NOREAD | SEC_READONLY; + break; + + case 'i': /* STYP_INFO */ + case 'l': /* STYP_LIB */ + case 'o': /* STYP_OVER */ + as_warn (_("unsupported section attribute '%c'"), attr); + break; + + default: + as_warn (_("unknown section attribute '%c'"), attr); + break; + } + } + if (attr == '"') + ++input_line_pointer; + } + } + + sec = subseg_new (name, (subsegT) exp); + + if (alignment >= 0) + sec->alignment_power = alignment; + + oldflags = bfd_get_section_flags (stdoutput, sec); + if (oldflags == SEC_NO_FLAGS) + { + /* Set section flags for a new section just created by subseg_new. + Provide a default if no flags were parsed. */ + if (flags == SEC_NO_FLAGS) + flags = TC_COFF_SECTION_DEFAULT_ATTRIBUTES; + +#ifdef COFF_LONG_SECTION_NAMES + /* Add SEC_LINK_ONCE and SEC_LINK_DUPLICATES_DISCARD to .gnu.linkonce + sections so adjust_reloc_syms in write.c will correctly handle + relocs which refer to non-local symbols in these sections. */ + if (strneq (name, ".gnu.linkonce", sizeof (".gnu.linkonce") - 1)) + flags |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD; +#endif + + if (! bfd_set_section_flags (stdoutput, sec, flags)) + as_warn (_("error setting flags for \"%s\": %s"), + bfd_section_name (stdoutput, sec), + bfd_errmsg (bfd_get_error ())); + } + else if (flags != SEC_NO_FLAGS) + { + /* This section's attributes have already been set. Warn if the + attributes don't match. */ + flagword matchflags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE + | SEC_DATA | SEC_COFF_SHARED | SEC_NEVER_LOAD + | SEC_COFF_NOREAD); + if ((flags ^ oldflags) & matchflags) + as_warn (_("Ignoring changed section attributes for %s"), name); + } + + demand_empty_rest_of_line (); +} + +void +coff_adjust_symtab (void) +{ + if (symbol_rootP == NULL + || S_GET_STORAGE_CLASS (symbol_rootP) != C_FILE) + c_dot_file_symbol ("fake", 0); +} + +void +coff_frob_section (segT sec) +{ + segT strsec; + char *p; + fragS *fragp; + bfd_vma n_entries; + + /* The COFF back end in BFD requires that all section sizes be + rounded up to multiples of the corresponding section alignments, + supposedly because standard COFF has no other way of encoding alignment + for sections. If your COFF flavor has a different way of encoding + section alignment, then skip this step, as TICOFF does. */ + bfd_vma size = bfd_get_section_size (sec); +#if !defined(TICOFF) + bfd_vma align_power = (bfd_vma) sec->alignment_power + OCTETS_PER_BYTE_POWER; + bfd_vma mask = ((bfd_vma) 1 << align_power) - 1; + + if (size & mask) + { + bfd_vma new_size; + fragS *last; + + new_size = (size + mask) & ~mask; + bfd_set_section_size (stdoutput, sec, new_size); + + /* If the size had to be rounded up, add some padding in + the last non-empty frag. */ + fragp = seg_info (sec)->frchainP->frch_root; + last = seg_info (sec)->frchainP->frch_last; + while (fragp->fr_next != last) + fragp = fragp->fr_next; + last->fr_address = size; + fragp->fr_offset += new_size - size; + } +#endif + + /* If the section size is non-zero, the section symbol needs an aux + entry associated with it, indicating the size. We don't know + all the values yet; coff_frob_symbol will fill them in later. */ +#ifndef TICOFF + if (size != 0 + || sec == text_section + || sec == data_section + || sec == bss_section) +#endif + { + symbolS *secsym = section_symbol (sec); + unsigned char sclass = C_STAT; + +#ifdef OBJ_XCOFF + if (bfd_get_section_flags (stdoutput, sec) & SEC_DEBUGGING) + sclass = C_DWARF; +#endif + S_SET_STORAGE_CLASS (secsym, sclass); + S_SET_NUMBER_AUXILIARY (secsym, 1); + SF_SET_STATICS (secsym); + SA_SET_SCN_SCNLEN (secsym, size); + } + /* FIXME: These should be in a "stabs.h" file, or maybe as.h. */ +#ifndef STAB_SECTION_NAME +#define STAB_SECTION_NAME ".stab" +#endif +#ifndef STAB_STRING_SECTION_NAME +#define STAB_STRING_SECTION_NAME ".stabstr" +#endif + if (! streq (STAB_STRING_SECTION_NAME, sec->name)) + return; + + strsec = sec; + sec = subseg_get (STAB_SECTION_NAME, 0); + /* size is already rounded up, since other section will be listed first */ + size = bfd_get_section_size (strsec); + + n_entries = bfd_get_section_size (sec) / 12 - 1; + + /* Find first non-empty frag. It should be large enough. */ + fragp = seg_info (sec)->frchainP->frch_root; + while (fragp && fragp->fr_fix == 0) + fragp = fragp->fr_next; + gas_assert (fragp != 0 && fragp->fr_fix >= 12); + + /* Store the values. */ + p = fragp->fr_literal; + bfd_h_put_16 (stdoutput, n_entries, (bfd_byte *) p + 6); + bfd_h_put_32 (stdoutput, size, (bfd_byte *) p + 8); +} + +void +obj_coff_init_stab_section (segT seg) +{ + char *file; + char *p; + char *stabstr_name; + unsigned int stroff; + + /* Make space for this first symbol. */ + p = frag_more (12); + /* Zero it out. */ + memset (p, 0, 12); + as_where (&file, (unsigned int *) NULL); + stabstr_name = xmalloc (strlen (seg->name) + 4); + strcpy (stabstr_name, seg->name); + strcat (stabstr_name, "str"); + stroff = get_stab_string_offset (file, stabstr_name); + know (stroff == 1); + md_number_to_chars (p, stroff, 4); +} + +#ifdef DEBUG +const char * s_get_name (symbolS *); + +const char * +s_get_name (symbolS *s) +{ + return ((s == NULL) ? "(NULL)" : S_GET_NAME (s)); +} + +void symbol_dump (void); + +void +symbol_dump (void) +{ + symbolS *symbolP; + + for (symbolP = symbol_rootP; symbolP; symbolP = symbol_next (symbolP)) + printf (_("0x%lx: \"%s\" type = %ld, class = %d, segment = %d\n"), + (unsigned long) symbolP, + S_GET_NAME (symbolP), + (long) S_GET_DATA_TYPE (symbolP), + S_GET_STORAGE_CLASS (symbolP), + (int) S_GET_SEGMENT (symbolP)); +} + +#endif /* DEBUG */ + +const pseudo_typeS coff_pseudo_table[] = +{ + {"ABORT", s_abort, 0}, + {"appline", obj_coff_ln, 1}, + /* We accept the .bss directive for backward compatibility with + earlier versions of gas. */ + {"bss", obj_coff_bss, 0}, +#ifdef TE_PE + /* PE provides an enhanced version of .comm with alignment. */ + {"comm", obj_coff_comm, 0}, +#endif /* TE_PE */ + {"def", obj_coff_def, 0}, + {"dim", obj_coff_dim, 0}, + {"endef", obj_coff_endef, 0}, + {"ident", obj_coff_ident, 0}, + {"line", obj_coff_line, 0}, + {"ln", obj_coff_ln, 0}, + {"scl", obj_coff_scl, 0}, + {"sect", obj_coff_section, 0}, + {"sect.s", obj_coff_section, 0}, + {"section", obj_coff_section, 0}, + {"section.s", obj_coff_section, 0}, + /* FIXME: We ignore the MRI short attribute. */ + {"size", obj_coff_size, 0}, + {"tag", obj_coff_tag, 0}, + {"type", obj_coff_type, 0}, + {"val", obj_coff_val, 0}, + {"version", s_ignore, 0}, + {"loc", obj_coff_loc, 0}, + {"optim", s_ignore, 0}, /* For sun386i cc (?) */ + {"weak", obj_coff_weak, 0}, +#if defined TC_TIC4X + /* The tic4x uses sdef instead of def. */ + {"sdef", obj_coff_def, 0}, +#endif +#if defined(SEH_CMDS) + SEH_CMDS +#endif + {NULL, NULL, 0} +}; + + +/* Support for a COFF emulation. */ + +static void +coff_pop_insert (void) +{ + pop_insert (coff_pseudo_table); +} + +static int +coff_separate_stab_sections (void) +{ + return 1; +} + +const struct format_ops coff_format_ops = +{ + bfd_target_coff_flavour, + 0, /* dfl_leading_underscore */ + 1, /* emit_section_symbols */ + 0, /* begin */ + c_dot_file_symbol, + coff_frob_symbol, + 0, /* frob_file */ + 0, /* frob_file_before_adjust */ + 0, /* frob_file_before_fix */ + coff_frob_file_after_relocs, + 0, /* s_get_size */ + 0, /* s_set_size */ + 0, /* s_get_align */ + 0, /* s_set_align */ + 0, /* s_get_other */ + 0, /* s_set_other */ + 0, /* s_get_desc */ + 0, /* s_set_desc */ + 0, /* s_get_type */ + 0, /* s_set_type */ + 0, /* copy_symbol_attributes */ + 0, /* generate_asm_lineno */ + 0, /* process_stab */ + coff_separate_stab_sections, + obj_coff_init_stab_section, + 0, /* sec_sym_ok_for_reloc */ + coff_pop_insert, + 0, /* ecoff_set_ext */ + coff_obj_read_begin_hook, + coff_obj_symbol_new_hook, + coff_obj_symbol_clone_hook, + coff_adjust_symtab +}; diff --git a/contrib/toolchain/binutils/gas/config/obj-coff.h b/contrib/toolchain/binutils/gas/config/obj-coff.h new file mode 100644 index 0000000000..ff5548eab4 --- /dev/null +++ b/contrib/toolchain/binutils/gas/config/obj-coff.h @@ -0,0 +1,415 @@ +/* coff object file format + Copyright 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 + Free Software Foundation, Inc. + + This file is part of GAS. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef OBJ_FORMAT_H +#define OBJ_FORMAT_H + +#define OBJ_COFF 1 + +#include "targ-cpu.h" + +/* This internal_lineno crap is to stop namespace pollution from the + bfd internal coff headerfile. */ +#define internal_lineno bfd_internal_lineno +#include "coff/internal.h" +#undef internal_lineno + +/* CPU-specific setup: */ + +#ifdef TC_ARM +#include "coff/arm.h" +#ifndef TARGET_FORMAT +#define TARGET_FORMAT "coff-arm" +#endif +#endif + +#ifdef TC_PPC +#ifdef TE_PE +#include "coff/powerpc.h" +#else +#include "coff/rs6000.h" +#endif +#endif + +#ifdef TC_SPARC +#include "coff/sparc.h" +#endif + +#ifdef TC_I386 +#ifdef TE_PEP +#include "coff/x86_64.h" +#else +#include "coff/i386.h" +#endif + +#ifndef TARGET_FORMAT +#ifdef TE_PEP +#define TARGET_FORMAT "coff-x86-64" +#else +#define TARGET_FORMAT "coff-i386" +#endif +#endif +#endif + +#ifdef TC_M68K +#include "coff/m68k.h" +#ifndef TARGET_FORMAT +#define TARGET_FORMAT "coff-m68k" +#endif +#endif + +#ifdef TC_OR32 +#include "coff/or32.h" +#define TARGET_FORMAT "coff-or32-big" +#endif + +#ifdef TC_I960 +#include "coff/i960.h" +#define TARGET_FORMAT "coff-Intel-little" +#endif + +#ifdef TC_Z80 +#include "coff/z80.h" +#define TARGET_FORMAT "coff-z80" +#endif + +#ifdef TC_Z8K +#include "coff/z8k.h" +#define TARGET_FORMAT "coff-z8k" +#endif + +#ifdef TC_H8300 +#include "coff/h8300.h" +#define TARGET_FORMAT "coff-h8300" +#endif + +#ifdef TC_H8500 +#include "coff/h8500.h" +#define TARGET_FORMAT "coff-h8500" +#endif + +#ifdef TC_SH + +#ifdef TE_PE +#define COFF_WITH_PE +#endif + +#include "coff/sh.h" + +#ifdef TE_PE +#define TARGET_FORMAT "pe-shl" +#else + +#define TARGET_FORMAT \ + (!target_big_endian \ + ? (sh_small ? "coff-shl-small" : "coff-shl") \ + : (sh_small ? "coff-sh-small" : "coff-sh")) + +#endif +#endif + +#ifdef TC_MIPS +#define COFF_WITH_PE +#include "coff/mipspe.h" +#undef TARGET_FORMAT +#define TARGET_FORMAT "pe-mips" +#endif + +#ifdef TC_TIC30 +#include "coff/tic30.h" +#define TARGET_FORMAT "coff-tic30" +#endif + +#ifdef TC_TIC4X +#include "coff/tic4x.h" +#define TARGET_FORMAT "coff2-tic4x" +#endif + +#ifdef TC_TIC54X +#include "coff/tic54x.h" +#define TARGET_FORMAT "coff1-c54x" +#endif + +#ifdef TC_MCORE +#include "coff/mcore.h" +#ifndef TARGET_FORMAT +#define TARGET_FORMAT "pe-mcore" +#endif +#endif + +#ifdef TE_PE +#define obj_set_weak_hook pecoff_obj_set_weak_hook +#define obj_clear_weak_hook pecoff_obj_clear_weak_hook +#endif + +#ifndef OBJ_COFF_MAX_AUXENTRIES +#define OBJ_COFF_MAX_AUXENTRIES 1 +#endif + +#define obj_symbol_new_hook coff_obj_symbol_new_hook +#define obj_symbol_clone_hook coff_obj_symbol_clone_hook +#define obj_read_begin_hook coff_obj_read_begin_hook + +#include "bfd/libcoff.h" + +#define OUTPUT_FLAVOR bfd_target_coff_flavour + +/* Alter the field names, for now, until we've fixed up the other + references to use the new name. */ +#ifdef TC_I960 +#define TC_SYMFIELD_TYPE symbolS * +#define sy_tc bal +#endif + +#define OBJ_SYMFIELD_TYPE unsigned long +#define sy_obj sy_obj_flags + +/* We can't use the predefined section symbols in bfd/section.c, as + COFF symbols have extra fields. See bfd/libcoff.h:coff_symbol_type. */ +#ifndef obj_sec_sym_ok_for_reloc +#define obj_sec_sym_ok_for_reloc(SEC) ((SEC)->owner != 0) +#endif + +#define SYM_AUXENT(S) \ + (&coffsymbol (symbol_get_bfdsym (S))->native[1].u.auxent) +#define SYM_AUXINFO(S) \ + (&coffsymbol (symbol_get_bfdsym (S))->native[1]) + +/* The number of auxiliary entries. */ +#define S_GET_NUMBER_AUXILIARY(s) \ + (coffsymbol (symbol_get_bfdsym (s))->native->u.syment.n_numaux) +/* The number of auxiliary entries. */ +#define S_SET_NUMBER_AUXILIARY(s, v) (S_GET_NUMBER_AUXILIARY (s) = (v)) + +/* True if a symbol name is in the string table, i.e. its length is > 8. */ +#define S_IS_STRING(s) (strlen (S_GET_NAME (s)) > 8 ? 1 : 0) + +/* Auxiliary entry macros. SA_ stands for symbol auxiliary. */ +/* Omit the tv related fields. */ +/* Accessors. */ + +#define SA_GET_SYM_TAGNDX(s) (SYM_AUXENT (s)->x_sym.x_tagndx.l) +#define SA_GET_SYM_LNNO(s) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_lnno) +#define SA_GET_SYM_SIZE(s) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_size) +#define SA_GET_SYM_FSIZE(s) (SYM_AUXENT (s)->x_sym.x_misc.x_fsize) +#define SA_GET_SYM_LNNOPTR(s) (SYM_AUXENT (s)->x_sym.x_fcnary.x_fcn.x_lnnoptr) +#define SA_GET_SYM_ENDNDX(s) (SYM_AUXENT (s)->x_sym.x_fcnary.x_fcn.x_endndx) +#define SA_GET_SYM_DIMEN(s,i) (SYM_AUXENT (s)->x_sym.x_fcnary.x_ary.x_dimen[(i)]) +#define SA_GET_FILE_FNAME(s) (SYM_AUXENT (s)->x_file.x_fname) +#define SA_GET_SCN_SCNLEN(s) (SYM_AUXENT (s)->x_scn.x_scnlen) +#define SA_GET_SCN_NRELOC(s) (SYM_AUXENT (s)->x_scn.x_nreloc) +#define SA_GET_SCN_NLINNO(s) (SYM_AUXENT (s)->x_scn.x_nlinno) + +#define SA_SET_SYM_LNNO(s,v) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_lnno = (v)) +#define SA_SET_SYM_SIZE(s,v) (SYM_AUXENT (s)->x_sym.x_misc.x_lnsz.x_size = (v)) +#define SA_SET_SYM_FSIZE(s,v) (SYM_AUXENT (s)->x_sym.x_misc.x_fsize = (v)) +#define SA_SET_SYM_LNNOPTR(s,v) (SYM_AUXENT (s)->x_sym.x_fcnary.x_fcn.x_lnnoptr = (v)) +#define SA_SET_SYM_DIMEN(s,i,v) (SYM_AUXENT (s)->x_sym.x_fcnary.x_ary.x_dimen[(i)] = (v)) +#define SA_SET_FILE_FNAME(s,v) strncpy (SYM_AUXENT (s)->x_file.x_fname, (v), FILNMLEN) +#define SA_SET_SCN_SCNLEN(s,v) (SYM_AUXENT (s)->x_scn.x_scnlen = (v)) +#define SA_SET_SCN_NRELOC(s,v) (SYM_AUXENT (s)->x_scn.x_nreloc = (v)) +#define SA_SET_SCN_NLINNO(s,v) (SYM_AUXENT (s)->x_scn.x_nlinno = (v)) + +/* Internal use only definitions. SF_ stands for symbol flags. + + These values can be assigned to sy_symbol.ost_flags field of a symbolS. + + You'll break i960 if you shift the SYSPROC bits anywhere else. for + more on the balname/callname hack, see tc-i960.h. b.out is done + differently. */ + +#define SF_I960_MASK 0x000001ff /* Bits 0-8 are used by the i960 port. */ +#define SF_SYSPROC 0x0000003f /* bits 0-5 are used to store the sysproc number. */ +#define SF_IS_SYSPROC 0x00000040 /* bit 6 marks symbols that are sysprocs. */ +#define SF_BALNAME 0x00000080 /* bit 7 marks BALNAME symbols. */ +#define SF_CALLNAME 0x00000100 /* bit 8 marks CALLNAME symbols. */ + +#define SF_NORMAL_MASK 0x0000ffff /* bits 12-15 are general purpose. */ + +#define SF_STATICS 0x00001000 /* Mark the .text & all symbols. */ +#define SF_DEFINED 0x00002000 /* Symbol is defined in this file. */ +#define SF_STRING 0x00004000 /* Symbol name length > 8. */ +#define SF_LOCAL 0x00008000 /* Symbol must not be emitted. */ + +#define SF_DEBUG_MASK 0xffff0000 /* bits 16-31 are debug info. */ + +#define SF_FUNCTION 0x00010000 /* The symbol is a function. */ +#define SF_PROCESS 0x00020000 /* Process symbol before write. */ +#define SF_TAGGED 0x00040000 /* Is associated with a tag. */ +#define SF_TAG 0x00080000 /* Is a tag. */ +#define SF_DEBUG 0x00100000 /* Is in debug or abs section. */ +#define SF_GET_SEGMENT 0x00200000 /* Get the section of the forward symbol. */ +/* All other bits are unused. */ + +/* Accessors. */ +#define SF_GET(s) (* symbol_get_obj (s)) +#define SF_GET_DEBUG(s) (symbol_get_bfdsym (s)->flags & BSF_DEBUGGING) +#define SF_SET_DEBUG(s) (symbol_get_bfdsym (s)->flags |= BSF_DEBUGGING) +#define SF_GET_NORMAL_FIELD(s) (SF_GET (s) & SF_NORMAL_MASK) +#define SF_GET_DEBUG_FIELD(s) (SF_GET (s) & SF_DEBUG_MASK) +#define SF_GET_FILE(s) (SF_GET (s) & SF_FILE) +#define SF_GET_STATICS(s) (SF_GET (s) & SF_STATICS) +#define SF_GET_DEFINED(s) (SF_GET (s) & SF_DEFINED) +#define SF_GET_STRING(s) (SF_GET (s) & SF_STRING) +#define SF_GET_LOCAL(s) (SF_GET (s) & SF_LOCAL) +#define SF_GET_FUNCTION(s) (SF_GET (s) & SF_FUNCTION) +#define SF_GET_PROCESS(s) (SF_GET (s) & SF_PROCESS) +#define SF_GET_TAGGED(s) (SF_GET (s) & SF_TAGGED) +#define SF_GET_TAG(s) (SF_GET (s) & SF_TAG) +#define SF_GET_GET_SEGMENT(s) (SF_GET (s) & SF_GET_SEGMENT) +#define SF_GET_I960(s) (SF_GET (s) & SF_I960_MASK) /* Used by i960. */ +#define SF_GET_BALNAME(s) (SF_GET (s) & SF_BALNAME) /* Used by i960. */ +#define SF_GET_CALLNAME(s) (SF_GET (s) & SF_CALLNAME) /* Used by i960. */ +#define SF_GET_IS_SYSPROC(s) (SF_GET (s) & SF_IS_SYSPROC) /* Used by i960. */ +#define SF_GET_SYSPROC(s) (SF_GET (s) & SF_SYSPROC) /* Used by i960. */ + +/* Modifiers. */ +#define SF_SET(s,v) (SF_GET (s) = (v)) +#define SF_SET_NORMAL_FIELD(s,v)(SF_GET (s) |= ((v) & SF_NORMAL_MASK)) +#define SF_SET_DEBUG_FIELD(s,v) (SF_GET (s) |= ((v) & SF_DEBUG_MASK)) +#define SF_SET_FILE(s) (SF_GET (s) |= SF_FILE) +#define SF_SET_STATICS(s) (SF_GET (s) |= SF_STATICS) +#define SF_SET_DEFINED(s) (SF_GET (s) |= SF_DEFINED) +#define SF_SET_STRING(s) (SF_GET (s) |= SF_STRING) +#define SF_SET_LOCAL(s) (SF_GET (s) |= SF_LOCAL) +#define SF_CLEAR_LOCAL(s) (SF_GET (s) &= ~SF_LOCAL) +#define SF_SET_FUNCTION(s) (SF_GET (s) |= SF_FUNCTION) +#define SF_SET_PROCESS(s) (SF_GET (s) |= SF_PROCESS) +#define SF_SET_TAGGED(s) (SF_GET (s) |= SF_TAGGED) +#define SF_SET_TAG(s) (SF_GET (s) |= SF_TAG) +#define SF_SET_GET_SEGMENT(s) (SF_GET (s) |= SF_GET_SEGMENT) +#define SF_SET_I960(s,v) (SF_GET (s) |= ((v) & SF_I960_MASK)) /* Used by i960. */ +#define SF_SET_BALNAME(s) (SF_GET (s) |= SF_BALNAME) /* Used by i960. */ +#define SF_SET_CALLNAME(s) (SF_GET (s) |= SF_CALLNAME) /* Used by i960. */ +#define SF_SET_IS_SYSPROC(s) (SF_GET (s) |= SF_IS_SYSPROC) /* Used by i960. */ +#define SF_SET_SYSPROC(s,v) (SF_GET (s) |= ((v) & SF_SYSPROC)) /* Used by i960. */ + + +/* Line number handling. */ +extern int text_lineno_number; +extern int coff_line_base; +extern int coff_n_line_nos; +extern symbolS *coff_last_function; + +#define obj_emit_lineno(WHERE, LINE, FILE_START) abort () +#define obj_app_file(name, app) c_dot_file_symbol (name, app) +#define obj_frob_symbol(S,P) coff_frob_symbol (S, & P) +#define obj_frob_section(S) coff_frob_section (S) +#define obj_frob_file_after_relocs() coff_frob_file_after_relocs () +#ifndef obj_adjust_symtab +#define obj_adjust_symtab() coff_adjust_symtab () +#endif + +/* Forward the segment of a forwarded symbol, handle assignments that + just copy symbol values, etc. */ +#ifndef OBJ_COPY_SYMBOL_ATTRIBUTES +#ifndef TE_I386AIX +#define OBJ_COPY_SYMBOL_ATTRIBUTES(dest, src) \ + (SF_GET_GET_SEGMENT (dest) \ + ? (S_SET_SEGMENT (dest, S_GET_SEGMENT (src)), 0) \ + : 0) +#else +#define OBJ_COPY_SYMBOL_ATTRIBUTES(dest, src) \ + (SF_GET_GET_SEGMENT (dest) && S_GET_SEGMENT (dest) == SEG_UNKNOWN \ + ? (S_SET_SEGMENT (dest, S_GET_SEGMENT (src)), 0) \ + : 0) +#endif +#endif + +/* Sanity check. */ + +#ifdef TC_I960 +#ifndef C_LEAFSTAT +hey ! Where is the C_LEAFSTAT definition ? i960 - coff support is depending on it. +#endif /* no C_LEAFSTAT */ +#endif /* TC_I960 */ + +extern const pseudo_typeS coff_pseudo_table[]; + +#ifndef obj_pop_insert +#define obj_pop_insert() pop_insert (coff_pseudo_table) +#endif + +/* In COFF, if a symbol is defined using .def/.val SYM/.endef, it's OK + to redefine the symbol later on. This can happen if C symbols use + a prefix, and a symbol is defined both with and without the prefix, + as in start/_start/__start in gcc/libgcc1-test.c. */ +#define RESOLVE_SYMBOL_REDEFINITION(sym) \ +(SF_GET_GET_SEGMENT (sym) \ + ? (sym->sy_frag = frag_now, \ + S_SET_VALUE (sym, frag_now_fix ()), \ + S_SET_SEGMENT (sym, now_seg), \ + 0) \ + : 0) + +/* Stabs in a coff file go into their own section. */ +#define SEPARATE_STAB_SECTIONS 1 + +/* We need 12 bytes at the start of the section to hold some initial + information. */ +#define INIT_STAB_SECTION(seg) obj_coff_init_stab_section (seg) + +/* Store the number of relocations in the section aux entry. */ +#define SET_SECTION_RELOCS(sec, relocs, n) \ + SA_SET_SCN_NRELOC (section_symbol (sec), n) + +#define obj_app_file(name, app) c_dot_file_symbol (name, app) + +extern int S_SET_DATA_TYPE (symbolS *, int); +extern int S_SET_STORAGE_CLASS (symbolS *, int); +extern int S_GET_STORAGE_CLASS (symbolS *); +extern void SA_SET_SYM_ENDNDX (symbolS *, symbolS *); +extern void coff_add_linesym (symbolS *); +extern void c_dot_file_symbol (const char *, int); +extern void coff_frob_symbol (symbolS *, int *); +extern void coff_adjust_symtab (void); +extern void coff_frob_section (segT); +extern void coff_adjust_section_syms (bfd *, asection *, void *); +extern void coff_frob_file_after_relocs (void); +extern void coff_obj_symbol_new_hook (symbolS *); +extern void coff_obj_symbol_clone_hook (symbolS *, symbolS *); +extern void coff_obj_read_begin_hook (void); +#ifdef TE_PE +extern void pecoff_obj_set_weak_hook (symbolS *); +extern void pecoff_obj_clear_weak_hook (symbolS *); +#endif +extern void obj_coff_section (int); +extern segT obj_coff_add_segment (const char *); +extern void obj_coff_section (int); +extern void c_dot_file_symbol (const char *, int); +extern segT s_get_segment (symbolS *); +#ifndef tc_coff_symbol_emit_hook +extern void tc_coff_symbol_emit_hook (symbolS *); +#endif +extern void obj_coff_pe_handle_link_once (void); +extern void obj_coff_init_stab_section (segT); +extern void c_section_header (struct internal_scnhdr *, + char *, long, long, long, long, + long, long, long, long); +extern void obj_coff_seh_do_final (void); + +#ifndef obj_coff_generate_pdata +#define obj_coff_generate_pdata obj_coff_seh_do_final +#endif + + +#endif /* OBJ_FORMAT_H */ diff --git a/contrib/toolchain/binutils/gas/config/tc-i386-intel.c b/contrib/toolchain/binutils/gas/config/tc-i386-intel.c new file mode 100644 index 0000000000..22594bce91 --- /dev/null +++ b/contrib/toolchain/binutils/gas/config/tc-i386-intel.c @@ -0,0 +1,1005 @@ +/* tc-i386.c -- Assemble Intel syntax code for ix86/x86-64 + Copyright 2009, 2010 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +static struct + { + operatorT op_modifier; /* Operand modifier. */ + int is_mem; /* 1 if operand is memory reference. */ + int is_indirect; /* 1 if operand is indirect reference. */ + int has_offset; /* 1 if operand has offset. */ + unsigned int in_offset; /* >=1 if processing operand of offset. */ + unsigned int in_bracket; /* >=1 if processing operand in brackets. */ + unsigned int in_scale; /* >=1 if processing multipication operand + * in brackets. */ + i386_operand_type reloc_types; /* Value obtained from lex_got(). */ + const reg_entry *base; /* Base register (if any). */ + const reg_entry *index; /* Index register (if any). */ + offsetT scale_factor; /* Accumulated scale factor. */ + symbolS *seg; + } +intel_state; + +/* offset X_add_symbol */ +#define O_offset O_md32 +/* offset X_add_symbol */ +#define O_short O_md31 +/* near ptr X_add_symbol */ +#define O_near_ptr O_md30 +/* far ptr X_add_symbol */ +#define O_far_ptr O_md29 +/* byte ptr X_add_symbol */ +#define O_byte_ptr O_md28 +/* word ptr X_add_symbol */ +#define O_word_ptr O_md27 +/* dword ptr X_add_symbol */ +#define O_dword_ptr O_md26 +/* qword ptr X_add_symbol */ +#define O_qword_ptr O_md25 +/* oword ptr X_add_symbol */ +#define O_oword_ptr O_md24 +/* fword ptr X_add_symbol */ +#define O_fword_ptr O_md23 +/* tbyte ptr X_add_symbol */ +#define O_tbyte_ptr O_md22 +/* xmmword ptr X_add_symbol */ +#define O_xmmword_ptr O_md21 +/* ymmword ptr X_add_symbol */ +#define O_ymmword_ptr O_md20 +/* zmmword ptr X_add_symbol */ +#define O_zmmword_ptr O_md19 + +static struct + { + const char *name; + operatorT op; + unsigned int operands; + } +const i386_operators[] = + { + { "and", O_bit_and, 2 }, + { "eq", O_eq, 2 }, + { "ge", O_ge, 2 }, + { "gt", O_gt, 2 }, + { "le", O_le, 2 }, + { "lt", O_lt, 2 }, + { "mod", O_modulus, 2 }, + { "ne", O_ne, 2 }, + { "not", O_bit_not, 1 }, + { "offset", O_offset, 1 }, + { "or", O_bit_inclusive_or, 2 }, + { "shl", O_left_shift, 2 }, + { "short", O_short, 1 }, + { "shr", O_right_shift, 2 }, + { "xor", O_bit_exclusive_or, 2 }, + { NULL, O_illegal, 0 } + }; + +static struct + { + const char *name; + operatorT op; + unsigned short sz[3]; + } +const i386_types[] = + { +#define I386_TYPE(t, n) { #t, O_##t##_ptr, { n, n, n } } + I386_TYPE(byte, 1), + I386_TYPE(word, 2), + I386_TYPE(dword, 4), + I386_TYPE(fword, 6), + I386_TYPE(qword, 8), + I386_TYPE(tbyte, 10), + I386_TYPE(oword, 16), + I386_TYPE(xmmword, 16), + I386_TYPE(ymmword, 32), + I386_TYPE(zmmword, 64), +#undef I386_TYPE + { "near", O_near_ptr, { 0xff04, 0xff02, 0xff08 } }, + { "far", O_far_ptr, { 0xff06, 0xff05, 0xff06 } }, + { NULL, O_illegal, { 0, 0, 0 } } + }; + +operatorT i386_operator (const char *name, unsigned int operands, char *pc) +{ + unsigned int j; + + if (!intel_syntax) + return O_absent; + + if (!name) + { + if (operands != 2) + return O_illegal; + switch (*input_line_pointer) + { + case ':': + ++input_line_pointer; + return O_full_ptr; + case '[': + ++input_line_pointer; + return O_index; + case '@': + if (this_operand >= 0 && i.reloc[this_operand] == NO_RELOC) + { + int adjust = 0; + char *gotfree_input_line = lex_got (&i.reloc[this_operand], + &adjust, + &intel_state.reloc_types, + (i.bnd_prefix != NULL + || add_bnd_prefix)); + + if (!gotfree_input_line) + break; + free (gotfree_input_line); + *input_line_pointer++ = '+'; + memset (input_line_pointer, '0', adjust - 1); + input_line_pointer[adjust - 1] = ' '; + return O_add; + } + break; + } + return O_illegal; + } + + for (j = 0; i386_operators[j].name; ++j) + if (strcasecmp (i386_operators[j].name, name) == 0) + { + if (i386_operators[j].operands + && i386_operators[j].operands != operands) + return O_illegal; + return i386_operators[j].op; + } + + for (j = 0; i386_types[j].name; ++j) + if (strcasecmp (i386_types[j].name, name) == 0) + break; + if (i386_types[j].name && *pc == ' ') + { + char *pname = ++input_line_pointer; + char c = get_symbol_end (); + + if (strcasecmp (pname, "ptr") == 0) + { + pname[-1] = *pc; + *pc = c; + if (intel_syntax > 0 || operands != 1) + return O_illegal; + return i386_types[j].op; + } + + *input_line_pointer = c; + input_line_pointer = pname - 1; + } + + return O_absent; +} + +static int i386_intel_parse_name (const char *name, expressionS *e) +{ + unsigned int j; + + if (! strcmp (name, "$")) + { + current_location (e); + return 1; + } + + for (j = 0; i386_types[j].name; ++j) + if (strcasecmp(i386_types[j].name, name) == 0) + { + e->X_op = O_constant; + e->X_add_number = i386_types[j].sz[flag_code]; + e->X_add_symbol = NULL; + e->X_op_symbol = NULL; + return 1; + } + + return 0; +} + +static INLINE int i386_intel_check (const reg_entry *rreg, + const reg_entry *base, + const reg_entry *iindex) +{ + if ((this_operand >= 0 + && rreg != i.op[this_operand].regs) + || base != intel_state.base + || iindex != intel_state.index) + { + as_bad (_("invalid use of register")); + return 0; + } + return 1; +} + +static INLINE void i386_intel_fold (expressionS *e, symbolS *sym) +{ + expressionS *exp = symbol_get_value_expression (sym); + if (S_GET_SEGMENT (sym) == absolute_section) + { + offsetT val = e->X_add_number; + + *e = *exp; + e->X_add_number += val; + } + else + { + if (exp->X_op == O_symbol + && strcmp (S_GET_NAME (exp->X_add_symbol), + GLOBAL_OFFSET_TABLE_NAME) == 0) + sym = exp->X_add_symbol; + e->X_add_symbol = sym; + e->X_op_symbol = NULL; + e->X_op = O_symbol; + } +} + +static int +i386_intel_simplify_register (expressionS *e) +{ + int reg_num; + + if (this_operand < 0 || intel_state.in_offset) + { + as_bad (_("invalid use of register")); + return 0; + } + + if (e->X_op == O_register) + reg_num = e->X_add_number; + else + reg_num = e->X_md - 1; + + if (!intel_state.in_bracket) + { + if (i.op[this_operand].regs) + { + as_bad (_("invalid use of register")); + return 0; + } + if (i386_regtab[reg_num].reg_type.bitfield.sreg3 + && i386_regtab[reg_num].reg_num == RegFlat) + { + as_bad (_("invalid use of pseudo-register")); + return 0; + } + i.op[this_operand].regs = i386_regtab + reg_num; + } + else if (!intel_state.index + && (i386_regtab[reg_num].reg_type.bitfield.regxmm + || i386_regtab[reg_num].reg_type.bitfield.regymm + || i386_regtab[reg_num].reg_type.bitfield.regzmm)) + intel_state.index = i386_regtab + reg_num; + else if (!intel_state.base && !intel_state.in_scale) + intel_state.base = i386_regtab + reg_num; + else if (!intel_state.index) + { + if (intel_state.in_scale + || i386_regtab[reg_num].reg_type.bitfield.baseindex) + intel_state.index = i386_regtab + reg_num; + else + { + /* Convert base to index and make ESP/RSP the base. */ + intel_state.index = intel_state.base; + intel_state.base = i386_regtab + reg_num; + } + } + else + { + /* esp is invalid as index */ + intel_state.index = i386_regtab + REGNAM_EAX + ESP_REG_NUM; + } + return 2; +} + +static int i386_intel_simplify (expressionS *); + +static INLINE int i386_intel_simplify_symbol(symbolS *sym) +{ + int ret = i386_intel_simplify (symbol_get_value_expression (sym)); + + if (ret == 2) + { + S_SET_SEGMENT(sym, absolute_section); + ret = 1; + } + return ret; +} + +static int i386_intel_simplify (expressionS *e) +{ + const reg_entry *the_reg = (this_operand >= 0 + ? i.op[this_operand].regs : NULL); + const reg_entry *base = intel_state.base; + const reg_entry *state_index = intel_state.index; + int ret; + + if (!intel_syntax) + return 1; + + switch (e->X_op) + { + case O_index: + if (e->X_add_symbol) + { + if (!i386_intel_simplify_symbol (e->X_add_symbol) + || !i386_intel_check(the_reg, intel_state.base, + intel_state.index)) + return 0; + } + if (!intel_state.in_offset) + ++intel_state.in_bracket; + ret = i386_intel_simplify_symbol (e->X_op_symbol); + if (!intel_state.in_offset) + --intel_state.in_bracket; + if (!ret) + return 0; + if (e->X_add_symbol) + e->X_op = O_add; + else + i386_intel_fold (e, e->X_op_symbol); + break; + + case O_offset: + intel_state.has_offset = 1; + ++intel_state.in_offset; + ret = i386_intel_simplify_symbol (e->X_add_symbol); + --intel_state.in_offset; + if (!ret || !i386_intel_check(the_reg, base, state_index)) + return 0; + i386_intel_fold (e, e->X_add_symbol); + return ret; + + case O_byte_ptr: + case O_word_ptr: + case O_dword_ptr: + case O_fword_ptr: + case O_qword_ptr: + case O_tbyte_ptr: + case O_oword_ptr: + case O_xmmword_ptr: + case O_ymmword_ptr: + case O_zmmword_ptr: + case O_near_ptr: + case O_far_ptr: + if (intel_state.op_modifier == O_absent) + intel_state.op_modifier = e->X_op; + /* FALLTHROUGH */ + case O_short: + if (symbol_get_value_expression (e->X_add_symbol)->X_op + == O_register) + { + as_bad (_("invalid use of register")); + return 0; + } + if (!i386_intel_simplify_symbol (e->X_add_symbol)) + return 0; + i386_intel_fold (e, e->X_add_symbol); + break; + + case O_full_ptr: + if (symbol_get_value_expression (e->X_op_symbol)->X_op + == O_register) + { + as_bad (_("invalid use of register")); + return 0; + } + if (!i386_intel_simplify_symbol (e->X_op_symbol) + || !i386_intel_check(the_reg, intel_state.base, + intel_state.index)) + return 0; + if (!intel_state.in_offset) + intel_state.seg = e->X_add_symbol; + i386_intel_fold (e, e->X_op_symbol); + break; + + case O_multiply: + if (this_operand >= 0 && intel_state.in_bracket) + { + expressionS *scale = NULL; + + if (intel_state.index) + --scale; + + if (!intel_state.in_scale++) + intel_state.scale_factor = 1; + + ret = i386_intel_simplify_symbol (e->X_add_symbol); + if (ret && !scale && intel_state.index) + scale = symbol_get_value_expression (e->X_op_symbol); + + if (ret) + ret = i386_intel_simplify_symbol (e->X_op_symbol); + if (ret && !scale && intel_state.index) + scale = symbol_get_value_expression (e->X_add_symbol); + + if (ret && scale && (scale + 1)) + { + resolve_expression (scale); + if (scale->X_op != O_constant + || intel_state.index->reg_type.bitfield.reg16) + scale->X_add_number = 0; + intel_state.scale_factor *= scale->X_add_number; + } + + --intel_state.in_scale; + if (!ret) + return 0; + + if (!intel_state.in_scale) + switch (intel_state.scale_factor) + { + case 1: + i.log2_scale_factor = 0; + break; + case 2: + i.log2_scale_factor = 1; + break; + case 4: + i.log2_scale_factor = 2; + break; + case 8: + i.log2_scale_factor = 3; + break; + default: + /* esp is invalid as index */ + intel_state.index = i386_regtab + REGNAM_EAX + ESP_REG_NUM; + break; + } + + break; + } + goto fallthrough; + + case O_register: + ret = i386_intel_simplify_register (e); + if (ret == 2) + { + gas_assert (e->X_add_number < (unsigned short) -1); + e->X_md = (unsigned short) e->X_add_number + 1; + e->X_op = O_constant; + e->X_add_number = 0; + } + return ret; + + case O_constant: + if (e->X_md) + return i386_intel_simplify_register (e); + + /* FALLTHROUGH */ + default: +fallthrough: + if (e->X_add_symbol + && !i386_intel_simplify_symbol (e->X_add_symbol)) + return 0; + if (e->X_op == O_add || e->X_op == O_subtract) + { + base = intel_state.base; + state_index = intel_state.index; + } + if (!i386_intel_check (the_reg, base, state_index) + || (e->X_op_symbol + && !i386_intel_simplify_symbol (e->X_op_symbol)) + || !i386_intel_check (the_reg, + (e->X_op != O_add + ? base : intel_state.base), + (e->X_op != O_add + ? state_index : intel_state.index))) + return 0; + break; + } + + if (this_operand >= 0 + && e->X_op == O_symbol + && !intel_state.in_offset) + { + segT seg = S_GET_SEGMENT (e->X_add_symbol); + + if (seg != absolute_section + && seg != reg_section + && seg != expr_section) + intel_state.is_mem |= 2 - !intel_state.in_bracket; + } + + return 1; +} + +int i386_need_index_operator (void) +{ + return intel_syntax < 0; +} + +static int +i386_intel_operand (char *operand_string, int got_a_float) +{ + char *saved_input_line_pointer, *buf; + segT exp_seg; + expressionS exp, *expP; + char suffix = 0; + int ret; + + /* Handle vector immediates. */ + if (RC_SAE_immediate (operand_string)) + return 1; + + /* Initialize state structure. */ + intel_state.op_modifier = O_absent; + intel_state.is_mem = 0; + intel_state.is_indirect = 0; + intel_state.has_offset = 0; + intel_state.base = NULL; + intel_state.index = NULL; + intel_state.seg = NULL; + operand_type_set (&intel_state.reloc_types, ~0); + gas_assert (!intel_state.in_offset); + gas_assert (!intel_state.in_bracket); + gas_assert (!intel_state.in_scale); + + saved_input_line_pointer = input_line_pointer; + input_line_pointer = buf = xstrdup (operand_string); + + intel_syntax = -1; + memset (&exp, 0, sizeof(exp)); + exp_seg = expression (&exp); + ret = i386_intel_simplify (&exp); + intel_syntax = 1; + + SKIP_WHITESPACE (); + + /* Handle vector operations. */ + if (*input_line_pointer == '{') + { + char *end = check_VecOperations (input_line_pointer, NULL); + if (end) + input_line_pointer = end; + else + ret = 0; + } + + if (!is_end_of_line[(unsigned char) *input_line_pointer]) + { + as_bad (_("junk `%s' after expression"), input_line_pointer); + ret = 0; + } + else if (exp.X_op == O_illegal || exp.X_op == O_absent) + { + as_bad (_("invalid expression")); + ret = 0; + } + else if (!intel_state.has_offset + && input_line_pointer > buf + && *(input_line_pointer - 1) == ']') + { + intel_state.is_mem |= 1; + intel_state.is_indirect = 1; + } + + input_line_pointer = saved_input_line_pointer; + free (buf); + + gas_assert (!intel_state.in_offset); + gas_assert (!intel_state.in_bracket); + gas_assert (!intel_state.in_scale); + + if (!ret) + return 0; + + if (intel_state.op_modifier != O_absent + && current_templates->start->base_opcode != 0x8d /* lea */) + { + i.types[this_operand].bitfield.unspecified = 0; + + switch (intel_state.op_modifier) + { + case O_byte_ptr: + i.types[this_operand].bitfield.byte = 1; + suffix = BYTE_MNEM_SUFFIX; + break; + + case O_word_ptr: + i.types[this_operand].bitfield.word = 1; + if ((current_templates->start->name[0] == 'l' + && current_templates->start->name[2] == 's' + && current_templates->start->name[3] == 0) + || current_templates->start->base_opcode == 0x62 /* bound */) + suffix = BYTE_MNEM_SUFFIX; /* so it will cause an error */ + else if (got_a_float == 2) /* "fi..." */ + suffix = SHORT_MNEM_SUFFIX; + else + suffix = WORD_MNEM_SUFFIX; + break; + + case O_dword_ptr: + i.types[this_operand].bitfield.dword = 1; + if ((current_templates->start->name[0] == 'l' + && current_templates->start->name[2] == 's' + && current_templates->start->name[3] == 0) + || current_templates->start->base_opcode == 0x62 /* bound */) + suffix = WORD_MNEM_SUFFIX; + else if (flag_code == CODE_16BIT + && (current_templates->start->opcode_modifier.jump + || current_templates->start->opcode_modifier.jumpdword)) + suffix = LONG_DOUBLE_MNEM_SUFFIX; + else if (got_a_float == 1) /* "f..." */ + suffix = SHORT_MNEM_SUFFIX; + else + suffix = LONG_MNEM_SUFFIX; + break; + + case O_fword_ptr: + i.types[this_operand].bitfield.fword = 1; + if (current_templates->start->name[0] == 'l' + && current_templates->start->name[2] == 's' + && current_templates->start->name[3] == 0) + suffix = LONG_MNEM_SUFFIX; + else if (!got_a_float) + { + if (flag_code == CODE_16BIT) + add_prefix (DATA_PREFIX_OPCODE); + suffix = LONG_DOUBLE_MNEM_SUFFIX; + } + else + suffix = BYTE_MNEM_SUFFIX; /* so it will cause an error */ + break; + + case O_qword_ptr: + i.types[this_operand].bitfield.qword = 1; + if (current_templates->start->base_opcode == 0x62 /* bound */ + || got_a_float == 1) /* "f..." */ + suffix = LONG_MNEM_SUFFIX; + else + suffix = QWORD_MNEM_SUFFIX; + break; + + case O_tbyte_ptr: + i.types[this_operand].bitfield.tbyte = 1; + if (got_a_float == 1) + suffix = LONG_DOUBLE_MNEM_SUFFIX; + else + suffix = BYTE_MNEM_SUFFIX; /* so it will cause an error */ + break; + + case O_oword_ptr: + case O_xmmword_ptr: + i.types[this_operand].bitfield.xmmword = 1; + suffix = XMMWORD_MNEM_SUFFIX; + break; + + case O_ymmword_ptr: + i.types[this_operand].bitfield.ymmword = 1; + suffix = YMMWORD_MNEM_SUFFIX; + break; + + case O_zmmword_ptr: + i.types[this_operand].bitfield.zmmword = 1; + suffix = ZMMWORD_MNEM_SUFFIX; + break; + + case O_far_ptr: + suffix = LONG_DOUBLE_MNEM_SUFFIX; + /* FALLTHROUGH */ + case O_near_ptr: + if (!current_templates->start->opcode_modifier.jump + && !current_templates->start->opcode_modifier.jumpdword) + suffix = got_a_float /* so it will cause an error */ + ? BYTE_MNEM_SUFFIX + : LONG_DOUBLE_MNEM_SUFFIX; + break; + + default: + BAD_CASE (intel_state.op_modifier); + break; + } + + if (!i.suffix) + i.suffix = suffix; + else if (i.suffix != suffix) + { + as_bad (_("conflicting operand size modifiers")); + return 0; + } + } + + /* Operands for jump/call need special consideration. */ + if (current_templates->start->opcode_modifier.jump + || current_templates->start->opcode_modifier.jumpdword + || current_templates->start->opcode_modifier.jumpintersegment) + { + if (i.op[this_operand].regs + || intel_state.base + || intel_state.index + || intel_state.is_mem > 1) + i.types[this_operand].bitfield.jumpabsolute = 1; + else + switch (intel_state.op_modifier) + { + case O_near_ptr: + if (intel_state.seg) + i.types[this_operand].bitfield.jumpabsolute = 1; + else + intel_state.is_mem = 1; + break; + case O_far_ptr: + case O_absent: + if (!intel_state.seg) + { + intel_state.is_mem = 1; + if (intel_state.op_modifier == O_absent) + { + if (intel_state.is_indirect == 1) + i.types[this_operand].bitfield.jumpabsolute = 1; + break; + } + as_bad (_("cannot infer the segment part of the operand")); + return 0; + } + else if (S_GET_SEGMENT (intel_state.seg) == reg_section) + i.types[this_operand].bitfield.jumpabsolute = 1; + else + { + i386_operand_type types; + + if (i.imm_operands >= MAX_IMMEDIATE_OPERANDS) + { + as_bad (_("at most %d immediate operands are allowed"), + MAX_IMMEDIATE_OPERANDS); + return 0; + } + expP = &im_expressions[i.imm_operands++]; + memset (expP, 0, sizeof(*expP)); + expP->X_op = O_symbol; + expP->X_add_symbol = intel_state.seg; + i.op[this_operand].imms = expP; + + resolve_expression (expP); + operand_type_set (&types, ~0); + if (!i386_finalize_immediate (S_GET_SEGMENT (intel_state.seg), + expP, types, operand_string)) + return 0; + if (i.operands < MAX_OPERANDS) + { + this_operand = i.operands++; + i.types[this_operand].bitfield.unspecified = 1; + } + if (suffix == LONG_DOUBLE_MNEM_SUFFIX) + i.suffix = 0; + intel_state.seg = NULL; + intel_state.is_mem = 0; + } + break; + default: + i.types[this_operand].bitfield.jumpabsolute = 1; + break; + } + if (i.types[this_operand].bitfield.jumpabsolute) + intel_state.is_mem |= 1; + } + else if (intel_state.seg) + intel_state.is_mem |= 1; + + if (i.op[this_operand].regs) + { + i386_operand_type temp; + + /* Register operand. */ + if (intel_state.base || intel_state.index || intel_state.seg) + { + as_bad (_("invalid operand")); + return 0; + } + + temp = i.op[this_operand].regs->reg_type; + temp.bitfield.baseindex = 0; + i.types[this_operand] = operand_type_or (i.types[this_operand], + temp); + i.types[this_operand].bitfield.unspecified = 0; + ++i.reg_operands; + } + else if (intel_state.base + || intel_state.index + || intel_state.seg + || intel_state.is_mem) + { + /* Memory operand. */ + if ((int) i.mem_operands + >= 2 - !current_templates->start->opcode_modifier.isstring) + { + /* Handle + + call 0x9090,0x90909090 + lcall 0x9090,0x90909090 + jmp 0x9090,0x90909090 + ljmp 0x9090,0x90909090 + */ + + if ((current_templates->start->opcode_modifier.jumpintersegment + || current_templates->start->opcode_modifier.jumpdword + || current_templates->start->opcode_modifier.jump) + && this_operand == 1 + && intel_state.seg == NULL + && i.mem_operands == 1 + && i.disp_operands == 1 + && intel_state.op_modifier == O_absent) + { + /* Try to process the first operand as immediate, */ + this_operand = 0; + if (i386_finalize_immediate (exp_seg, i.op[0].imms, + intel_state.reloc_types, + NULL)) + { + this_operand = 1; + expP = &im_expressions[0]; + i.op[this_operand].imms = expP; + *expP = exp; + + /* Try to process the second operand as immediate, */ + if (i386_finalize_immediate (exp_seg, expP, + intel_state.reloc_types, + NULL)) + { + i.mem_operands = 0; + i.disp_operands = 0; + i.imm_operands = 2; + i.types[0].bitfield.mem = 0; + i.types[0].bitfield.disp16 = 0; + i.types[0].bitfield.disp32 = 0; + i.types[0].bitfield.disp32s = 0; + return 1; + } + } + } + + as_bad (_("too many memory references for `%s'"), + current_templates->start->name); + return 0; + } + + expP = &disp_expressions[i.disp_operands]; + memcpy (expP, &exp, sizeof(exp)); + resolve_expression (expP); + + if (expP->X_op != O_constant + || expP->X_add_number + || (!intel_state.base + && !intel_state.index)) + { + i.op[this_operand].disps = expP; + i.disp_operands++; + + if (flag_code == CODE_64BIT) + { + i.types[this_operand].bitfield.disp32 = 1; + if (!i.prefix[ADDR_PREFIX]) + { + i.types[this_operand].bitfield.disp64 = 1; + i.types[this_operand].bitfield.disp32s = 1; + } + } + else if (!i.prefix[ADDR_PREFIX] ^ (flag_code == CODE_16BIT)) + i.types[this_operand].bitfield.disp32 = 1; + else + i.types[this_operand].bitfield.disp16 = 1; + +#if defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT) + /* + * exp_seg is used only for verification in + * i386_finalize_displacement, and we can end up seeing reg_section + * here - but we know we removed all registers from the expression + * (or error-ed on any remaining ones) in i386_intel_simplify. I + * consider the check in i386_finalize_displacement bogus anyway, in + * particular because it doesn't allow for expr_section, so I'd + * rather see that check (and the similar one in + * i386_finalize_immediate) use SEG_NORMAL(), but not being an a.out + * expert I can't really say whether that would have other bad side + * effects. + */ + if (OUTPUT_FLAVOR == bfd_target_aout_flavour + && exp_seg == reg_section) + exp_seg = expP->X_op != O_constant ? undefined_section + : absolute_section; +#endif + + if (!i386_finalize_displacement (exp_seg, expP, + intel_state.reloc_types, + operand_string)) + return 0; + } + + if (intel_state.base || intel_state.index) + i.types[this_operand].bitfield.baseindex = 1; + + if (intel_state.seg) + { + for (;;) + { + expP = symbol_get_value_expression (intel_state.seg); + if (expP->X_op != O_full_ptr) + break; + intel_state.seg = expP->X_add_symbol; + } + if (expP->X_op != O_register) + { + as_bad (_("segment register name expected")); + return 0; + } + if (!i386_regtab[expP->X_add_number].reg_type.bitfield.sreg2 + && !i386_regtab[expP->X_add_number].reg_type.bitfield.sreg3) + { + as_bad (_("invalid use of register")); + return 0; + } + switch (i386_regtab[expP->X_add_number].reg_num) + { + case 0: i.seg[i.mem_operands] = &es; break; + case 1: i.seg[i.mem_operands] = &cs; break; + case 2: i.seg[i.mem_operands] = &ss; break; + case 3: i.seg[i.mem_operands] = &ds; break; + case 4: i.seg[i.mem_operands] = &fs; break; + case 5: i.seg[i.mem_operands] = &gs; break; + case RegFlat: i.seg[i.mem_operands] = NULL; break; + } + } + + /* Swap base and index in 16-bit memory operands like + [si+bx]. Since i386_index_check is also used in AT&T + mode we have to do that here. */ + if (intel_state.base + && intel_state.index + && intel_state.base->reg_type.bitfield.reg16 + && intel_state.index->reg_type.bitfield.reg16 + && intel_state.base->reg_num >= 6 + && intel_state.index->reg_num < 6) + { + i.base_reg = intel_state.index; + i.index_reg = intel_state.base; + } + else + { + i.base_reg = intel_state.base; + i.index_reg = intel_state.index; + } + + if (!i386_index_check (operand_string)) + return 0; + + i.types[this_operand].bitfield.mem = 1; + ++i.mem_operands; + } + else + { + /* Immediate. */ + if (i.imm_operands >= MAX_IMMEDIATE_OPERANDS) + { + as_bad (_("at most %d immediate operands are allowed"), + MAX_IMMEDIATE_OPERANDS); + return 0; + } + + expP = &im_expressions[i.imm_operands++]; + i.op[this_operand].imms = expP; + *expP = exp; + + return i386_finalize_immediate (exp_seg, expP, intel_state.reloc_types, + operand_string); + } + + return 1; +} diff --git a/contrib/toolchain/binutils/gas/config/tc-i386.c b/contrib/toolchain/binutils/gas/config/tc-i386.c new file mode 100644 index 0000000000..3c423da7a8 --- /dev/null +++ b/contrib/toolchain/binutils/gas/config/tc-i386.c @@ -0,0 +1,10565 @@ +/* tc-i386.c -- Assemble code for the Intel 80386 + Copyright 1989, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, + 2012 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* Intel 80386 machine specific gas. + Written by Eliot Dresselhaus (eliot@mgm.mit.edu). + x86_64 support by Jan Hubicka (jh@suse.cz) + VIA PadLock support by Michal Ludvig (mludvig@suse.cz) + Bugs & suggestions are completely welcome. This is free software. + Please help us make it better. */ + +#include "as.h" +#include "safe-ctype.h" +#include "subsegs.h" +#include "dwarf2dbg.h" +#include "dw2gencfi.h" +#include "elf/x86-64.h" +#include "opcodes/i386-init.h" + +#ifndef REGISTER_WARNINGS +#define REGISTER_WARNINGS 1 +#endif + +#ifndef INFER_ADDR_PREFIX +#define INFER_ADDR_PREFIX 1 +#endif + +#ifndef DEFAULT_ARCH +#define DEFAULT_ARCH "i386" +#endif + +#ifndef INLINE +#if __GNUC__ >= 2 +#define INLINE __inline__ +#else +#define INLINE +#endif +#endif + +/* Prefixes will be emitted in the order defined below. + WAIT_PREFIX must be the first prefix since FWAIT is really is an + instruction, and so must come before any prefixes. + The preferred prefix order is SEG_PREFIX, ADDR_PREFIX, DATA_PREFIX, + REP_PREFIX/HLE_PREFIX, LOCK_PREFIX. */ +#define WAIT_PREFIX 0 +#define SEG_PREFIX 1 +#define ADDR_PREFIX 2 +#define DATA_PREFIX 3 +#define REP_PREFIX 4 +#define HLE_PREFIX REP_PREFIX +#define BND_PREFIX REP_PREFIX +#define LOCK_PREFIX 5 +#define REX_PREFIX 6 /* must come last. */ +#define MAX_PREFIXES 7 /* max prefixes per opcode */ + +/* we define the syntax here (modulo base,index,scale syntax) */ +#define REGISTER_PREFIX '%' +#define IMMEDIATE_PREFIX '$' +#define ABSOLUTE_PREFIX '*' + +/* these are the instruction mnemonic suffixes in AT&T syntax or + memory operand size in Intel syntax. */ +#define WORD_MNEM_SUFFIX 'w' +#define BYTE_MNEM_SUFFIX 'b' +#define SHORT_MNEM_SUFFIX 's' +#define LONG_MNEM_SUFFIX 'l' +#define QWORD_MNEM_SUFFIX 'q' +#define XMMWORD_MNEM_SUFFIX 'x' +#define YMMWORD_MNEM_SUFFIX 'y' +#define ZMMWORD_MNEM_SUFFIX 'z' +/* Intel Syntax. Use a non-ascii letter since since it never appears + in instructions. */ +#define LONG_DOUBLE_MNEM_SUFFIX '\1' + +#define END_OF_INSN '\0' + +/* + 'templates' is for grouping together 'template' structures for opcodes + of the same name. This is only used for storing the insns in the grand + ole hash table of insns. + The templates themselves start at START and range up to (but not including) + END. + */ +typedef struct +{ + const insn_template *start; + const insn_template *end; +} +templates; + +/* 386 operand encoding bytes: see 386 book for details of this. */ +typedef struct +{ + unsigned int regmem; /* codes register or memory operand */ + unsigned int reg; /* codes register operand (or extended opcode) */ + unsigned int mode; /* how to interpret regmem & reg */ +} +modrm_byte; + +/* x86-64 extension prefix. */ +typedef int rex_byte; + +/* 386 opcode byte to code indirect addressing. */ +typedef struct +{ + unsigned base; + unsigned index; + unsigned scale; +} +sib_byte; + +/* x86 arch names, types and features */ +typedef struct +{ + const char *name; /* arch name */ + unsigned int len; /* arch string length */ + enum processor_type type; /* arch type */ + i386_cpu_flags flags; /* cpu feature flags */ + unsigned int skip; /* show_arch should skip this. */ + unsigned int negated; /* turn off indicated flags. */ +} +arch_entry; + +static void update_code_flag (int, int); +static void set_code_flag (int); +static void set_16bit_gcc_code_flag (int); +static void set_intel_syntax (int); +static void set_intel_mnemonic (int); +static void set_allow_index_reg (int); +static void set_check (int); +static void set_cpu_arch (int); +#ifdef TE_PE +static void pe_directive_secrel (int); +#endif +static void signed_cons (int); +static char *output_invalid (int c); +static int i386_finalize_immediate (segT, expressionS *, i386_operand_type, + const char *); +static int i386_finalize_displacement (segT, expressionS *, i386_operand_type, + const char *); +static int i386_att_operand (char *); +static int i386_intel_operand (char *, int); +static int i386_intel_simplify (expressionS *); +static int i386_intel_parse_name (const char *, expressionS *); +static const reg_entry *parse_register (char *, char **); +static char *parse_insn (char *, char *); +static char *parse_operands (char *, const char *); +static void swap_operands (void); +static void swap_2_operands (int, int); +static void optimize_imm (void); +static void optimize_disp (void); +static const insn_template *match_template (void); +static int check_string (void); +static int process_suffix (void); +static int check_byte_reg (void); +static int check_long_reg (void); +static int check_qword_reg (void); +static int check_word_reg (void); +static int finalize_imm (void); +static int process_operands (void); +static const seg_entry *build_modrm_byte (void); +static void output_insn (void); +static void output_imm (fragS *, offsetT); +static void output_disp (fragS *, offsetT); +#ifndef I386COFF +static void s_bss (int); +#endif +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) +static void handle_large_common (int small ATTRIBUTE_UNUSED); +#endif + +static const char *default_arch = DEFAULT_ARCH; + +/* This struct describes rounding control and SAE in the instruction. */ +struct RC_Operation +{ + enum rc_type + { + rne = 0, + rd, + ru, + rz, + saeonly + } type; + int operand; +}; + +static struct RC_Operation rc_op; + +/* The struct describes masking, applied to OPERAND in the instruction. + MASK is a pointer to the corresponding mask register. ZEROING tells + whether merging or zeroing mask is used. */ +struct Mask_Operation +{ + const reg_entry *mask; + unsigned int zeroing; + /* The operand where this operation is associated. */ + int operand; +}; + +static struct Mask_Operation mask_op; + +/* The struct describes broadcasting, applied to OPERAND. FACTOR is + broadcast factor. */ +struct Broadcast_Operation +{ + /* Type of broadcast: no broadcast, {1to8}, or {1to16}. */ + int type; + + /* Index of broadcasted operand. */ + int operand; +}; + +static struct Broadcast_Operation broadcast_op; + +/* VEX prefix. */ +typedef struct +{ + /* VEX prefix is either 2 byte or 3 byte. EVEX is 4 byte. */ + unsigned char bytes[4]; + unsigned int length; + /* Destination or source register specifier. */ + const reg_entry *register_specifier; +} vex_prefix; + +/* 'md_assemble ()' gathers together information and puts it into a + i386_insn. */ + +union i386_op + { + expressionS *disps; + expressionS *imms; + const reg_entry *regs; + }; + +enum i386_error + { + operand_size_mismatch, + operand_type_mismatch, + register_type_mismatch, + number_of_operands_mismatch, + invalid_instruction_suffix, + bad_imm4, + old_gcc_only, + unsupported_with_intel_mnemonic, + unsupported_syntax, + unsupported, + invalid_vsib_address, + invalid_vector_register_set, + unsupported_vector_index_register, + unsupported_broadcast, + broadcast_not_on_src_operand, + broadcast_needed, + unsupported_masking, + mask_not_on_destination, + no_default_mask, + unsupported_rc_sae, + rc_sae_operand_not_last_imm, + invalid_register_operand, + try_vector_disp8 + }; + +struct _i386_insn + { + /* TM holds the template for the insn were currently assembling. */ + insn_template tm; + + /* SUFFIX holds the instruction size suffix for byte, word, dword + or qword, if given. */ + char suffix; + + /* OPERANDS gives the number of given operands. */ + unsigned int operands; + + /* REG_OPERANDS, DISP_OPERANDS, MEM_OPERANDS, IMM_OPERANDS give the number + of given register, displacement, memory operands and immediate + operands. */ + unsigned int reg_operands, disp_operands, mem_operands, imm_operands; + + /* TYPES [i] is the type (see above #defines) which tells us how to + use OP[i] for the corresponding operand. */ + i386_operand_type types[MAX_OPERANDS]; + + /* Displacement expression, immediate expression, or register for each + operand. */ + union i386_op op[MAX_OPERANDS]; + + /* Flags for operands. */ + unsigned int flags[MAX_OPERANDS]; +#define Operand_PCrel 1 + + /* Relocation type for operand */ + enum bfd_reloc_code_real reloc[MAX_OPERANDS]; + + /* BASE_REG, INDEX_REG, and LOG2_SCALE_FACTOR are used to encode + the base index byte below. */ + const reg_entry *base_reg; + const reg_entry *index_reg; + unsigned int log2_scale_factor; + + /* SEG gives the seg_entries of this insn. They are zero unless + explicit segment overrides are given. */ + const seg_entry *seg[2]; + + /* PREFIX holds all the given prefix opcodes (usually null). + PREFIXES is the number of prefix opcodes. */ + unsigned int prefixes; + unsigned char prefix[MAX_PREFIXES]; + + /* RM and SIB are the modrm byte and the sib byte where the + addressing modes of this insn are encoded. */ + modrm_byte rm; + rex_byte rex; + rex_byte vrex; + sib_byte sib; + vex_prefix vex; + + /* Masking attributes. */ + struct Mask_Operation *mask; + + /* Rounding control and SAE attributes. */ + struct RC_Operation *rounding; + + /* Broadcasting attributes. */ + struct Broadcast_Operation *broadcast; + + /* Compressed disp8*N attribute. */ + unsigned int memshift; + + /* Swap operand in encoding. */ + unsigned int swap_operand; + + /* Prefer 8bit or 32bit displacement in encoding. */ + enum + { + disp_encoding_default = 0, + disp_encoding_8bit, + disp_encoding_32bit + } disp_encoding; + + /* REP prefix. */ + const char *rep_prefix; + + /* HLE prefix. */ + const char *hle_prefix; + + /* Have BND prefix. */ + const char *bnd_prefix; + + /* Need VREX to support upper 16 registers. */ + int need_vrex; + + /* Error message. */ + enum i386_error error; + }; + +typedef struct _i386_insn i386_insn; + +/* Link RC type with corresponding string, that'll be looked for in + asm. */ +struct RC_name +{ + enum rc_type type; + const char *name; + unsigned int len; +}; + +static const struct RC_name RC_NamesTable[] = +{ + { rne, STRING_COMMA_LEN ("rn-sae") }, + { rd, STRING_COMMA_LEN ("rd-sae") }, + { ru, STRING_COMMA_LEN ("ru-sae") }, + { rz, STRING_COMMA_LEN ("rz-sae") }, + { saeonly, STRING_COMMA_LEN ("sae") }, +}; + +/* List of chars besides those in app.c:symbol_chars that can start an + operand. Used to prevent the scrubber eating vital white-space. */ +const char extra_symbol_chars[] = "*%-([{" +#ifdef LEX_AT + "@" +#endif +#ifdef LEX_QM + "?" +#endif + ; + +#if (defined (TE_I386AIX) \ + || ((defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) \ + && !defined (TE_GNU) \ + && !defined (TE_LINUX) \ + && !defined (TE_NACL) \ + && !defined (TE_NETWARE) \ + && !defined (TE_FreeBSD) \ + && !defined (TE_DragonFly) \ + && !defined (TE_NetBSD))) +/* This array holds the chars that always start a comment. If the + pre-processor is disabled, these aren't very useful. The option + --divide will remove '/' from this list. */ +const char *i386_comment_chars = "#/"; +#define SVR4_COMMENT_CHARS 1 +#define PREFIX_SEPARATOR '\\' + +#else +const char *i386_comment_chars = "#"; +#define PREFIX_SEPARATOR '/' +#endif + +/* This array holds the chars that only start a comment at the beginning of + a line. If the line seems to have the form '# 123 filename' + .line and .file directives will appear in the pre-processed output. + Note that input_file.c hand checks for '#' at the beginning of the + first line of the input file. This is because the compiler outputs + #NO_APP at the beginning of its output. + Also note that comments started like this one will always work if + '/' isn't otherwise defined. */ +const char line_comment_chars[] = "#/"; + +const char line_separator_chars[] = ";"; + +/* Chars that can be used to separate mant from exp in floating point + nums. */ +const char EXP_CHARS[] = "eE"; + +/* Chars that mean this number is a floating point constant + As in 0f12.456 + or 0d1.2345e12. */ +const char FLT_CHARS[] = "fFdDxX"; + +/* Tables for lexical analysis. */ +static char mnemonic_chars[256]; +static char register_chars[256]; +static char operand_chars[256]; +static char identifier_chars[256]; +static char digit_chars[256]; + +/* Lexical macros. */ +#define is_mnemonic_char(x) (mnemonic_chars[(unsigned char) x]) +#define is_operand_char(x) (operand_chars[(unsigned char) x]) +#define is_register_char(x) (register_chars[(unsigned char) x]) +#define is_space_char(x) ((x) == ' ') +#define is_identifier_char(x) (identifier_chars[(unsigned char) x]) +#define is_digit_char(x) (digit_chars[(unsigned char) x]) + +/* All non-digit non-letter characters that may occur in an operand. */ +static char operand_special_chars[] = "%$-+(,)*._~/<>|&^!:[@]"; + +/* md_assemble() always leaves the strings it's passed unaltered. To + effect this we maintain a stack of saved characters that we've smashed + with '\0's (indicating end of strings for various sub-fields of the + assembler instruction). */ +static char save_stack[32]; +static char *save_stack_p; +#define END_STRING_AND_SAVE(s) \ + do { *save_stack_p++ = *(s); *(s) = '\0'; } while (0) +#define RESTORE_END_STRING(s) \ + do { *(s) = *--save_stack_p; } while (0) + +/* The instruction we're assembling. */ +static i386_insn i; + +/* Possible templates for current insn. */ +static const templates *current_templates; + +/* Per instruction expressionS buffers: max displacements & immediates. */ +static expressionS disp_expressions[MAX_MEMORY_OPERANDS]; +static expressionS im_expressions[MAX_IMMEDIATE_OPERANDS]; + +/* Current operand we are working on. */ +static int this_operand = -1; + +/* We support four different modes. FLAG_CODE variable is used to distinguish + these. */ + +enum flag_code { + CODE_32BIT, + CODE_16BIT, + CODE_64BIT }; + +static enum flag_code flag_code; +static unsigned int object_64bit; +static unsigned int disallow_64bit_reloc; +static int use_rela_relocations = 0; + +#if ((defined (OBJ_MAYBE_COFF) && defined (OBJ_MAYBE_AOUT)) \ + || defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \ + || defined (TE_PE) || defined (TE_PEP) || defined (OBJ_MACH_O)) + +/* The ELF ABI to use. */ +enum x86_elf_abi +{ + I386_ABI, + X86_64_ABI, + X86_64_X32_ABI +}; + +static enum x86_elf_abi x86_elf_abi = I386_ABI; +#endif + +/* 1 for intel syntax, + 0 if att syntax. */ +static int intel_syntax = 0; + +/* 1 for intel mnemonic, + 0 if att mnemonic. */ +static int intel_mnemonic = !SYSV386_COMPAT; + +/* 1 if support old (<= 2.8.1) versions of gcc. */ +static int old_gcc = OLDGCC_COMPAT; + +/* 1 if pseudo registers are permitted. */ +static int allow_pseudo_reg = 0; + +/* 1 if register prefix % not required. */ +static int allow_naked_reg = 0; + +/* 1 if the assembler should add BND prefix for all control-tranferring + instructions supporting it, even if this prefix wasn't specified + explicitly. */ +static int add_bnd_prefix = 0; + +/* 1 if pseudo index register, eiz/riz, is allowed . */ +static int allow_index_reg = 0; + +static enum check_kind + { + check_none = 0, + check_warning, + check_error + } +sse_check, operand_check = check_warning; + +/* Register prefix used for error message. */ +static const char *register_prefix = "%"; + +/* Used in 16 bit gcc mode to add an l suffix to call, ret, enter, + leave, push, and pop instructions so that gcc has the same stack + frame as in 32 bit mode. */ +static char stackop_size = '\0'; + +/* Non-zero to optimize code alignment. */ +int optimize_align_code = 1; + +/* Non-zero to quieten some warnings. */ +static int quiet_warnings = 0; + +/* CPU name. */ +static const char *cpu_arch_name = NULL; +static char *cpu_sub_arch_name = NULL; + +/* CPU feature flags. */ +static i386_cpu_flags cpu_arch_flags = CPU_UNKNOWN_FLAGS; + +/* If we have selected a cpu we are generating instructions for. */ +static int cpu_arch_tune_set = 0; + +/* Cpu we are generating instructions for. */ +enum processor_type cpu_arch_tune = PROCESSOR_UNKNOWN; + +/* CPU feature flags of cpu we are generating instructions for. */ +static i386_cpu_flags cpu_arch_tune_flags; + +/* CPU instruction set architecture used. */ +enum processor_type cpu_arch_isa = PROCESSOR_UNKNOWN; + +/* CPU feature flags of instruction set architecture used. */ +i386_cpu_flags cpu_arch_isa_flags; + +/* If set, conditional jumps are not automatically promoted to handle + larger than a byte offset. */ +static unsigned int no_cond_jump_promotion = 0; + +/* Encode SSE instructions with VEX prefix. */ +static unsigned int sse2avx; + +/* Encode scalar AVX instructions with specific vector length. */ +static enum + { + vex128 = 0, + vex256 + } avxscalar; + +/* Encode scalar EVEX LIG instructions with specific vector length. */ +static enum + { + evexl128 = 0, + evexl256, + evexl512 + } evexlig; + +/* Encode EVEX WIG instructions with specific evex.w. */ +static enum + { + evexw0 = 0, + evexw1 + } evexwig; + +/* Pre-defined "_GLOBAL_OFFSET_TABLE_". */ +static symbolS *GOT_symbol; + +/* The dwarf2 return column, adjusted for 32 or 64 bit. */ +unsigned int x86_dwarf2_return_column; + +/* The dwarf2 data alignment, adjusted for 32 or 64 bit. */ +int x86_cie_data_alignment; + +/* Interface to relax_segment. + There are 3 major relax states for 386 jump insns because the + different types of jumps add different sizes to frags when we're + figuring out what sort of jump to choose to reach a given label. */ + +/* Types. */ +#define UNCOND_JUMP 0 +#define COND_JUMP 1 +#define COND_JUMP86 2 + +/* Sizes. */ +#define CODE16 1 +#define SMALL 0 +#define SMALL16 (SMALL | CODE16) +#define BIG 2 +#define BIG16 (BIG | CODE16) + +#ifndef INLINE +#ifdef __GNUC__ +#define INLINE __inline__ +#else +#define INLINE +#endif +#endif + +#define ENCODE_RELAX_STATE(type, size) \ + ((relax_substateT) (((type) << 2) | (size))) +#define TYPE_FROM_RELAX_STATE(s) \ + ((s) >> 2) +#define DISP_SIZE_FROM_RELAX_STATE(s) \ + ((((s) & 3) == BIG ? 4 : (((s) & 3) == BIG16 ? 2 : 1))) + +/* This table is used by relax_frag to promote short jumps to long + ones where necessary. SMALL (short) jumps may be promoted to BIG + (32 bit long) ones, and SMALL16 jumps to BIG16 (16 bit long). We + don't allow a short jump in a 32 bit code segment to be promoted to + a 16 bit offset jump because it's slower (requires data size + prefix), and doesn't work, unless the destination is in the bottom + 64k of the code segment (The top 16 bits of eip are zeroed). */ + +const relax_typeS md_relax_table[] = +{ + /* The fields are: + 1) most positive reach of this state, + 2) most negative reach of this state, + 3) how many bytes this mode will have in the variable part of the frag + 4) which index into the table to try if we can't fit into this one. */ + + /* UNCOND_JUMP states. */ + {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (UNCOND_JUMP, BIG)}, + {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (UNCOND_JUMP, BIG16)}, + /* dword jmp adds 4 bytes to frag: + 0 extra opcode bytes, 4 displacement bytes. */ + {0, 0, 4, 0}, + /* word jmp adds 2 byte2 to frag: + 0 extra opcode bytes, 2 displacement bytes. */ + {0, 0, 2, 0}, + + /* COND_JUMP states. */ + {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (COND_JUMP, BIG)}, + {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (COND_JUMP, BIG16)}, + /* dword conditionals adds 5 bytes to frag: + 1 extra opcode byte, 4 displacement bytes. */ + {0, 0, 5, 0}, + /* word conditionals add 3 bytes to frag: + 1 extra opcode byte, 2 displacement bytes. */ + {0, 0, 3, 0}, + + /* COND_JUMP86 states. */ + {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (COND_JUMP86, BIG)}, + {127 + 1, -128 + 1, 1, ENCODE_RELAX_STATE (COND_JUMP86, BIG16)}, + /* dword conditionals adds 5 bytes to frag: + 1 extra opcode byte, 4 displacement bytes. */ + {0, 0, 5, 0}, + /* word conditionals add 4 bytes to frag: + 1 displacement byte and a 3 byte long branch insn. */ + {0, 0, 4, 0} +}; + +static const arch_entry cpu_arch[] = +{ + /* Do not replace the first two entries - i386_target_format() + relies on them being there in this order. */ + { STRING_COMMA_LEN ("generic32"), PROCESSOR_GENERIC32, + CPU_GENERIC32_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("generic64"), PROCESSOR_GENERIC64, + CPU_GENERIC64_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("i8086"), PROCESSOR_UNKNOWN, + CPU_NONE_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("i186"), PROCESSOR_UNKNOWN, + CPU_I186_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("i286"), PROCESSOR_UNKNOWN, + CPU_I286_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("i386"), PROCESSOR_I386, + CPU_I386_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("i486"), PROCESSOR_I486, + CPU_I486_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("i586"), PROCESSOR_PENTIUM, + CPU_I586_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("i686"), PROCESSOR_PENTIUMPRO, + CPU_I686_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("pentium"), PROCESSOR_PENTIUM, + CPU_I586_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("pentiumpro"), PROCESSOR_PENTIUMPRO, + CPU_PENTIUMPRO_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("pentiumii"), PROCESSOR_PENTIUMPRO, + CPU_P2_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("pentiumiii"),PROCESSOR_PENTIUMPRO, + CPU_P3_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("pentium4"), PROCESSOR_PENTIUM4, + CPU_P4_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("prescott"), PROCESSOR_NOCONA, + CPU_CORE_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("nocona"), PROCESSOR_NOCONA, + CPU_NOCONA_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("yonah"), PROCESSOR_CORE, + CPU_CORE_FLAGS, 1, 0 }, + { STRING_COMMA_LEN ("core"), PROCESSOR_CORE, + CPU_CORE_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("merom"), PROCESSOR_CORE2, + CPU_CORE2_FLAGS, 1, 0 }, + { STRING_COMMA_LEN ("core2"), PROCESSOR_CORE2, + CPU_CORE2_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("corei7"), PROCESSOR_COREI7, + CPU_COREI7_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("l1om"), PROCESSOR_L1OM, + CPU_L1OM_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("k1om"), PROCESSOR_K1OM, + CPU_K1OM_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("k6"), PROCESSOR_K6, + CPU_K6_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("k6_2"), PROCESSOR_K6, + CPU_K6_2_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("athlon"), PROCESSOR_ATHLON, + CPU_ATHLON_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("sledgehammer"), PROCESSOR_K8, + CPU_K8_FLAGS, 1, 0 }, + { STRING_COMMA_LEN ("opteron"), PROCESSOR_K8, + CPU_K8_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("k8"), PROCESSOR_K8, + CPU_K8_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("amdfam10"), PROCESSOR_AMDFAM10, + CPU_AMDFAM10_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("bdver1"), PROCESSOR_BD, + CPU_BDVER1_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("bdver2"), PROCESSOR_BD, + CPU_BDVER2_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("bdver3"), PROCESSOR_BD, + CPU_BDVER3_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("btver1"), PROCESSOR_BT, + CPU_BTVER1_FLAGS, 0, 0 }, + { STRING_COMMA_LEN ("btver2"), PROCESSOR_BT, + CPU_BTVER2_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".8087"), PROCESSOR_UNKNOWN, + CPU_8087_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".287"), PROCESSOR_UNKNOWN, + CPU_287_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".387"), PROCESSOR_UNKNOWN, + CPU_387_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".no87"), PROCESSOR_UNKNOWN, + CPU_ANY87_FLAGS, 0, 1 }, + { STRING_COMMA_LEN (".mmx"), PROCESSOR_UNKNOWN, + CPU_MMX_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".nommx"), PROCESSOR_UNKNOWN, + CPU_3DNOWA_FLAGS, 0, 1 }, + { STRING_COMMA_LEN (".sse"), PROCESSOR_UNKNOWN, + CPU_SSE_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".sse2"), PROCESSOR_UNKNOWN, + CPU_SSE2_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".sse3"), PROCESSOR_UNKNOWN, + CPU_SSE3_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".ssse3"), PROCESSOR_UNKNOWN, + CPU_SSSE3_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".sse4.1"), PROCESSOR_UNKNOWN, + CPU_SSE4_1_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".sse4.2"), PROCESSOR_UNKNOWN, + CPU_SSE4_2_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".sse4"), PROCESSOR_UNKNOWN, + CPU_SSE4_2_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".nosse"), PROCESSOR_UNKNOWN, + CPU_ANY_SSE_FLAGS, 0, 1 }, + { STRING_COMMA_LEN (".avx"), PROCESSOR_UNKNOWN, + CPU_AVX_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".avx2"), PROCESSOR_UNKNOWN, + CPU_AVX2_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".avx512f"), PROCESSOR_UNKNOWN, + CPU_AVX512F_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".avx512cd"), PROCESSOR_UNKNOWN, + CPU_AVX512CD_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".avx512er"), PROCESSOR_UNKNOWN, + CPU_AVX512ER_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".avx512pf"), PROCESSOR_UNKNOWN, + CPU_AVX512PF_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".noavx"), PROCESSOR_UNKNOWN, + CPU_ANY_AVX_FLAGS, 0, 1 }, + { STRING_COMMA_LEN (".vmx"), PROCESSOR_UNKNOWN, + CPU_VMX_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".vmfunc"), PROCESSOR_UNKNOWN, + CPU_VMFUNC_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".smx"), PROCESSOR_UNKNOWN, + CPU_SMX_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".xsave"), PROCESSOR_UNKNOWN, + CPU_XSAVE_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".xsaveopt"), PROCESSOR_UNKNOWN, + CPU_XSAVEOPT_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".aes"), PROCESSOR_UNKNOWN, + CPU_AES_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".pclmul"), PROCESSOR_UNKNOWN, + CPU_PCLMUL_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".clmul"), PROCESSOR_UNKNOWN, + CPU_PCLMUL_FLAGS, 1, 0 }, + { STRING_COMMA_LEN (".fsgsbase"), PROCESSOR_UNKNOWN, + CPU_FSGSBASE_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".rdrnd"), PROCESSOR_UNKNOWN, + CPU_RDRND_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".f16c"), PROCESSOR_UNKNOWN, + CPU_F16C_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".bmi2"), PROCESSOR_UNKNOWN, + CPU_BMI2_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".fma"), PROCESSOR_UNKNOWN, + CPU_FMA_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".fma4"), PROCESSOR_UNKNOWN, + CPU_FMA4_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".xop"), PROCESSOR_UNKNOWN, + CPU_XOP_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".lwp"), PROCESSOR_UNKNOWN, + CPU_LWP_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".movbe"), PROCESSOR_UNKNOWN, + CPU_MOVBE_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".cx16"), PROCESSOR_UNKNOWN, + CPU_CX16_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".ept"), PROCESSOR_UNKNOWN, + CPU_EPT_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".lzcnt"), PROCESSOR_UNKNOWN, + CPU_LZCNT_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".hle"), PROCESSOR_UNKNOWN, + CPU_HLE_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".rtm"), PROCESSOR_UNKNOWN, + CPU_RTM_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".invpcid"), PROCESSOR_UNKNOWN, + CPU_INVPCID_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".clflush"), PROCESSOR_UNKNOWN, + CPU_CLFLUSH_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".nop"), PROCESSOR_UNKNOWN, + CPU_NOP_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".syscall"), PROCESSOR_UNKNOWN, + CPU_SYSCALL_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".rdtscp"), PROCESSOR_UNKNOWN, + CPU_RDTSCP_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".3dnow"), PROCESSOR_UNKNOWN, + CPU_3DNOW_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".3dnowa"), PROCESSOR_UNKNOWN, + CPU_3DNOWA_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".padlock"), PROCESSOR_UNKNOWN, + CPU_PADLOCK_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".pacifica"), PROCESSOR_UNKNOWN, + CPU_SVME_FLAGS, 1, 0 }, + { STRING_COMMA_LEN (".svme"), PROCESSOR_UNKNOWN, + CPU_SVME_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".sse4a"), PROCESSOR_UNKNOWN, + CPU_SSE4A_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".abm"), PROCESSOR_UNKNOWN, + CPU_ABM_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".bmi"), PROCESSOR_UNKNOWN, + CPU_BMI_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".tbm"), PROCESSOR_UNKNOWN, + CPU_TBM_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".adx"), PROCESSOR_UNKNOWN, + CPU_ADX_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".rdseed"), PROCESSOR_UNKNOWN, + CPU_RDSEED_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".prfchw"), PROCESSOR_UNKNOWN, + CPU_PRFCHW_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".smap"), PROCESSOR_UNKNOWN, + CPU_SMAP_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".mpx"), PROCESSOR_UNKNOWN, + CPU_MPX_FLAGS, 0, 0 }, + { STRING_COMMA_LEN (".sha"), PROCESSOR_UNKNOWN, + CPU_SHA_FLAGS, 0, 0 }, +}; + +#ifdef I386COFF +/* Like s_lcomm_internal in gas/read.c but the alignment string + is allowed to be optional. */ + +static symbolS * +pe_lcomm_internal (int needs_align, symbolS *symbolP, addressT size) +{ + addressT align = 0; + + SKIP_WHITESPACE (); + + if (needs_align + && *input_line_pointer == ',') + { + align = parse_align (needs_align - 1); + + if (align == (addressT) -1) + return NULL; + } + else + { + if (size >= 8) + align = 3; + else if (size >= 4) + align = 2; + else if (size >= 2) + align = 1; + else + align = 0; + } + + bss_alloc (symbolP, size, align); + return symbolP; +} + +static void +pe_lcomm (int needs_align) +{ + s_comm_internal (needs_align * 2, pe_lcomm_internal); +} +#endif + +const pseudo_typeS md_pseudo_table[] = +{ +#if !defined(OBJ_AOUT) && !defined(USE_ALIGN_PTWO) + {"align", s_align_bytes, 0}, +#else + {"align", s_align_ptwo, 0}, +#endif + {"arch", set_cpu_arch, 0}, +#ifndef I386COFF + {"bss", s_bss, 0}, +#else + {"lcomm", pe_lcomm, 1}, +#endif + {"ffloat", float_cons, 'f'}, + {"dfloat", float_cons, 'd'}, + {"tfloat", float_cons, 'x'}, + {"value", cons, 2}, + {"slong", signed_cons, 4}, + {"noopt", s_ignore, 0}, + {"optim", s_ignore, 0}, + {"code16gcc", set_16bit_gcc_code_flag, CODE_16BIT}, + {"code16", set_code_flag, CODE_16BIT}, + {"code32", set_code_flag, CODE_32BIT}, + {"code64", set_code_flag, CODE_64BIT}, + {"intel_syntax", set_intel_syntax, 1}, + {"att_syntax", set_intel_syntax, 0}, + {"intel_mnemonic", set_intel_mnemonic, 1}, + {"att_mnemonic", set_intel_mnemonic, 0}, + {"allow_index_reg", set_allow_index_reg, 1}, + {"disallow_index_reg", set_allow_index_reg, 0}, + {"sse_check", set_check, 0}, + {"operand_check", set_check, 1}, +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + {"largecomm", handle_large_common, 0}, +#else + {"file", (void (*) (int)) dwarf2_directive_file, 0}, + {"loc", dwarf2_directive_loc, 0}, + {"loc_mark_labels", dwarf2_directive_loc_mark_labels, 0}, +#endif +#ifdef TE_PE + {"secrel32", pe_directive_secrel, 0}, +#endif + {0, 0, 0} +}; + +/* For interface with expression (). */ +extern char *input_line_pointer; + +/* Hash table for instruction mnemonic lookup. */ +static struct hash_control *op_hash; + +/* Hash table for register lookup. */ +static struct hash_control *reg_hash; + +void +i386_align_code (fragS *fragP, int count) +{ + /* Various efficient no-op patterns for aligning code labels. + Note: Don't try to assemble the instructions in the comments. + 0L and 0w are not legal. */ + static const char f32_1[] = + {0x90}; /* nop */ + static const char f32_2[] = + {0x66,0x90}; /* xchg %ax,%ax */ + static const char f32_3[] = + {0x8d,0x76,0x00}; /* leal 0(%esi),%esi */ + static const char f32_4[] = + {0x8d,0x74,0x26,0x00}; /* leal 0(%esi,1),%esi */ + static const char f32_5[] = + {0x90, /* nop */ + 0x8d,0x74,0x26,0x00}; /* leal 0(%esi,1),%esi */ + static const char f32_6[] = + {0x8d,0xb6,0x00,0x00,0x00,0x00}; /* leal 0L(%esi),%esi */ + static const char f32_7[] = + {0x8d,0xb4,0x26,0x00,0x00,0x00,0x00}; /* leal 0L(%esi,1),%esi */ + static const char f32_8[] = + {0x90, /* nop */ + 0x8d,0xb4,0x26,0x00,0x00,0x00,0x00}; /* leal 0L(%esi,1),%esi */ + static const char f32_9[] = + {0x89,0xf6, /* movl %esi,%esi */ + 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */ + static const char f32_10[] = + {0x8d,0x76,0x00, /* leal 0(%esi),%esi */ + 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */ + static const char f32_11[] = + {0x8d,0x74,0x26,0x00, /* leal 0(%esi,1),%esi */ + 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */ + static const char f32_12[] = + {0x8d,0xb6,0x00,0x00,0x00,0x00, /* leal 0L(%esi),%esi */ + 0x8d,0xbf,0x00,0x00,0x00,0x00}; /* leal 0L(%edi),%edi */ + static const char f32_13[] = + {0x8d,0xb6,0x00,0x00,0x00,0x00, /* leal 0L(%esi),%esi */ + 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */ + static const char f32_14[] = + {0x8d,0xb4,0x26,0x00,0x00,0x00,0x00, /* leal 0L(%esi,1),%esi */ + 0x8d,0xbc,0x27,0x00,0x00,0x00,0x00}; /* leal 0L(%edi,1),%edi */ + static const char f16_3[] = + {0x8d,0x74,0x00}; /* lea 0(%esi),%esi */ + static const char f16_4[] = + {0x8d,0xb4,0x00,0x00}; /* lea 0w(%si),%si */ + static const char f16_5[] = + {0x90, /* nop */ + 0x8d,0xb4,0x00,0x00}; /* lea 0w(%si),%si */ + static const char f16_6[] = + {0x89,0xf6, /* mov %si,%si */ + 0x8d,0xbd,0x00,0x00}; /* lea 0w(%di),%di */ + static const char f16_7[] = + {0x8d,0x74,0x00, /* lea 0(%si),%si */ + 0x8d,0xbd,0x00,0x00}; /* lea 0w(%di),%di */ + static const char f16_8[] = + {0x8d,0xb4,0x00,0x00, /* lea 0w(%si),%si */ + 0x8d,0xbd,0x00,0x00}; /* lea 0w(%di),%di */ + static const char jump_31[] = + {0xeb,0x1d,0x90,0x90,0x90,0x90,0x90, /* jmp .+31; lotsa nops */ + 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, + 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90, + 0x90,0x90,0x90,0x90,0x90,0x90,0x90,0x90}; + static const char *const f32_patt[] = { + f32_1, f32_2, f32_3, f32_4, f32_5, f32_6, f32_7, f32_8, + f32_9, f32_10, f32_11, f32_12, f32_13, f32_14 + }; + static const char *const f16_patt[] = { + f32_1, f32_2, f16_3, f16_4, f16_5, f16_6, f16_7, f16_8 + }; + /* nopl (%[re]ax) */ + static const char alt_3[] = + {0x0f,0x1f,0x00}; + /* nopl 0(%[re]ax) */ + static const char alt_4[] = + {0x0f,0x1f,0x40,0x00}; + /* nopl 0(%[re]ax,%[re]ax,1) */ + static const char alt_5[] = + {0x0f,0x1f,0x44,0x00,0x00}; + /* nopw 0(%[re]ax,%[re]ax,1) */ + static const char alt_6[] = + {0x66,0x0f,0x1f,0x44,0x00,0x00}; + /* nopl 0L(%[re]ax) */ + static const char alt_7[] = + {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00}; + /* nopl 0L(%[re]ax,%[re]ax,1) */ + static const char alt_8[] = + {0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* nopw 0L(%[re]ax,%[re]ax,1) */ + static const char alt_9[] = + {0x66,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* nopw %cs:0L(%[re]ax,%[re]ax,1) */ + static const char alt_10[] = + {0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* data16 + nopw %cs:0L(%[re]ax,%[re]ax,1) */ + static const char alt_long_11[] = + {0x66, + 0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* data16 + data16 + nopw %cs:0L(%[re]ax,%[re]ax,1) */ + static const char alt_long_12[] = + {0x66, + 0x66, + 0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* data16 + data16 + data16 + nopw %cs:0L(%[re]ax,%[re]ax,1) */ + static const char alt_long_13[] = + {0x66, + 0x66, + 0x66, + 0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* data16 + data16 + data16 + data16 + nopw %cs:0L(%[re]ax,%[re]ax,1) */ + static const char alt_long_14[] = + {0x66, + 0x66, + 0x66, + 0x66, + 0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* data16 + data16 + data16 + data16 + data16 + nopw %cs:0L(%[re]ax,%[re]ax,1) */ + static const char alt_long_15[] = + {0x66, + 0x66, + 0x66, + 0x66, + 0x66, + 0x66,0x2e,0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + /* nopl 0(%[re]ax,%[re]ax,1) + nopw 0(%[re]ax,%[re]ax,1) */ + static const char alt_short_11[] = + {0x0f,0x1f,0x44,0x00,0x00, + 0x66,0x0f,0x1f,0x44,0x00,0x00}; + /* nopw 0(%[re]ax,%[re]ax,1) + nopw 0(%[re]ax,%[re]ax,1) */ + static const char alt_short_12[] = + {0x66,0x0f,0x1f,0x44,0x00,0x00, + 0x66,0x0f,0x1f,0x44,0x00,0x00}; + /* nopw 0(%[re]ax,%[re]ax,1) + nopl 0L(%[re]ax) */ + static const char alt_short_13[] = + {0x66,0x0f,0x1f,0x44,0x00,0x00, + 0x0f,0x1f,0x80,0x00,0x00,0x00,0x00}; + /* nopl 0L(%[re]ax) + nopl 0L(%[re]ax) */ + static const char alt_short_14[] = + {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00, + 0x0f,0x1f,0x80,0x00,0x00,0x00,0x00}; + /* nopl 0L(%[re]ax) + nopl 0L(%[re]ax,%[re]ax,1) */ + static const char alt_short_15[] = + {0x0f,0x1f,0x80,0x00,0x00,0x00,0x00, + 0x0f,0x1f,0x84,0x00,0x00,0x00,0x00,0x00}; + static const char *const alt_short_patt[] = { + f32_1, f32_2, alt_3, alt_4, alt_5, alt_6, alt_7, alt_8, + alt_9, alt_10, alt_short_11, alt_short_12, alt_short_13, + alt_short_14, alt_short_15 + }; + static const char *const alt_long_patt[] = { + f32_1, f32_2, alt_3, alt_4, alt_5, alt_6, alt_7, alt_8, + alt_9, alt_10, alt_long_11, alt_long_12, alt_long_13, + alt_long_14, alt_long_15 + }; + + /* Only align for at least a positive non-zero boundary. */ + if (count <= 0 || count > MAX_MEM_FOR_RS_ALIGN_CODE) + return; + + /* We need to decide which NOP sequence to use for 32bit and + 64bit. When -mtune= is used: + + 1. For PROCESSOR_I386, PROCESSOR_I486, PROCESSOR_PENTIUM and + PROCESSOR_GENERIC32, f32_patt will be used. + 2. For PROCESSOR_PENTIUMPRO, PROCESSOR_PENTIUM4, PROCESSOR_NOCONA, + PROCESSOR_CORE, PROCESSOR_CORE2, PROCESSOR_COREI7, and + PROCESSOR_GENERIC64, alt_long_patt will be used. + 3. For PROCESSOR_ATHLON, PROCESSOR_K6, PROCESSOR_K8 and + PROCESSOR_AMDFAM10, PROCESSOR_BD and PROCESSOR_BT, alt_short_patt + will be used. + + When -mtune= isn't used, alt_long_patt will be used if + cpu_arch_isa_flags has CpuNop. Otherwise, f32_patt will + be used. + + When -march= or .arch is used, we can't use anything beyond + cpu_arch_isa_flags. */ + + if (flag_code == CODE_16BIT) + { + if (count > 8) + { + memcpy (fragP->fr_literal + fragP->fr_fix, + jump_31, count); + /* Adjust jump offset. */ + fragP->fr_literal[fragP->fr_fix + 1] = count - 2; + } + else + memcpy (fragP->fr_literal + fragP->fr_fix, + f16_patt[count - 1], count); + } + else + { + const char *const *patt = NULL; + + if (fragP->tc_frag_data.isa == PROCESSOR_UNKNOWN) + { + /* PROCESSOR_UNKNOWN means that all ISAs may be used. */ + switch (cpu_arch_tune) + { + case PROCESSOR_UNKNOWN: + /* We use cpu_arch_isa_flags to check if we SHOULD + optimize with nops. */ + if (fragP->tc_frag_data.isa_flags.bitfield.cpunop) + patt = alt_long_patt; + else + patt = f32_patt; + break; + case PROCESSOR_PENTIUM4: + case PROCESSOR_NOCONA: + case PROCESSOR_CORE: + case PROCESSOR_CORE2: + case PROCESSOR_COREI7: + case PROCESSOR_L1OM: + case PROCESSOR_K1OM: + case PROCESSOR_GENERIC64: + patt = alt_long_patt; + break; + case PROCESSOR_K6: + case PROCESSOR_ATHLON: + case PROCESSOR_K8: + case PROCESSOR_AMDFAM10: + case PROCESSOR_BD: + case PROCESSOR_BT: + patt = alt_short_patt; + break; + case PROCESSOR_I386: + case PROCESSOR_I486: + case PROCESSOR_PENTIUM: + case PROCESSOR_PENTIUMPRO: + case PROCESSOR_GENERIC32: + patt = f32_patt; + break; + } + } + else + { + switch (fragP->tc_frag_data.tune) + { + case PROCESSOR_UNKNOWN: + /* When cpu_arch_isa is set, cpu_arch_tune shouldn't be + PROCESSOR_UNKNOWN. */ + abort (); + break; + + case PROCESSOR_I386: + case PROCESSOR_I486: + case PROCESSOR_PENTIUM: + case PROCESSOR_K6: + case PROCESSOR_ATHLON: + case PROCESSOR_K8: + case PROCESSOR_AMDFAM10: + case PROCESSOR_BD: + case PROCESSOR_BT: + case PROCESSOR_GENERIC32: + /* We use cpu_arch_isa_flags to check if we CAN optimize + with nops. */ + if (fragP->tc_frag_data.isa_flags.bitfield.cpunop) + patt = alt_short_patt; + else + patt = f32_patt; + break; + case PROCESSOR_PENTIUMPRO: + case PROCESSOR_PENTIUM4: + case PROCESSOR_NOCONA: + case PROCESSOR_CORE: + case PROCESSOR_CORE2: + case PROCESSOR_COREI7: + case PROCESSOR_L1OM: + case PROCESSOR_K1OM: + if (fragP->tc_frag_data.isa_flags.bitfield.cpunop) + patt = alt_long_patt; + else + patt = f32_patt; + break; + case PROCESSOR_GENERIC64: + patt = alt_long_patt; + break; + } + } + + if (patt == f32_patt) + { + /* If the padding is less than 15 bytes, we use the normal + ones. Otherwise, we use a jump instruction and adjust + its offset. */ + int limit; + + /* For 64bit, the limit is 3 bytes. */ + if (flag_code == CODE_64BIT + && fragP->tc_frag_data.isa_flags.bitfield.cpulm) + limit = 3; + else + limit = 15; + if (count < limit) + memcpy (fragP->fr_literal + fragP->fr_fix, + patt[count - 1], count); + else + { + memcpy (fragP->fr_literal + fragP->fr_fix, + jump_31, count); + /* Adjust jump offset. */ + fragP->fr_literal[fragP->fr_fix + 1] = count - 2; + } + } + else + { + /* Maximum length of an instruction is 15 byte. If the + padding is greater than 15 bytes and we don't use jump, + we have to break it into smaller pieces. */ + int padding = count; + while (padding > 15) + { + padding -= 15; + memcpy (fragP->fr_literal + fragP->fr_fix + padding, + patt [14], 15); + } + + if (padding) + memcpy (fragP->fr_literal + fragP->fr_fix, + patt [padding - 1], padding); + } + } + fragP->fr_var = count; +} + +static INLINE int +operand_type_all_zero (const union i386_operand_type *x) +{ + switch (ARRAY_SIZE(x->array)) + { + case 3: + if (x->array[2]) + return 0; + case 2: + if (x->array[1]) + return 0; + case 1: + return !x->array[0]; + default: + abort (); + } +} + +static INLINE void +operand_type_set (union i386_operand_type *x, unsigned int v) +{ + switch (ARRAY_SIZE(x->array)) + { + case 3: + x->array[2] = v; + case 2: + x->array[1] = v; + case 1: + x->array[0] = v; + break; + default: + abort (); + } +} + +static INLINE int +operand_type_equal (const union i386_operand_type *x, + const union i386_operand_type *y) +{ + switch (ARRAY_SIZE(x->array)) + { + case 3: + if (x->array[2] != y->array[2]) + return 0; + case 2: + if (x->array[1] != y->array[1]) + return 0; + case 1: + return x->array[0] == y->array[0]; + break; + default: + abort (); + } +} + +static INLINE int +cpu_flags_all_zero (const union i386_cpu_flags *x) +{ + switch (ARRAY_SIZE(x->array)) + { + case 3: + if (x->array[2]) + return 0; + case 2: + if (x->array[1]) + return 0; + case 1: + return !x->array[0]; + default: + abort (); + } +} + +static INLINE void +cpu_flags_set (union i386_cpu_flags *x, unsigned int v) +{ + switch (ARRAY_SIZE(x->array)) + { + case 3: + x->array[2] = v; + case 2: + x->array[1] = v; + case 1: + x->array[0] = v; + break; + default: + abort (); + } +} + +static INLINE int +cpu_flags_equal (const union i386_cpu_flags *x, + const union i386_cpu_flags *y) +{ + switch (ARRAY_SIZE(x->array)) + { + case 3: + if (x->array[2] != y->array[2]) + return 0; + case 2: + if (x->array[1] != y->array[1]) + return 0; + case 1: + return x->array[0] == y->array[0]; + break; + default: + abort (); + } +} + +static INLINE int +cpu_flags_check_cpu64 (i386_cpu_flags f) +{ + return !((flag_code == CODE_64BIT && f.bitfield.cpuno64) + || (flag_code != CODE_64BIT && f.bitfield.cpu64)); +} + +static INLINE i386_cpu_flags +cpu_flags_and (i386_cpu_flags x, i386_cpu_flags y) +{ + switch (ARRAY_SIZE (x.array)) + { + case 3: + x.array [2] &= y.array [2]; + case 2: + x.array [1] &= y.array [1]; + case 1: + x.array [0] &= y.array [0]; + break; + default: + abort (); + } + return x; +} + +static INLINE i386_cpu_flags +cpu_flags_or (i386_cpu_flags x, i386_cpu_flags y) +{ + switch (ARRAY_SIZE (x.array)) + { + case 3: + x.array [2] |= y.array [2]; + case 2: + x.array [1] |= y.array [1]; + case 1: + x.array [0] |= y.array [0]; + break; + default: + abort (); + } + return x; +} + +static INLINE i386_cpu_flags +cpu_flags_and_not (i386_cpu_flags x, i386_cpu_flags y) +{ + switch (ARRAY_SIZE (x.array)) + { + case 3: + x.array [2] &= ~y.array [2]; + case 2: + x.array [1] &= ~y.array [1]; + case 1: + x.array [0] &= ~y.array [0]; + break; + default: + abort (); + } + return x; +} + +#define CPU_FLAGS_ARCH_MATCH 0x1 +#define CPU_FLAGS_64BIT_MATCH 0x2 +#define CPU_FLAGS_AES_MATCH 0x4 +#define CPU_FLAGS_PCLMUL_MATCH 0x8 +#define CPU_FLAGS_AVX_MATCH 0x10 + +#define CPU_FLAGS_32BIT_MATCH \ + (CPU_FLAGS_ARCH_MATCH | CPU_FLAGS_AES_MATCH \ + | CPU_FLAGS_PCLMUL_MATCH | CPU_FLAGS_AVX_MATCH) +#define CPU_FLAGS_PERFECT_MATCH \ + (CPU_FLAGS_32BIT_MATCH | CPU_FLAGS_64BIT_MATCH) + +/* Return CPU flags match bits. */ + +static int +cpu_flags_match (const insn_template *t) +{ + i386_cpu_flags x = t->cpu_flags; + int match = cpu_flags_check_cpu64 (x) ? CPU_FLAGS_64BIT_MATCH : 0; + + x.bitfield.cpu64 = 0; + x.bitfield.cpuno64 = 0; + + if (cpu_flags_all_zero (&x)) + { + /* This instruction is available on all archs. */ + match |= CPU_FLAGS_32BIT_MATCH; + } + else + { + /* This instruction is available only on some archs. */ + i386_cpu_flags cpu = cpu_arch_flags; + + cpu.bitfield.cpu64 = 0; + cpu.bitfield.cpuno64 = 0; + cpu = cpu_flags_and (x, cpu); + if (!cpu_flags_all_zero (&cpu)) + { + if (x.bitfield.cpuavx) + { + /* We only need to check AES/PCLMUL/SSE2AVX with AVX. */ + if (cpu.bitfield.cpuavx) + { + /* Check SSE2AVX. */ + if (!t->opcode_modifier.sse2avx|| sse2avx) + { + match |= (CPU_FLAGS_ARCH_MATCH + | CPU_FLAGS_AVX_MATCH); + /* Check AES. */ + if (!x.bitfield.cpuaes || cpu.bitfield.cpuaes) + match |= CPU_FLAGS_AES_MATCH; + /* Check PCLMUL. */ + if (!x.bitfield.cpupclmul + || cpu.bitfield.cpupclmul) + match |= CPU_FLAGS_PCLMUL_MATCH; + } + } + else + match |= CPU_FLAGS_ARCH_MATCH; + } + else + match |= CPU_FLAGS_32BIT_MATCH; + } + } + return match; +} + +static INLINE i386_operand_type +operand_type_and (i386_operand_type x, i386_operand_type y) +{ + switch (ARRAY_SIZE (x.array)) + { + case 3: + x.array [2] &= y.array [2]; + case 2: + x.array [1] &= y.array [1]; + case 1: + x.array [0] &= y.array [0]; + break; + default: + abort (); + } + return x; +} + +static INLINE i386_operand_type +operand_type_or (i386_operand_type x, i386_operand_type y) +{ + switch (ARRAY_SIZE (x.array)) + { + case 3: + x.array [2] |= y.array [2]; + case 2: + x.array [1] |= y.array [1]; + case 1: + x.array [0] |= y.array [0]; + break; + default: + abort (); + } + return x; +} + +static INLINE i386_operand_type +operand_type_xor (i386_operand_type x, i386_operand_type y) +{ + switch (ARRAY_SIZE (x.array)) + { + case 3: + x.array [2] ^= y.array [2]; + case 2: + x.array [1] ^= y.array [1]; + case 1: + x.array [0] ^= y.array [0]; + break; + default: + abort (); + } + return x; +} + +static const i386_operand_type acc32 = OPERAND_TYPE_ACC32; +static const i386_operand_type acc64 = OPERAND_TYPE_ACC64; +static const i386_operand_type control = OPERAND_TYPE_CONTROL; +static const i386_operand_type inoutportreg + = OPERAND_TYPE_INOUTPORTREG; +static const i386_operand_type reg16_inoutportreg + = OPERAND_TYPE_REG16_INOUTPORTREG; +static const i386_operand_type disp16 = OPERAND_TYPE_DISP16; +static const i386_operand_type disp32 = OPERAND_TYPE_DISP32; +static const i386_operand_type disp32s = OPERAND_TYPE_DISP32S; +static const i386_operand_type disp16_32 = OPERAND_TYPE_DISP16_32; +static const i386_operand_type anydisp + = OPERAND_TYPE_ANYDISP; +static const i386_operand_type regxmm = OPERAND_TYPE_REGXMM; +static const i386_operand_type regymm = OPERAND_TYPE_REGYMM; +static const i386_operand_type regzmm = OPERAND_TYPE_REGZMM; +static const i386_operand_type regmask = OPERAND_TYPE_REGMASK; +static const i386_operand_type imm8 = OPERAND_TYPE_IMM8; +static const i386_operand_type imm8s = OPERAND_TYPE_IMM8S; +static const i386_operand_type imm16 = OPERAND_TYPE_IMM16; +static const i386_operand_type imm32 = OPERAND_TYPE_IMM32; +static const i386_operand_type imm32s = OPERAND_TYPE_IMM32S; +static const i386_operand_type imm64 = OPERAND_TYPE_IMM64; +static const i386_operand_type imm16_32 = OPERAND_TYPE_IMM16_32; +static const i386_operand_type imm16_32s = OPERAND_TYPE_IMM16_32S; +static const i386_operand_type imm16_32_32s = OPERAND_TYPE_IMM16_32_32S; +static const i386_operand_type vec_imm4 = OPERAND_TYPE_VEC_IMM4; +static const i386_operand_type regbnd = OPERAND_TYPE_REGBND; +static const i386_operand_type vec_disp8 = OPERAND_TYPE_VEC_DISP8; + +enum operand_type +{ + reg, + imm, + disp, + anymem +}; + +static INLINE int +operand_type_check (i386_operand_type t, enum operand_type c) +{ + switch (c) + { + case reg: + return (t.bitfield.reg8 + || t.bitfield.reg16 + || t.bitfield.reg32 + || t.bitfield.reg64); + + case imm: + return (t.bitfield.imm8 + || t.bitfield.imm8s + || t.bitfield.imm16 + || t.bitfield.imm32 + || t.bitfield.imm32s + || t.bitfield.imm64); + + case disp: + return (t.bitfield.disp8 + || t.bitfield.disp16 + || t.bitfield.disp32 + || t.bitfield.disp32s + || t.bitfield.disp64); + + case anymem: + return (t.bitfield.disp8 + || t.bitfield.disp16 + || t.bitfield.disp32 + || t.bitfield.disp32s + || t.bitfield.disp64 + || t.bitfield.baseindex); + + default: + abort (); + } + + return 0; +} + +/* Return 1 if there is no conflict in 8bit/16bit/32bit/64bit on + operand J for instruction template T. */ + +static INLINE int +match_reg_size (const insn_template *t, unsigned int j) +{ + return !((i.types[j].bitfield.byte + && !t->operand_types[j].bitfield.byte) + || (i.types[j].bitfield.word + && !t->operand_types[j].bitfield.word) + || (i.types[j].bitfield.dword + && !t->operand_types[j].bitfield.dword) + || (i.types[j].bitfield.qword + && !t->operand_types[j].bitfield.qword)); +} + +/* Return 1 if there is no conflict in any size on operand J for + instruction template T. */ + +static INLINE int +match_mem_size (const insn_template *t, unsigned int j) +{ + return (match_reg_size (t, j) + && !((i.types[j].bitfield.unspecified + && !t->operand_types[j].bitfield.unspecified) + || (i.types[j].bitfield.fword + && !t->operand_types[j].bitfield.fword) + || (i.types[j].bitfield.tbyte + && !t->operand_types[j].bitfield.tbyte) + || (i.types[j].bitfield.xmmword + && !t->operand_types[j].bitfield.xmmword) + || (i.types[j].bitfield.ymmword + && !t->operand_types[j].bitfield.ymmword) + || (i.types[j].bitfield.zmmword + && !t->operand_types[j].bitfield.zmmword))); +} + +/* Return 1 if there is no size conflict on any operands for + instruction template T. */ + +static INLINE int +operand_size_match (const insn_template *t) +{ + unsigned int j; + int match = 1; + + /* Don't check jump instructions. */ + if (t->opcode_modifier.jump + || t->opcode_modifier.jumpbyte + || t->opcode_modifier.jumpdword + || t->opcode_modifier.jumpintersegment) + return match; + + /* Check memory and accumulator operand size. */ + for (j = 0; j < i.operands; j++) + { + if (t->operand_types[j].bitfield.anysize) + continue; + + if (t->operand_types[j].bitfield.acc && !match_reg_size (t, j)) + { + match = 0; + break; + } + + if (i.types[j].bitfield.mem && !match_mem_size (t, j)) + { + match = 0; + break; + } + } + + if (match) + return match; + else if (!t->opcode_modifier.d && !t->opcode_modifier.floatd) + { +mismatch: + i.error = operand_size_mismatch; + return 0; + } + + /* Check reverse. */ + gas_assert (i.operands == 2); + + match = 1; + for (j = 0; j < 2; j++) + { + if (t->operand_types[j].bitfield.acc + && !match_reg_size (t, j ? 0 : 1)) + goto mismatch; + + if (i.types[j].bitfield.mem + && !match_mem_size (t, j ? 0 : 1)) + goto mismatch; + } + + return match; +} + +static INLINE int +operand_type_match (i386_operand_type overlap, + i386_operand_type given) +{ + i386_operand_type temp = overlap; + + temp.bitfield.jumpabsolute = 0; + temp.bitfield.unspecified = 0; + temp.bitfield.byte = 0; + temp.bitfield.word = 0; + temp.bitfield.dword = 0; + temp.bitfield.fword = 0; + temp.bitfield.qword = 0; + temp.bitfield.tbyte = 0; + temp.bitfield.xmmword = 0; + temp.bitfield.ymmword = 0; + temp.bitfield.zmmword = 0; + if (operand_type_all_zero (&temp)) + goto mismatch; + + if (given.bitfield.baseindex == overlap.bitfield.baseindex + && given.bitfield.jumpabsolute == overlap.bitfield.jumpabsolute) + return 1; + +mismatch: + i.error = operand_type_mismatch; + return 0; +} + +/* If given types g0 and g1 are registers they must be of the same type + unless the expected operand type register overlap is null. + Note that Acc in a template matches every size of reg. */ + +static INLINE int +operand_type_register_match (i386_operand_type m0, + i386_operand_type g0, + i386_operand_type t0, + i386_operand_type m1, + i386_operand_type g1, + i386_operand_type t1) +{ + if (!operand_type_check (g0, reg)) + return 1; + + if (!operand_type_check (g1, reg)) + return 1; + + if (g0.bitfield.reg8 == g1.bitfield.reg8 + && g0.bitfield.reg16 == g1.bitfield.reg16 + && g0.bitfield.reg32 == g1.bitfield.reg32 + && g0.bitfield.reg64 == g1.bitfield.reg64) + return 1; + + if (m0.bitfield.acc) + { + t0.bitfield.reg8 = 1; + t0.bitfield.reg16 = 1; + t0.bitfield.reg32 = 1; + t0.bitfield.reg64 = 1; + } + + if (m1.bitfield.acc) + { + t1.bitfield.reg8 = 1; + t1.bitfield.reg16 = 1; + t1.bitfield.reg32 = 1; + t1.bitfield.reg64 = 1; + } + + if (!(t0.bitfield.reg8 & t1.bitfield.reg8) + && !(t0.bitfield.reg16 & t1.bitfield.reg16) + && !(t0.bitfield.reg32 & t1.bitfield.reg32) + && !(t0.bitfield.reg64 & t1.bitfield.reg64)) + return 1; + + i.error = register_type_mismatch; + + return 0; +} + +static INLINE unsigned int +register_number (const reg_entry *r) +{ + unsigned int nr = r->reg_num; + + if (r->reg_flags & RegRex) + nr += 8; + + return nr; +} + +static INLINE unsigned int +mode_from_disp_size (i386_operand_type t) +{ + if (t.bitfield.disp8 || t.bitfield.vec_disp8) + return 1; + else if (t.bitfield.disp16 + || t.bitfield.disp32 + || t.bitfield.disp32s) + return 2; + else + return 0; +} + +static INLINE int +fits_in_signed_byte (offsetT num) +{ + return (num >= -128) && (num <= 127); +} + +static INLINE int +fits_in_unsigned_byte (offsetT num) +{ + return (num & 0xff) == num; +} + +static INLINE int +fits_in_unsigned_word (offsetT num) +{ + return (num & 0xffff) == num; +} + +static INLINE int +fits_in_signed_word (offsetT num) +{ + return (-32768 <= num) && (num <= 32767); +} + +static INLINE int +fits_in_signed_long (offsetT num ATTRIBUTE_UNUSED) +{ +#ifndef BFD64 + return 1; +#else + return (!(((offsetT) -1 << 31) & num) + || (((offsetT) -1 << 31) & num) == ((offsetT) -1 << 31)); +#endif +} /* fits_in_signed_long() */ + +static INLINE int +fits_in_unsigned_long (offsetT num ATTRIBUTE_UNUSED) +{ +#ifndef BFD64 + return 1; +#else + return (num & (((offsetT) 2 << 31) - 1)) == num; +#endif +} /* fits_in_unsigned_long() */ + +static INLINE int +fits_in_vec_disp8 (offsetT num) +{ + int shift = i.memshift; + unsigned int mask; + + if (shift == -1) + abort (); + + mask = (1 << shift) - 1; + + /* Return 0 if NUM isn't properly aligned. */ + if ((num & mask)) + return 0; + + /* Check if NUM will fit in 8bit after shift. */ + return fits_in_signed_byte (num >> shift); +} + +static INLINE int +fits_in_imm4 (offsetT num) +{ + return (num & 0xf) == num; +} + +static i386_operand_type +smallest_imm_type (offsetT num) +{ + i386_operand_type t; + + operand_type_set (&t, 0); + t.bitfield.imm64 = 1; + + if (cpu_arch_tune != PROCESSOR_I486 && num == 1) + { + /* This code is disabled on the 486 because all the Imm1 forms + in the opcode table are slower on the i486. They're the + versions with the implicitly specified single-position + displacement, which has another syntax if you really want to + use that form. */ + t.bitfield.imm1 = 1; + t.bitfield.imm8 = 1; + t.bitfield.imm8s = 1; + t.bitfield.imm16 = 1; + t.bitfield.imm32 = 1; + t.bitfield.imm32s = 1; + } + else if (fits_in_signed_byte (num)) + { + t.bitfield.imm8 = 1; + t.bitfield.imm8s = 1; + t.bitfield.imm16 = 1; + t.bitfield.imm32 = 1; + t.bitfield.imm32s = 1; + } + else if (fits_in_unsigned_byte (num)) + { + t.bitfield.imm8 = 1; + t.bitfield.imm16 = 1; + t.bitfield.imm32 = 1; + t.bitfield.imm32s = 1; + } + else if (fits_in_signed_word (num) || fits_in_unsigned_word (num)) + { + t.bitfield.imm16 = 1; + t.bitfield.imm32 = 1; + t.bitfield.imm32s = 1; + } + else if (fits_in_signed_long (num)) + { + t.bitfield.imm32 = 1; + t.bitfield.imm32s = 1; + } + else if (fits_in_unsigned_long (num)) + t.bitfield.imm32 = 1; + + return t; +} + +static offsetT +offset_in_range (offsetT val, int size) +{ + addressT mask; + + switch (size) + { + case 1: mask = ((addressT) 1 << 8) - 1; break; + case 2: mask = ((addressT) 1 << 16) - 1; break; + case 4: mask = ((addressT) 2 << 31) - 1; break; +#ifdef BFD64 + case 8: mask = ((addressT) 2 << 63) - 1; break; +#endif + default: abort (); + } + +#ifdef BFD64 + /* If BFD64, sign extend val for 32bit address mode. */ + if (flag_code != CODE_64BIT + || i.prefix[ADDR_PREFIX]) + if ((val & ~(((addressT) 2 << 31) - 1)) == 0) + val = (val ^ ((addressT) 1 << 31)) - ((addressT) 1 << 31); +#endif + + if ((val & ~mask) != 0 && (val & ~mask) != ~mask) + { + char buf1[40], buf2[40]; + + sprint_value (buf1, val); + sprint_value (buf2, val & mask); + as_warn (_("%s shortened to %s"), buf1, buf2); + } + return val & mask; +} + +enum PREFIX_GROUP +{ + PREFIX_EXIST = 0, + PREFIX_LOCK, + PREFIX_REP, + PREFIX_OTHER +}; + +/* Returns + a. PREFIX_EXIST if attempting to add a prefix where one from the + same class already exists. + b. PREFIX_LOCK if lock prefix is added. + c. PREFIX_REP if rep/repne prefix is added. + d. PREFIX_OTHER if other prefix is added. + */ + +static enum PREFIX_GROUP +add_prefix (unsigned int prefix) +{ + enum PREFIX_GROUP ret = PREFIX_OTHER; + unsigned int q; + + if (prefix >= REX_OPCODE && prefix < REX_OPCODE + 16 + && flag_code == CODE_64BIT) + { + if ((i.prefix[REX_PREFIX] & prefix & REX_W) + || ((i.prefix[REX_PREFIX] & (REX_R | REX_X | REX_B)) + && (prefix & (REX_R | REX_X | REX_B)))) + ret = PREFIX_EXIST; + q = REX_PREFIX; + } + else + { + switch (prefix) + { + default: + abort (); + + case CS_PREFIX_OPCODE: + case DS_PREFIX_OPCODE: + case ES_PREFIX_OPCODE: + case FS_PREFIX_OPCODE: + case GS_PREFIX_OPCODE: + case SS_PREFIX_OPCODE: + q = SEG_PREFIX; + break; + + case REPNE_PREFIX_OPCODE: + case REPE_PREFIX_OPCODE: + q = REP_PREFIX; + ret = PREFIX_REP; + break; + + case LOCK_PREFIX_OPCODE: + q = LOCK_PREFIX; + ret = PREFIX_LOCK; + break; + + case FWAIT_OPCODE: + q = WAIT_PREFIX; + break; + + case ADDR_PREFIX_OPCODE: + q = ADDR_PREFIX; + break; + + case DATA_PREFIX_OPCODE: + q = DATA_PREFIX; + break; + } + if (i.prefix[q] != 0) + ret = PREFIX_EXIST; + } + + if (ret) + { + if (!i.prefix[q]) + ++i.prefixes; + i.prefix[q] |= prefix; + } + else + as_bad (_("same type of prefix used twice")); + + return ret; +} + +static void +update_code_flag (int value, int check) +{ + PRINTF_LIKE ((*as_error)); + + flag_code = (enum flag_code) value; + if (flag_code == CODE_64BIT) + { + cpu_arch_flags.bitfield.cpu64 = 1; + cpu_arch_flags.bitfield.cpuno64 = 0; + } + else + { + cpu_arch_flags.bitfield.cpu64 = 0; + cpu_arch_flags.bitfield.cpuno64 = 1; + } + if (value == CODE_64BIT && !cpu_arch_flags.bitfield.cpulm ) + { + if (check) + as_error = as_fatal; + else + as_error = as_bad; + (*as_error) (_("64bit mode not supported on `%s'."), + cpu_arch_name ? cpu_arch_name : default_arch); + } + if (value == CODE_32BIT && !cpu_arch_flags.bitfield.cpui386) + { + if (check) + as_error = as_fatal; + else + as_error = as_bad; + (*as_error) (_("32bit mode not supported on `%s'."), + cpu_arch_name ? cpu_arch_name : default_arch); + } + stackop_size = '\0'; +} + +static void +set_code_flag (int value) +{ + update_code_flag (value, 0); +} + +static void +set_16bit_gcc_code_flag (int new_code_flag) +{ + flag_code = (enum flag_code) new_code_flag; + if (flag_code != CODE_16BIT) + abort (); + cpu_arch_flags.bitfield.cpu64 = 0; + cpu_arch_flags.bitfield.cpuno64 = 1; + stackop_size = LONG_MNEM_SUFFIX; +} + +static void +set_intel_syntax (int syntax_flag) +{ + /* Find out if register prefixing is specified. */ + int ask_naked_reg = 0; + + SKIP_WHITESPACE (); + if (!is_end_of_line[(unsigned char) *input_line_pointer]) + { + char *string = input_line_pointer; + int e = get_symbol_end (); + + if (strcmp (string, "prefix") == 0) + ask_naked_reg = 1; + else if (strcmp (string, "noprefix") == 0) + ask_naked_reg = -1; + else + as_bad (_("bad argument to syntax directive.")); + *input_line_pointer = e; + } + demand_empty_rest_of_line (); + + intel_syntax = syntax_flag; + + if (ask_naked_reg == 0) + allow_naked_reg = (intel_syntax + && (bfd_get_symbol_leading_char (stdoutput) != '\0')); + else + allow_naked_reg = (ask_naked_reg < 0); + + expr_set_rank (O_full_ptr, syntax_flag ? 10 : 0); + + identifier_chars['%'] = intel_syntax && allow_naked_reg ? '%' : 0; + identifier_chars['$'] = intel_syntax ? '$' : 0; + register_prefix = allow_naked_reg ? "" : "%"; +} + +static void +set_intel_mnemonic (int mnemonic_flag) +{ + intel_mnemonic = mnemonic_flag; +} + +static void +set_allow_index_reg (int flag) +{ + allow_index_reg = flag; +} + +static void +set_check (int what) +{ + enum check_kind *kind; + const char *str; + + if (what) + { + kind = &operand_check; + str = "operand"; + } + else + { + kind = &sse_check; + str = "sse"; + } + + SKIP_WHITESPACE (); + + if (!is_end_of_line[(unsigned char) *input_line_pointer]) + { + char *string = input_line_pointer; + int e = get_symbol_end (); + + if (strcmp (string, "none") == 0) + *kind = check_none; + else if (strcmp (string, "warning") == 0) + *kind = check_warning; + else if (strcmp (string, "error") == 0) + *kind = check_error; + else + as_bad (_("bad argument to %s_check directive."), str); + *input_line_pointer = e; + } + else + as_bad (_("missing argument for %s_check directive"), str); + + demand_empty_rest_of_line (); +} + +static void +check_cpu_arch_compatible (const char *name ATTRIBUTE_UNUSED, + i386_cpu_flags new_flag ATTRIBUTE_UNUSED) +{ +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + static const char *arch; + + /* Intel LIOM is only supported on ELF. */ + if (!IS_ELF) + return; + + if (!arch) + { + /* Use cpu_arch_name if it is set in md_parse_option. Otherwise + use default_arch. */ + arch = cpu_arch_name; + if (!arch) + arch = default_arch; + } + + /* If we are targeting Intel L1OM, we must enable it. */ + if (get_elf_backend_data (stdoutput)->elf_machine_code != EM_L1OM + || new_flag.bitfield.cpul1om) + return; + + /* If we are targeting Intel K1OM, we must enable it. */ + if (get_elf_backend_data (stdoutput)->elf_machine_code != EM_K1OM + || new_flag.bitfield.cpuk1om) + return; + + as_bad (_("`%s' is not supported on `%s'"), name, arch); +#endif +} + +static void +set_cpu_arch (int dummy ATTRIBUTE_UNUSED) +{ + SKIP_WHITESPACE (); + + if (!is_end_of_line[(unsigned char) *input_line_pointer]) + { + char *string = input_line_pointer; + int e = get_symbol_end (); + unsigned int j; + i386_cpu_flags flags; + + for (j = 0; j < ARRAY_SIZE (cpu_arch); j++) + { + if (strcmp (string, cpu_arch[j].name) == 0) + { + check_cpu_arch_compatible (string, cpu_arch[j].flags); + + if (*string != '.') + { + cpu_arch_name = cpu_arch[j].name; + cpu_sub_arch_name = NULL; + cpu_arch_flags = cpu_arch[j].flags; + if (flag_code == CODE_64BIT) + { + cpu_arch_flags.bitfield.cpu64 = 1; + cpu_arch_flags.bitfield.cpuno64 = 0; + } + else + { + cpu_arch_flags.bitfield.cpu64 = 0; + cpu_arch_flags.bitfield.cpuno64 = 1; + } + cpu_arch_isa = cpu_arch[j].type; + cpu_arch_isa_flags = cpu_arch[j].flags; + if (!cpu_arch_tune_set) + { + cpu_arch_tune = cpu_arch_isa; + cpu_arch_tune_flags = cpu_arch_isa_flags; + } + break; + } + + if (!cpu_arch[j].negated) + flags = cpu_flags_or (cpu_arch_flags, + cpu_arch[j].flags); + else + flags = cpu_flags_and_not (cpu_arch_flags, + cpu_arch[j].flags); + if (!cpu_flags_equal (&flags, &cpu_arch_flags)) + { + if (cpu_sub_arch_name) + { + char *name = cpu_sub_arch_name; + cpu_sub_arch_name = concat (name, + cpu_arch[j].name, + (const char *) NULL); + free (name); + } + else + cpu_sub_arch_name = xstrdup (cpu_arch[j].name); + cpu_arch_flags = flags; + cpu_arch_isa_flags = flags; + } + *input_line_pointer = e; + demand_empty_rest_of_line (); + return; + } + } + if (j >= ARRAY_SIZE (cpu_arch)) + as_bad (_("no such architecture: `%s'"), string); + + *input_line_pointer = e; + } + else + as_bad (_("missing cpu architecture")); + + no_cond_jump_promotion = 0; + if (*input_line_pointer == ',' + && !is_end_of_line[(unsigned char) input_line_pointer[1]]) + { + char *string = ++input_line_pointer; + int e = get_symbol_end (); + + if (strcmp (string, "nojumps") == 0) + no_cond_jump_promotion = 1; + else if (strcmp (string, "jumps") == 0) + ; + else + as_bad (_("no such architecture modifier: `%s'"), string); + + *input_line_pointer = e; + } + + demand_empty_rest_of_line (); +} + +enum bfd_architecture +i386_arch (void) +{ + if (cpu_arch_isa == PROCESSOR_L1OM) + { + if (OUTPUT_FLAVOR != bfd_target_elf_flavour + || flag_code != CODE_64BIT) + as_fatal (_("Intel L1OM is 64bit ELF only")); + return bfd_arch_l1om; + } + else if (cpu_arch_isa == PROCESSOR_K1OM) + { + if (OUTPUT_FLAVOR != bfd_target_elf_flavour + || flag_code != CODE_64BIT) + as_fatal (_("Intel K1OM is 64bit ELF only")); + return bfd_arch_k1om; + } + else + return bfd_arch_i386; +} + +unsigned long +i386_mach (void) +{ + if (!strncmp (default_arch, "x86_64", 6)) + { + if (cpu_arch_isa == PROCESSOR_L1OM) + { + if (OUTPUT_FLAVOR != bfd_target_elf_flavour + || default_arch[6] != '\0') + as_fatal (_("Intel L1OM is 64bit ELF only")); + return bfd_mach_l1om; + } + else if (cpu_arch_isa == PROCESSOR_K1OM) + { + if (OUTPUT_FLAVOR != bfd_target_elf_flavour + || default_arch[6] != '\0') + as_fatal (_("Intel K1OM is 64bit ELF only")); + return bfd_mach_k1om; + } + else if (default_arch[6] == '\0') + return bfd_mach_x86_64; + else + return bfd_mach_x64_32; + } + else if (!strcmp (default_arch, "i386")) + return bfd_mach_i386_i386; + else + as_fatal (_("unknown architecture")); +} + +void +md_begin (void) +{ + const char *hash_err; + + /* Initialize op_hash hash table. */ + op_hash = hash_new (); + + { + const insn_template *optab; + templates *core_optab; + + /* Setup for loop. */ + optab = i386_optab; + core_optab = (templates *) xmalloc (sizeof (templates)); + core_optab->start = optab; + + while (1) + { + ++optab; + if (optab->name == NULL + || strcmp (optab->name, (optab - 1)->name) != 0) + { + /* different name --> ship out current template list; + add to hash table; & begin anew. */ + core_optab->end = optab; + hash_err = hash_insert (op_hash, + (optab - 1)->name, + (void *) core_optab); + if (hash_err) + { + as_fatal (_("can't hash %s: %s"), + (optab - 1)->name, + hash_err); + } + if (optab->name == NULL) + break; + core_optab = (templates *) xmalloc (sizeof (templates)); + core_optab->start = optab; + } + } + } + + /* Initialize reg_hash hash table. */ + reg_hash = hash_new (); + { + const reg_entry *regtab; + unsigned int regtab_size = i386_regtab_size; + + for (regtab = i386_regtab; regtab_size--; regtab++) + { + hash_err = hash_insert (reg_hash, regtab->reg_name, (void *) regtab); + if (hash_err) + as_fatal (_("can't hash %s: %s"), + regtab->reg_name, + hash_err); + } + } + + /* Fill in lexical tables: mnemonic_chars, operand_chars. */ + { + int c; + char *p; + + for (c = 0; c < 256; c++) + { + if (ISDIGIT (c)) + { + digit_chars[c] = c; + mnemonic_chars[c] = c; + register_chars[c] = c; + operand_chars[c] = c; + } + else if (ISLOWER (c)) + { + mnemonic_chars[c] = c; + register_chars[c] = c; + operand_chars[c] = c; + } + else if (ISUPPER (c)) + { + mnemonic_chars[c] = TOLOWER (c); + register_chars[c] = mnemonic_chars[c]; + operand_chars[c] = c; + } + else if (c == '{' || c == '}') + operand_chars[c] = c; + + if (ISALPHA (c) || ISDIGIT (c)) + identifier_chars[c] = c; + else if (c >= 128) + { + identifier_chars[c] = c; + operand_chars[c] = c; + } + } + +#ifdef LEX_AT + identifier_chars['@'] = '@'; +#endif +#ifdef LEX_QM + identifier_chars['?'] = '?'; + operand_chars['?'] = '?'; +#endif + digit_chars['-'] = '-'; + mnemonic_chars['_'] = '_'; + mnemonic_chars['-'] = '-'; + mnemonic_chars['.'] = '.'; + identifier_chars['_'] = '_'; + identifier_chars['.'] = '.'; + + for (p = operand_special_chars; *p != '\0'; p++) + operand_chars[(unsigned char) *p] = *p; + } + + if (flag_code == CODE_64BIT) + { +#if defined (OBJ_COFF) && defined (TE_PE) + x86_dwarf2_return_column = (OUTPUT_FLAVOR == bfd_target_coff_flavour + ? 32 : 16); +#else + x86_dwarf2_return_column = 16; +#endif + x86_cie_data_alignment = -8; + } + else + { + x86_dwarf2_return_column = 8; + x86_cie_data_alignment = -4; + } +} + +void +i386_print_statistics (FILE *file) +{ + hash_print_statistics (file, "i386 opcode", op_hash); + hash_print_statistics (file, "i386 register", reg_hash); +} + +#ifdef DEBUG386 + +/* Debugging routines for md_assemble. */ +static void pte (insn_template *); +static void pt (i386_operand_type); +static void pe (expressionS *); +static void ps (symbolS *); + +static void +pi (char *line, i386_insn *x) +{ + unsigned int j; + + fprintf (stdout, "%s: template ", line); + pte (&x->tm); + fprintf (stdout, " address: base %s index %s scale %x\n", + x->base_reg ? x->base_reg->reg_name : "none", + x->index_reg ? x->index_reg->reg_name : "none", + x->log2_scale_factor); + fprintf (stdout, " modrm: mode %x reg %x reg/mem %x\n", + x->rm.mode, x->rm.reg, x->rm.regmem); + fprintf (stdout, " sib: base %x index %x scale %x\n", + x->sib.base, x->sib.index, x->sib.scale); + fprintf (stdout, " rex: 64bit %x extX %x extY %x extZ %x\n", + (x->rex & REX_W) != 0, + (x->rex & REX_R) != 0, + (x->rex & REX_X) != 0, + (x->rex & REX_B) != 0); + for (j = 0; j < x->operands; j++) + { + fprintf (stdout, " #%d: ", j + 1); + pt (x->types[j]); + fprintf (stdout, "\n"); + if (x->types[j].bitfield.reg8 + || x->types[j].bitfield.reg16 + || x->types[j].bitfield.reg32 + || x->types[j].bitfield.reg64 + || x->types[j].bitfield.regmmx + || x->types[j].bitfield.regxmm + || x->types[j].bitfield.regymm + || x->types[j].bitfield.regzmm + || x->types[j].bitfield.sreg2 + || x->types[j].bitfield.sreg3 + || x->types[j].bitfield.control + || x->types[j].bitfield.debug + || x->types[j].bitfield.test) + fprintf (stdout, "%s\n", x->op[j].regs->reg_name); + if (operand_type_check (x->types[j], imm)) + pe (x->op[j].imms); + if (operand_type_check (x->types[j], disp)) + pe (x->op[j].disps); + } +} + +static void +pte (insn_template *t) +{ + unsigned int j; + fprintf (stdout, " %d operands ", t->operands); + fprintf (stdout, "opcode %x ", t->base_opcode); + if (t->extension_opcode != None) + fprintf (stdout, "ext %x ", t->extension_opcode); + if (t->opcode_modifier.d) + fprintf (stdout, "D"); + if (t->opcode_modifier.w) + fprintf (stdout, "W"); + fprintf (stdout, "\n"); + for (j = 0; j < t->operands; j++) + { + fprintf (stdout, " #%d type ", j + 1); + pt (t->operand_types[j]); + fprintf (stdout, "\n"); + } +} + +static void +pe (expressionS *e) +{ + fprintf (stdout, " operation %d\n", e->X_op); + fprintf (stdout, " add_number %ld (%lx)\n", + (long) e->X_add_number, (long) e->X_add_number); + if (e->X_add_symbol) + { + fprintf (stdout, " add_symbol "); + ps (e->X_add_symbol); + fprintf (stdout, "\n"); + } + if (e->X_op_symbol) + { + fprintf (stdout, " op_symbol "); + ps (e->X_op_symbol); + fprintf (stdout, "\n"); + } +} + +static void +ps (symbolS *s) +{ + fprintf (stdout, "%s type %s%s", + S_GET_NAME (s), + S_IS_EXTERNAL (s) ? "EXTERNAL " : "", + segment_name (S_GET_SEGMENT (s))); +} + +static struct type_name + { + i386_operand_type mask; + const char *name; + } +const type_names[] = +{ + { OPERAND_TYPE_REG8, "r8" }, + { OPERAND_TYPE_REG16, "r16" }, + { OPERAND_TYPE_REG32, "r32" }, + { OPERAND_TYPE_REG64, "r64" }, + { OPERAND_TYPE_IMM8, "i8" }, + { OPERAND_TYPE_IMM8, "i8s" }, + { OPERAND_TYPE_IMM16, "i16" }, + { OPERAND_TYPE_IMM32, "i32" }, + { OPERAND_TYPE_IMM32S, "i32s" }, + { OPERAND_TYPE_IMM64, "i64" }, + { OPERAND_TYPE_IMM1, "i1" }, + { OPERAND_TYPE_BASEINDEX, "BaseIndex" }, + { OPERAND_TYPE_DISP8, "d8" }, + { OPERAND_TYPE_DISP16, "d16" }, + { OPERAND_TYPE_DISP32, "d32" }, + { OPERAND_TYPE_DISP32S, "d32s" }, + { OPERAND_TYPE_DISP64, "d64" }, + { OPERAND_TYPE_VEC_DISP8, "Vector d8" }, + { OPERAND_TYPE_INOUTPORTREG, "InOutPortReg" }, + { OPERAND_TYPE_SHIFTCOUNT, "ShiftCount" }, + { OPERAND_TYPE_CONTROL, "control reg" }, + { OPERAND_TYPE_TEST, "test reg" }, + { OPERAND_TYPE_DEBUG, "debug reg" }, + { OPERAND_TYPE_FLOATREG, "FReg" }, + { OPERAND_TYPE_FLOATACC, "FAcc" }, + { OPERAND_TYPE_SREG2, "SReg2" }, + { OPERAND_TYPE_SREG3, "SReg3" }, + { OPERAND_TYPE_ACC, "Acc" }, + { OPERAND_TYPE_JUMPABSOLUTE, "Jump Absolute" }, + { OPERAND_TYPE_REGMMX, "rMMX" }, + { OPERAND_TYPE_REGXMM, "rXMM" }, + { OPERAND_TYPE_REGYMM, "rYMM" }, + { OPERAND_TYPE_REGZMM, "rZMM" }, + { OPERAND_TYPE_REGMASK, "Mask reg" }, + { OPERAND_TYPE_ESSEG, "es" }, +}; + +static void +pt (i386_operand_type t) +{ + unsigned int j; + i386_operand_type a; + + for (j = 0; j < ARRAY_SIZE (type_names); j++) + { + a = operand_type_and (t, type_names[j].mask); + if (!operand_type_all_zero (&a)) + fprintf (stdout, "%s, ", type_names[j].name); + } + fflush (stdout); +} + +#endif /* DEBUG386 */ + +static bfd_reloc_code_real_type +reloc (unsigned int size, + int pcrel, + int sign, + int bnd_prefix, + bfd_reloc_code_real_type other) +{ + if (other != NO_RELOC) + { + reloc_howto_type *rel; + + if (size == 8) + switch (other) + { + case BFD_RELOC_X86_64_GOT32: + return BFD_RELOC_X86_64_GOT64; + break; + case BFD_RELOC_X86_64_PLTOFF64: + return BFD_RELOC_X86_64_PLTOFF64; + break; + case BFD_RELOC_X86_64_GOTPC32: + other = BFD_RELOC_X86_64_GOTPC64; + break; + case BFD_RELOC_X86_64_GOTPCREL: + other = BFD_RELOC_X86_64_GOTPCREL64; + break; + case BFD_RELOC_X86_64_TPOFF32: + other = BFD_RELOC_X86_64_TPOFF64; + break; + case BFD_RELOC_X86_64_DTPOFF32: + other = BFD_RELOC_X86_64_DTPOFF64; + break; + default: + break; + } + +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + if (other == BFD_RELOC_SIZE32) + { + if (size == 8) + return BFD_RELOC_SIZE64; + if (pcrel) + as_bad (_("there are no pc-relative size relocations")); + } +#endif + + /* Sign-checking 4-byte relocations in 16-/32-bit code is pointless. */ + if (size == 4 && (flag_code != CODE_64BIT || disallow_64bit_reloc)) + sign = -1; + + rel = bfd_reloc_type_lookup (stdoutput, other); + if (!rel) + as_bad (_("unknown relocation (%u)"), other); + else if (size != bfd_get_reloc_size (rel)) + as_bad (_("%u-byte relocation cannot be applied to %u-byte field"), + bfd_get_reloc_size (rel), + size); + else if (pcrel && !rel->pc_relative) + as_bad (_("non-pc-relative relocation for pc-relative field")); + else if ((rel->complain_on_overflow == complain_overflow_signed + && !sign) + || (rel->complain_on_overflow == complain_overflow_unsigned + && sign > 0)) + as_bad (_("relocated field and relocation type differ in signedness")); + else + return other; + return NO_RELOC; + } + + if (pcrel) + { + if (!sign) + as_bad (_("there are no unsigned pc-relative relocations")); + switch (size) + { + case 1: return BFD_RELOC_8_PCREL; + case 2: return BFD_RELOC_16_PCREL; + case 4: return (bnd_prefix && object_64bit + ? BFD_RELOC_X86_64_PC32_BND + : BFD_RELOC_32_PCREL); + case 8: return BFD_RELOC_64_PCREL; + } + as_bad (_("cannot do %u byte pc-relative relocation"), size); + } + else + { + if (sign > 0) + switch (size) + { + case 4: return BFD_RELOC_X86_64_32S; + } + else + switch (size) + { + case 1: return BFD_RELOC_8; + case 2: return BFD_RELOC_16; + case 4: return BFD_RELOC_32; + case 8: return BFD_RELOC_64; + } + as_bad (_("cannot do %s %u byte relocation"), + sign > 0 ? "signed" : "unsigned", size); + } + + return NO_RELOC; +} + +/* Here we decide which fixups can be adjusted to make them relative to + the beginning of the section instead of the symbol. Basically we need + to make sure that the dynamic relocations are done correctly, so in + some cases we force the original symbol to be used. */ + +int +tc_i386_fix_adjustable (fixS *fixP ATTRIBUTE_UNUSED) +{ +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + if (!IS_ELF) + return 1; + + /* Don't adjust pc-relative references to merge sections in 64-bit + mode. */ + if (use_rela_relocations + && (S_GET_SEGMENT (fixP->fx_addsy)->flags & SEC_MERGE) != 0 + && fixP->fx_pcrel) + return 0; + + /* The x86_64 GOTPCREL are represented as 32bit PCrel relocations + and changed later by validate_fix. */ + if (GOT_symbol && fixP->fx_subsy == GOT_symbol + && fixP->fx_r_type == BFD_RELOC_32_PCREL) + return 0; + + /* Adjust_reloc_syms doesn't know about the GOT. Need to keep symbol + for size relocations. */ + if (fixP->fx_r_type == BFD_RELOC_SIZE32 + || fixP->fx_r_type == BFD_RELOC_SIZE64 + || fixP->fx_r_type == BFD_RELOC_386_GOTOFF + || fixP->fx_r_type == BFD_RELOC_386_PLT32 + || fixP->fx_r_type == BFD_RELOC_386_GOT32 + || fixP->fx_r_type == BFD_RELOC_386_TLS_GD + || fixP->fx_r_type == BFD_RELOC_386_TLS_LDM + || fixP->fx_r_type == BFD_RELOC_386_TLS_LDO_32 + || fixP->fx_r_type == BFD_RELOC_386_TLS_IE_32 + || fixP->fx_r_type == BFD_RELOC_386_TLS_IE + || fixP->fx_r_type == BFD_RELOC_386_TLS_GOTIE + || fixP->fx_r_type == BFD_RELOC_386_TLS_LE_32 + || fixP->fx_r_type == BFD_RELOC_386_TLS_LE + || fixP->fx_r_type == BFD_RELOC_386_TLS_GOTDESC + || fixP->fx_r_type == BFD_RELOC_386_TLS_DESC_CALL + || fixP->fx_r_type == BFD_RELOC_X86_64_PLT32 + || fixP->fx_r_type == BFD_RELOC_X86_64_GOT32 + || fixP->fx_r_type == BFD_RELOC_X86_64_GOTPCREL + || fixP->fx_r_type == BFD_RELOC_X86_64_TLSGD + || fixP->fx_r_type == BFD_RELOC_X86_64_TLSLD + || fixP->fx_r_type == BFD_RELOC_X86_64_DTPOFF32 + || fixP->fx_r_type == BFD_RELOC_X86_64_DTPOFF64 + || fixP->fx_r_type == BFD_RELOC_X86_64_GOTTPOFF + || fixP->fx_r_type == BFD_RELOC_X86_64_TPOFF32 + || fixP->fx_r_type == BFD_RELOC_X86_64_TPOFF64 + || fixP->fx_r_type == BFD_RELOC_X86_64_GOTOFF64 + || fixP->fx_r_type == BFD_RELOC_X86_64_GOTPC32_TLSDESC + || fixP->fx_r_type == BFD_RELOC_X86_64_TLSDESC_CALL + || fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + return 0; +#endif + return 1; +} + +static int +intel_float_operand (const char *mnemonic) +{ + /* Note that the value returned is meaningful only for opcodes with (memory) + operands, hence the code here is free to improperly handle opcodes that + have no operands (for better performance and smaller code). */ + + if (mnemonic[0] != 'f') + return 0; /* non-math */ + + switch (mnemonic[1]) + { + /* fclex, fdecstp, fdisi, femms, feni, fincstp, finit, fsetpm, and + the fs segment override prefix not currently handled because no + call path can make opcodes without operands get here */ + case 'i': + return 2 /* integer op */; + case 'l': + if (mnemonic[2] == 'd' && (mnemonic[3] == 'c' || mnemonic[3] == 'e')) + return 3; /* fldcw/fldenv */ + break; + case 'n': + if (mnemonic[2] != 'o' /* fnop */) + return 3; /* non-waiting control op */ + break; + case 'r': + if (mnemonic[2] == 's') + return 3; /* frstor/frstpm */ + break; + case 's': + if (mnemonic[2] == 'a') + return 3; /* fsave */ + if (mnemonic[2] == 't') + { + switch (mnemonic[3]) + { + case 'c': /* fstcw */ + case 'd': /* fstdw */ + case 'e': /* fstenv */ + case 's': /* fsts[gw] */ + return 3; + } + } + break; + case 'x': + if (mnemonic[2] == 'r' || mnemonic[2] == 's') + return 0; /* fxsave/fxrstor are not really math ops */ + break; + } + + return 1; +} + +/* Build the VEX prefix. */ + +static void +build_vex_prefix (const insn_template *t) +{ + unsigned int register_specifier; + unsigned int implied_prefix; + unsigned int vector_length; + + /* Check register specifier. */ + if (i.vex.register_specifier) + { + register_specifier = + ~register_number (i.vex.register_specifier) & 0xf; + gas_assert ((i.vex.register_specifier->reg_flags & RegVRex) == 0); + } + else + register_specifier = 0xf; + + /* Use 2-byte VEX prefix by swappping destination and source + operand. */ + if (!i.swap_operand + && i.operands == i.reg_operands + && i.tm.opcode_modifier.vexopcode == VEX0F + && i.tm.opcode_modifier.s + && i.rex == REX_B) + { + unsigned int xchg = i.operands - 1; + union i386_op temp_op; + i386_operand_type temp_type; + + temp_type = i.types[xchg]; + i.types[xchg] = i.types[0]; + i.types[0] = temp_type; + temp_op = i.op[xchg]; + i.op[xchg] = i.op[0]; + i.op[0] = temp_op; + + gas_assert (i.rm.mode == 3); + + i.rex = REX_R; + xchg = i.rm.regmem; + i.rm.regmem = i.rm.reg; + i.rm.reg = xchg; + + /* Use the next insn. */ + i.tm = t[1]; + } + + if (i.tm.opcode_modifier.vex == VEXScalar) + vector_length = avxscalar; + else + vector_length = i.tm.opcode_modifier.vex == VEX256 ? 1 : 0; + + switch ((i.tm.base_opcode >> 8) & 0xff) + { + case 0: + implied_prefix = 0; + break; + case DATA_PREFIX_OPCODE: + implied_prefix = 1; + break; + case REPE_PREFIX_OPCODE: + implied_prefix = 2; + break; + case REPNE_PREFIX_OPCODE: + implied_prefix = 3; + break; + default: + abort (); + } + + /* Use 2-byte VEX prefix if possible. */ + if (i.tm.opcode_modifier.vexopcode == VEX0F + && i.tm.opcode_modifier.vexw != VEXW1 + && (i.rex & (REX_W | REX_X | REX_B)) == 0) + { + /* 2-byte VEX prefix. */ + unsigned int r; + + i.vex.length = 2; + i.vex.bytes[0] = 0xc5; + + /* Check the REX.R bit. */ + r = (i.rex & REX_R) ? 0 : 1; + i.vex.bytes[1] = (r << 7 + | register_specifier << 3 + | vector_length << 2 + | implied_prefix); + } + else + { + /* 3-byte VEX prefix. */ + unsigned int m, w; + + i.vex.length = 3; + + switch (i.tm.opcode_modifier.vexopcode) + { + case VEX0F: + m = 0x1; + i.vex.bytes[0] = 0xc4; + break; + case VEX0F38: + m = 0x2; + i.vex.bytes[0] = 0xc4; + break; + case VEX0F3A: + m = 0x3; + i.vex.bytes[0] = 0xc4; + break; + case XOP08: + m = 0x8; + i.vex.bytes[0] = 0x8f; + break; + case XOP09: + m = 0x9; + i.vex.bytes[0] = 0x8f; + break; + case XOP0A: + m = 0xa; + i.vex.bytes[0] = 0x8f; + break; + default: + abort (); + } + + /* The high 3 bits of the second VEX byte are 1's compliment + of RXB bits from REX. */ + i.vex.bytes[1] = (~i.rex & 0x7) << 5 | m; + + /* Check the REX.W bit. */ + w = (i.rex & REX_W) ? 1 : 0; + if (i.tm.opcode_modifier.vexw) + { + if (w) + abort (); + + if (i.tm.opcode_modifier.vexw == VEXW1) + w = 1; + } + + i.vex.bytes[2] = (w << 7 + | register_specifier << 3 + | vector_length << 2 + | implied_prefix); + } +} + +/* Build the EVEX prefix. */ + +static void +build_evex_prefix (void) +{ + unsigned int register_specifier; + unsigned int implied_prefix; + unsigned int m, w; + rex_byte vrex_used = 0; + + /* Check register specifier. */ + if (i.vex.register_specifier) + { + gas_assert ((i.vrex & REX_X) == 0); + + register_specifier = i.vex.register_specifier->reg_num; + if ((i.vex.register_specifier->reg_flags & RegRex)) + register_specifier += 8; + /* The upper 16 registers are encoded in the fourth byte of the + EVEX prefix. */ + if (!(i.vex.register_specifier->reg_flags & RegVRex)) + i.vex.bytes[3] = 0x8; + register_specifier = ~register_specifier & 0xf; + } + else + { + register_specifier = 0xf; + + /* Encode upper 16 vector index register in the fourth byte of + the EVEX prefix. */ + if (!(i.vrex & REX_X)) + i.vex.bytes[3] = 0x8; + else + vrex_used |= REX_X; + } + + switch ((i.tm.base_opcode >> 8) & 0xff) + { + case 0: + implied_prefix = 0; + break; + case DATA_PREFIX_OPCODE: + implied_prefix = 1; + break; + case REPE_PREFIX_OPCODE: + implied_prefix = 2; + break; + case REPNE_PREFIX_OPCODE: + implied_prefix = 3; + break; + default: + abort (); + } + + /* 4 byte EVEX prefix. */ + i.vex.length = 4; + i.vex.bytes[0] = 0x62; + + /* mmmm bits. */ + switch (i.tm.opcode_modifier.vexopcode) + { + case VEX0F: + m = 1; + break; + case VEX0F38: + m = 2; + break; + case VEX0F3A: + m = 3; + break; + default: + abort (); + break; + } + + /* The high 3 bits of the second EVEX byte are 1's compliment of RXB + bits from REX. */ + i.vex.bytes[1] = (~i.rex & 0x7) << 5 | m; + + /* The fifth bit of the second EVEX byte is 1's compliment of the + REX_R bit in VREX. */ + if (!(i.vrex & REX_R)) + i.vex.bytes[1] |= 0x10; + else + vrex_used |= REX_R; + + if ((i.reg_operands + i.imm_operands) == i.operands) + { + /* When all operands are registers, the REX_X bit in REX is not + used. We reuse it to encode the upper 16 registers, which is + indicated by the REX_B bit in VREX. The REX_X bit is encoded + as 1's compliment. */ + if ((i.vrex & REX_B)) + { + vrex_used |= REX_B; + i.vex.bytes[1] &= ~0x40; + } + } + + /* EVEX instructions shouldn't need the REX prefix. */ + i.vrex &= ~vrex_used; + gas_assert (i.vrex == 0); + + /* Check the REX.W bit. */ + w = (i.rex & REX_W) ? 1 : 0; + if (i.tm.opcode_modifier.vexw) + { + if (i.tm.opcode_modifier.vexw == VEXW1) + w = 1; + } + /* If w is not set it means we are dealing with WIG instruction. */ + else if (!w) + { + if (evexwig == evexw1) + w = 1; + } + + /* Encode the U bit. */ + implied_prefix |= 0x4; + + /* The third byte of the EVEX prefix. */ + i.vex.bytes[2] = (w << 7 | register_specifier << 3 | implied_prefix); + + /* The fourth byte of the EVEX prefix. */ + /* The zeroing-masking bit. */ + if (i.mask && i.mask->zeroing) + i.vex.bytes[3] |= 0x80; + + /* Don't always set the broadcast bit if there is no RC. */ + if (!i.rounding) + { + /* Encode the vector length. */ + unsigned int vec_length; + + switch (i.tm.opcode_modifier.evex) + { + case EVEXLIG: /* LL' is ignored */ + vec_length = evexlig << 5; + break; + case EVEX128: + vec_length = 0 << 5; + break; + case EVEX256: + vec_length = 1 << 5; + break; + case EVEX512: + vec_length = 2 << 5; + break; + default: + abort (); + break; + } + i.vex.bytes[3] |= vec_length; + /* Encode the broadcast bit. */ + if (i.broadcast) + i.vex.bytes[3] |= 0x10; + } + else + { + if (i.rounding->type != saeonly) + i.vex.bytes[3] |= 0x10 | (i.rounding->type << 5); + else + i.vex.bytes[3] |= 0x10; + } + + if (i.mask && i.mask->mask) + i.vex.bytes[3] |= i.mask->mask->reg_num; +} + +static void +process_immext (void) +{ + expressionS *exp; + + if ((i.tm.cpu_flags.bitfield.cpusse3 || i.tm.cpu_flags.bitfield.cpusvme) + && i.operands > 0) + { + /* MONITOR/MWAIT as well as SVME instructions have fixed operands + with an opcode suffix which is coded in the same place as an + 8-bit immediate field would be. + Here we check those operands and remove them afterwards. */ + unsigned int x; + + for (x = 0; x < i.operands; x++) + if (register_number (i.op[x].regs) != x) + as_bad (_("can't use register '%s%s' as operand %d in '%s'."), + register_prefix, i.op[x].regs->reg_name, x + 1, + i.tm.name); + + i.operands = 0; + } + + /* These AMD 3DNow! and SSE2 instructions have an opcode suffix + which is coded in the same place as an 8-bit immediate field + would be. Here we fake an 8-bit immediate operand from the + opcode suffix stored in tm.extension_opcode. + + AVX instructions also use this encoding, for some of + 3 argument instructions. */ + + gas_assert (i.imm_operands <= 1 + && (i.operands <= 2 + || ((i.tm.opcode_modifier.vex + || i.tm.opcode_modifier.evex) + && i.operands <= 4))); + + exp = &im_expressions[i.imm_operands++]; + i.op[i.operands].imms = exp; + i.types[i.operands] = imm8; + i.operands++; + exp->X_op = O_constant; + exp->X_add_number = i.tm.extension_opcode; + i.tm.extension_opcode = None; +} + + +static int +check_hle (void) +{ + switch (i.tm.opcode_modifier.hleprefixok) + { + default: + abort (); + case HLEPrefixNone: + as_bad (_("invalid instruction `%s' after `%s'"), + i.tm.name, i.hle_prefix); + return 0; + case HLEPrefixLock: + if (i.prefix[LOCK_PREFIX]) + return 1; + as_bad (_("missing `lock' with `%s'"), i.hle_prefix); + return 0; + case HLEPrefixAny: + return 1; + case HLEPrefixRelease: + if (i.prefix[HLE_PREFIX] != XRELEASE_PREFIX_OPCODE) + { + as_bad (_("instruction `%s' after `xacquire' not allowed"), + i.tm.name); + return 0; + } + if (i.mem_operands == 0 + || !operand_type_check (i.types[i.operands - 1], anymem)) + { + as_bad (_("memory destination needed for instruction `%s'" + " after `xrelease'"), i.tm.name); + return 0; + } + return 1; + } +} + +/* This is the guts of the machine-dependent assembler. LINE points to a + machine dependent instruction. This function is supposed to emit + the frags/bytes it assembles to. */ + +void +md_assemble (char *line) +{ + unsigned int j; + char mnemonic[MAX_MNEM_SIZE]; + const insn_template *t; + + /* Initialize globals. */ + memset (&i, '\0', sizeof (i)); + for (j = 0; j < MAX_OPERANDS; j++) + i.reloc[j] = NO_RELOC; + memset (disp_expressions, '\0', sizeof (disp_expressions)); + memset (im_expressions, '\0', sizeof (im_expressions)); + save_stack_p = save_stack; + + /* First parse an instruction mnemonic & call i386_operand for the operands. + We assume that the scrubber has arranged it so that line[0] is the valid + start of a (possibly prefixed) mnemonic. */ + + line = parse_insn (line, mnemonic); + if (line == NULL) + return; + + line = parse_operands (line, mnemonic); + this_operand = -1; + if (line == NULL) + return; + + /* Now we've parsed the mnemonic into a set of templates, and have the + operands at hand. */ + + /* All intel opcodes have reversed operands except for "bound" and + "enter". We also don't reverse intersegment "jmp" and "call" + instructions with 2 immediate operands so that the immediate segment + precedes the offset, as it does when in AT&T mode. */ + if (intel_syntax + && i.operands > 1 + && (strcmp (mnemonic, "bound") != 0) + && (strcmp (mnemonic, "invlpga") != 0) + && !(operand_type_check (i.types[0], imm) + && operand_type_check (i.types[1], imm))) + swap_operands (); + + /* The order of the immediates should be reversed + for 2 immediates extrq and insertq instructions */ + if (i.imm_operands == 2 + && (strcmp (mnemonic, "extrq") == 0 + || strcmp (mnemonic, "insertq") == 0)) + swap_2_operands (0, 1); + + if (i.imm_operands) + optimize_imm (); + + /* Don't optimize displacement for movabs since it only takes 64bit + displacement. */ + if (i.disp_operands + && i.disp_encoding != disp_encoding_32bit + && (flag_code != CODE_64BIT + || strcmp (mnemonic, "movabs") != 0)) + optimize_disp (); + + /* Next, we find a template that matches the given insn, + making sure the overlap of the given operands types is consistent + with the template operand types. */ + + if (!(t = match_template ())) + return; + + if (sse_check != check_none + && !i.tm.opcode_modifier.noavx + && (i.tm.cpu_flags.bitfield.cpusse + || i.tm.cpu_flags.bitfield.cpusse2 + || i.tm.cpu_flags.bitfield.cpusse3 + || i.tm.cpu_flags.bitfield.cpussse3 + || i.tm.cpu_flags.bitfield.cpusse4_1 + || i.tm.cpu_flags.bitfield.cpusse4_2)) + { + (sse_check == check_warning + ? as_warn + : as_bad) (_("SSE instruction `%s' is used"), i.tm.name); + } + + /* Zap movzx and movsx suffix. The suffix has been set from + "word ptr" or "byte ptr" on the source operand in Intel syntax + or extracted from mnemonic in AT&T syntax. But we'll use + the destination register to choose the suffix for encoding. */ + if ((i.tm.base_opcode & ~9) == 0x0fb6) + { + /* In Intel syntax, there must be a suffix. In AT&T syntax, if + there is no suffix, the default will be byte extension. */ + if (i.reg_operands != 2 + && !i.suffix + && intel_syntax) + as_bad (_("ambiguous operand size for `%s'"), i.tm.name); + + i.suffix = 0; + } + + if (i.tm.opcode_modifier.fwait) + if (!add_prefix (FWAIT_OPCODE)) + return; + + /* Check if REP prefix is OK. */ + if (i.rep_prefix && !i.tm.opcode_modifier.repprefixok) + { + as_bad (_("invalid instruction `%s' after `%s'"), + i.tm.name, i.rep_prefix); + return; + } + + /* Check for lock without a lockable instruction. Destination operand + must be memory unless it is xchg (0x86). */ + if (i.prefix[LOCK_PREFIX] + && (!i.tm.opcode_modifier.islockable + || i.mem_operands == 0 + || (i.tm.base_opcode != 0x86 + && !operand_type_check (i.types[i.operands - 1], anymem)))) + { + as_bad (_("expecting lockable instruction after `lock'")); + return; + } + + /* Check if HLE prefix is OK. */ + if (i.hle_prefix && !check_hle ()) + return; + + /* Check BND prefix. */ + if (i.bnd_prefix && !i.tm.opcode_modifier.bndprefixok) + as_bad (_("expecting valid branch instruction after `bnd'")); + + if (i.tm.cpu_flags.bitfield.cpumpx + && flag_code == CODE_64BIT + && i.prefix[ADDR_PREFIX]) + as_bad (_("32-bit address isn't allowed in 64-bit MPX instructions.")); + + /* Insert BND prefix. */ + if (add_bnd_prefix + && i.tm.opcode_modifier.bndprefixok + && !i.prefix[BND_PREFIX]) + add_prefix (BND_PREFIX_OPCODE); + + /* Check string instruction segment overrides. */ + if (i.tm.opcode_modifier.isstring && i.mem_operands != 0) + { + if (!check_string ()) + return; + i.disp_operands = 0; + } + + if (!process_suffix ()) + return; + + /* Update operand types. */ + for (j = 0; j < i.operands; j++) + i.types[j] = operand_type_and (i.types[j], i.tm.operand_types[j]); + + /* Make still unresolved immediate matches conform to size of immediate + given in i.suffix. */ + if (!finalize_imm ()) + return; + + if (i.types[0].bitfield.imm1) + i.imm_operands = 0; /* kludge for shift insns. */ + + /* We only need to check those implicit registers for instructions + with 3 operands or less. */ + if (i.operands <= 3) + for (j = 0; j < i.operands; j++) + if (i.types[j].bitfield.inoutportreg + || i.types[j].bitfield.shiftcount + || i.types[j].bitfield.acc + || i.types[j].bitfield.floatacc) + i.reg_operands--; + + /* ImmExt should be processed after SSE2AVX. */ + if (!i.tm.opcode_modifier.sse2avx + && i.tm.opcode_modifier.immext) + process_immext (); + + /* For insns with operands there are more diddles to do to the opcode. */ + if (i.operands) + { + if (!process_operands ()) + return; + } + else if (!quiet_warnings && i.tm.opcode_modifier.ugh) + { + /* UnixWare fsub no args is alias for fsubp, fadd -> faddp, etc. */ + as_warn (_("translating to `%sp'"), i.tm.name); + } + + if (i.tm.opcode_modifier.vex) + build_vex_prefix (t); + + if (i.tm.opcode_modifier.evex) + build_evex_prefix (); + + /* Handle conversion of 'int $3' --> special int3 insn. XOP or FMA4 + instructions may define INT_OPCODE as well, so avoid this corner + case for those instructions that use MODRM. */ + if (i.tm.base_opcode == INT_OPCODE + && !i.tm.opcode_modifier.modrm + && i.op[0].imms->X_add_number == 3) + { + i.tm.base_opcode = INT3_OPCODE; + i.imm_operands = 0; + } + + if ((i.tm.opcode_modifier.jump + || i.tm.opcode_modifier.jumpbyte + || i.tm.opcode_modifier.jumpdword) + && i.op[0].disps->X_op == O_constant) + { + /* Convert "jmp constant" (and "call constant") to a jump (call) to + the absolute address given by the constant. Since ix86 jumps and + calls are pc relative, we need to generate a reloc. */ + i.op[0].disps->X_add_symbol = &abs_symbol; + i.op[0].disps->X_op = O_symbol; + } + + if (i.tm.opcode_modifier.rex64) + i.rex |= REX_W; + + /* For 8 bit registers we need an empty rex prefix. Also if the + instruction already has a prefix, we need to convert old + registers to new ones. */ + + if ((i.types[0].bitfield.reg8 + && (i.op[0].regs->reg_flags & RegRex64) != 0) + || (i.types[1].bitfield.reg8 + && (i.op[1].regs->reg_flags & RegRex64) != 0) + || ((i.types[0].bitfield.reg8 + || i.types[1].bitfield.reg8) + && i.rex != 0)) + { + int x; + + i.rex |= REX_OPCODE; + for (x = 0; x < 2; x++) + { + /* Look for 8 bit operand that uses old registers. */ + if (i.types[x].bitfield.reg8 + && (i.op[x].regs->reg_flags & RegRex64) == 0) + { + /* In case it is "hi" register, give up. */ + if (i.op[x].regs->reg_num > 3) + as_bad (_("can't encode register '%s%s' in an " + "instruction requiring REX prefix."), + register_prefix, i.op[x].regs->reg_name); + + /* Otherwise it is equivalent to the extended register. + Since the encoding doesn't change this is merely + cosmetic cleanup for debug output. */ + + i.op[x].regs = i.op[x].regs + 8; + } + } + } + + if (i.rex != 0) + add_prefix (REX_OPCODE | i.rex); + + /* We are ready to output the insn. */ + output_insn (); +} + +static char * +parse_insn (char *line, char *mnemonic) +{ + char *l = line; + char *token_start = l; + char *mnem_p; + int supported; + const insn_template *t; + char *dot_p = NULL; + + while (1) + { + mnem_p = mnemonic; + while ((*mnem_p = mnemonic_chars[(unsigned char) *l]) != 0) + { + if (*mnem_p == '.') + dot_p = mnem_p; + mnem_p++; + if (mnem_p >= mnemonic + MAX_MNEM_SIZE) + { + as_bad (_("no such instruction: `%s'"), token_start); + return NULL; + } + l++; + } + if (!is_space_char (*l) + && *l != END_OF_INSN + && (intel_syntax + || (*l != PREFIX_SEPARATOR + && *l != ','))) + { + as_bad (_("invalid character %s in mnemonic"), + output_invalid (*l)); + return NULL; + } + if (token_start == l) + { + if (!intel_syntax && *l == PREFIX_SEPARATOR) + as_bad (_("expecting prefix; got nothing")); + else + as_bad (_("expecting mnemonic; got nothing")); + return NULL; + } + + /* Look up instruction (or prefix) via hash table. */ + current_templates = (const templates *) hash_find (op_hash, mnemonic); + + if (*l != END_OF_INSN + && (!is_space_char (*l) || l[1] != END_OF_INSN) + && current_templates + && current_templates->start->opcode_modifier.isprefix) + { + if (!cpu_flags_check_cpu64 (current_templates->start->cpu_flags)) + { + as_bad ((flag_code != CODE_64BIT + ? _("`%s' is only supported in 64-bit mode") + : _("`%s' is not supported in 64-bit mode")), + current_templates->start->name); + return NULL; + } + /* If we are in 16-bit mode, do not allow addr16 or data16. + Similarly, in 32-bit mode, do not allow addr32 or data32. */ + if ((current_templates->start->opcode_modifier.size16 + || current_templates->start->opcode_modifier.size32) + && flag_code != CODE_64BIT + && (current_templates->start->opcode_modifier.size32 + ^ (flag_code == CODE_16BIT))) + { + as_bad (_("redundant %s prefix"), + current_templates->start->name); + return NULL; + } + /* Add prefix, checking for repeated prefixes. */ + switch (add_prefix (current_templates->start->base_opcode)) + { + case PREFIX_EXIST: + return NULL; + case PREFIX_REP: + if (current_templates->start->cpu_flags.bitfield.cpuhle) + i.hle_prefix = current_templates->start->name; + else if (current_templates->start->cpu_flags.bitfield.cpumpx) + i.bnd_prefix = current_templates->start->name; + else + i.rep_prefix = current_templates->start->name; + break; + default: + break; + } + /* Skip past PREFIX_SEPARATOR and reset token_start. */ + token_start = ++l; + } + else + break; + } + + if (!current_templates) + { + /* Check if we should swap operand or force 32bit displacement in + encoding. */ + if (mnem_p - 2 == dot_p && dot_p[1] == 's') + i.swap_operand = 1; + else if (mnem_p - 3 == dot_p + && dot_p[1] == 'd' + && dot_p[2] == '8') + i.disp_encoding = disp_encoding_8bit; + else if (mnem_p - 4 == dot_p + && dot_p[1] == 'd' + && dot_p[2] == '3' + && dot_p[3] == '2') + i.disp_encoding = disp_encoding_32bit; + else + goto check_suffix; + mnem_p = dot_p; + *dot_p = '\0'; + current_templates = (const templates *) hash_find (op_hash, mnemonic); + } + + if (!current_templates) + { +check_suffix: + /* See if we can get a match by trimming off a suffix. */ + switch (mnem_p[-1]) + { + case WORD_MNEM_SUFFIX: + if (intel_syntax && (intel_float_operand (mnemonic) & 2)) + i.suffix = SHORT_MNEM_SUFFIX; + else + case BYTE_MNEM_SUFFIX: + case QWORD_MNEM_SUFFIX: + i.suffix = mnem_p[-1]; + mnem_p[-1] = '\0'; + current_templates = (const templates *) hash_find (op_hash, + mnemonic); + break; + case SHORT_MNEM_SUFFIX: + case LONG_MNEM_SUFFIX: + if (!intel_syntax) + { + i.suffix = mnem_p[-1]; + mnem_p[-1] = '\0'; + current_templates = (const templates *) hash_find (op_hash, + mnemonic); + } + break; + + /* Intel Syntax. */ + case 'd': + if (intel_syntax) + { + if (intel_float_operand (mnemonic) == 1) + i.suffix = SHORT_MNEM_SUFFIX; + else + i.suffix = LONG_MNEM_SUFFIX; + mnem_p[-1] = '\0'; + current_templates = (const templates *) hash_find (op_hash, + mnemonic); + } + break; + } + if (!current_templates) + { + as_bad (_("no such instruction: `%s'"), token_start); + return NULL; + } + } + + if (current_templates->start->opcode_modifier.jump + || current_templates->start->opcode_modifier.jumpbyte) + { + /* Check for a branch hint. We allow ",pt" and ",pn" for + predict taken and predict not taken respectively. + I'm not sure that branch hints actually do anything on loop + and jcxz insns (JumpByte) for current Pentium4 chips. They + may work in the future and it doesn't hurt to accept them + now. */ + if (l[0] == ',' && l[1] == 'p') + { + if (l[2] == 't') + { + if (!add_prefix (DS_PREFIX_OPCODE)) + return NULL; + l += 3; + } + else if (l[2] == 'n') + { + if (!add_prefix (CS_PREFIX_OPCODE)) + return NULL; + l += 3; + } + } + } + /* Any other comma loses. */ + if (*l == ',') + { + as_bad (_("invalid character %s in mnemonic"), + output_invalid (*l)); + return NULL; + } + + /* Check if instruction is supported on specified architecture. */ + supported = 0; + for (t = current_templates->start; t < current_templates->end; ++t) + { + supported |= cpu_flags_match (t); + if (supported == CPU_FLAGS_PERFECT_MATCH) + goto skip; + } + + if (!(supported & CPU_FLAGS_64BIT_MATCH)) + { + as_bad (flag_code == CODE_64BIT + ? _("`%s' is not supported in 64-bit mode") + : _("`%s' is only supported in 64-bit mode"), + current_templates->start->name); + return NULL; + } + if (supported != CPU_FLAGS_PERFECT_MATCH) + { + as_bad (_("`%s' is not supported on `%s%s'"), + current_templates->start->name, + cpu_arch_name ? cpu_arch_name : default_arch, + cpu_sub_arch_name ? cpu_sub_arch_name : ""); + return NULL; + } + +skip: + if (!cpu_arch_flags.bitfield.cpui386 + && (flag_code != CODE_16BIT)) + { + as_warn (_("use .code16 to ensure correct addressing mode")); + } + + return l; +} + +static char * +parse_operands (char *l, const char *mnemonic) +{ + char *token_start; + + /* 1 if operand is pending after ','. */ + unsigned int expecting_operand = 0; + + /* Non-zero if operand parens not balanced. */ + unsigned int paren_not_balanced; + + while (*l != END_OF_INSN) + { + /* Skip optional white space before operand. */ + if (is_space_char (*l)) + ++l; + if (!is_operand_char (*l) && *l != END_OF_INSN) + { + as_bad (_("invalid character %s before operand %d"), + output_invalid (*l), + i.operands + 1); + return NULL; + } + token_start = l; /* after white space */ + paren_not_balanced = 0; + while (paren_not_balanced || *l != ',') + { + if (*l == END_OF_INSN) + { + if (paren_not_balanced) + { + if (!intel_syntax) + as_bad (_("unbalanced parenthesis in operand %d."), + i.operands + 1); + else + as_bad (_("unbalanced brackets in operand %d."), + i.operands + 1); + return NULL; + } + else + break; /* we are done */ + } + else if (!is_operand_char (*l) && !is_space_char (*l)) + { + as_bad (_("invalid character %s in operand %d"), + output_invalid (*l), + i.operands + 1); + return NULL; + } + if (!intel_syntax) + { + if (*l == '(') + ++paren_not_balanced; + if (*l == ')') + --paren_not_balanced; + } + else + { + if (*l == '[') + ++paren_not_balanced; + if (*l == ']') + --paren_not_balanced; + } + l++; + } + if (l != token_start) + { /* Yes, we've read in another operand. */ + unsigned int operand_ok; + this_operand = i.operands++; + i.types[this_operand].bitfield.unspecified = 1; + if (i.operands > MAX_OPERANDS) + { + as_bad (_("spurious operands; (%d operands/instruction max)"), + MAX_OPERANDS); + return NULL; + } + /* Now parse operand adding info to 'i' as we go along. */ + END_STRING_AND_SAVE (l); + + if (intel_syntax) + operand_ok = + i386_intel_operand (token_start, + intel_float_operand (mnemonic)); + else + operand_ok = i386_att_operand (token_start); + + RESTORE_END_STRING (l); + if (!operand_ok) + return NULL; + } + else + { + if (expecting_operand) + { + expecting_operand_after_comma: + as_bad (_("expecting operand after ','; got nothing")); + return NULL; + } + if (*l == ',') + { + as_bad (_("expecting operand before ','; got nothing")); + return NULL; + } + } + + /* Now *l must be either ',' or END_OF_INSN. */ + if (*l == ',') + { + if (*++l == END_OF_INSN) + { + /* Just skip it, if it's \n complain. */ + goto expecting_operand_after_comma; + } + expecting_operand = 1; + } + } + return l; +} + +static void +swap_2_operands (int xchg1, int xchg2) +{ + union i386_op temp_op; + i386_operand_type temp_type; + enum bfd_reloc_code_real temp_reloc; + + temp_type = i.types[xchg2]; + i.types[xchg2] = i.types[xchg1]; + i.types[xchg1] = temp_type; + temp_op = i.op[xchg2]; + i.op[xchg2] = i.op[xchg1]; + i.op[xchg1] = temp_op; + temp_reloc = i.reloc[xchg2]; + i.reloc[xchg2] = i.reloc[xchg1]; + i.reloc[xchg1] = temp_reloc; + + if (i.mask) + { + if (i.mask->operand == xchg1) + i.mask->operand = xchg2; + else if (i.mask->operand == xchg2) + i.mask->operand = xchg1; + } + if (i.broadcast) + { + if (i.broadcast->operand == xchg1) + i.broadcast->operand = xchg2; + else if (i.broadcast->operand == xchg2) + i.broadcast->operand = xchg1; + } + if (i.rounding) + { + if (i.rounding->operand == xchg1) + i.rounding->operand = xchg2; + else if (i.rounding->operand == xchg2) + i.rounding->operand = xchg1; + } +} + +static void +swap_operands (void) +{ + switch (i.operands) + { + case 5: + case 4: + swap_2_operands (1, i.operands - 2); + case 3: + case 2: + swap_2_operands (0, i.operands - 1); + break; + default: + abort (); + } + + if (i.mem_operands == 2) + { + const seg_entry *temp_seg; + temp_seg = i.seg[0]; + i.seg[0] = i.seg[1]; + i.seg[1] = temp_seg; + } +} + +/* Try to ensure constant immediates are represented in the smallest + opcode possible. */ +static void +optimize_imm (void) +{ + char guess_suffix = 0; + int op; + + if (i.suffix) + guess_suffix = i.suffix; + else if (i.reg_operands) + { + /* Figure out a suffix from the last register operand specified. + We can't do this properly yet, ie. excluding InOutPortReg, + but the following works for instructions with immediates. + In any case, we can't set i.suffix yet. */ + for (op = i.operands; --op >= 0;) + if (i.types[op].bitfield.reg8) + { + guess_suffix = BYTE_MNEM_SUFFIX; + break; + } + else if (i.types[op].bitfield.reg16) + { + guess_suffix = WORD_MNEM_SUFFIX; + break; + } + else if (i.types[op].bitfield.reg32) + { + guess_suffix = LONG_MNEM_SUFFIX; + break; + } + else if (i.types[op].bitfield.reg64) + { + guess_suffix = QWORD_MNEM_SUFFIX; + break; + } + } + else if ((flag_code == CODE_16BIT) ^ (i.prefix[DATA_PREFIX] != 0)) + guess_suffix = WORD_MNEM_SUFFIX; + + for (op = i.operands; --op >= 0;) + if (operand_type_check (i.types[op], imm)) + { + switch (i.op[op].imms->X_op) + { + case O_constant: + /* If a suffix is given, this operand may be shortened. */ + switch (guess_suffix) + { + case LONG_MNEM_SUFFIX: + i.types[op].bitfield.imm32 = 1; + i.types[op].bitfield.imm64 = 1; + break; + case WORD_MNEM_SUFFIX: + i.types[op].bitfield.imm16 = 1; + i.types[op].bitfield.imm32 = 1; + i.types[op].bitfield.imm32s = 1; + i.types[op].bitfield.imm64 = 1; + break; + case BYTE_MNEM_SUFFIX: + i.types[op].bitfield.imm8 = 1; + i.types[op].bitfield.imm8s = 1; + i.types[op].bitfield.imm16 = 1; + i.types[op].bitfield.imm32 = 1; + i.types[op].bitfield.imm32s = 1; + i.types[op].bitfield.imm64 = 1; + break; + } + + /* If this operand is at most 16 bits, convert it + to a signed 16 bit number before trying to see + whether it will fit in an even smaller size. + This allows a 16-bit operand such as $0xffe0 to + be recognised as within Imm8S range. */ + if ((i.types[op].bitfield.imm16) + && (i.op[op].imms->X_add_number & ~(offsetT) 0xffff) == 0) + { + i.op[op].imms->X_add_number = + (((i.op[op].imms->X_add_number & 0xffff) ^ 0x8000) - 0x8000); + } + if ((i.types[op].bitfield.imm32) + && ((i.op[op].imms->X_add_number & ~(((offsetT) 2 << 31) - 1)) + == 0)) + { + i.op[op].imms->X_add_number = ((i.op[op].imms->X_add_number + ^ ((offsetT) 1 << 31)) + - ((offsetT) 1 << 31)); + } + i.types[op] + = operand_type_or (i.types[op], + smallest_imm_type (i.op[op].imms->X_add_number)); + + /* We must avoid matching of Imm32 templates when 64bit + only immediate is available. */ + if (guess_suffix == QWORD_MNEM_SUFFIX) + i.types[op].bitfield.imm32 = 0; + break; + + case O_absent: + case O_register: + abort (); + + /* Symbols and expressions. */ + default: + /* Convert symbolic operand to proper sizes for matching, but don't + prevent matching a set of insns that only supports sizes other + than those matching the insn suffix. */ + { + i386_operand_type mask, allowed; + const insn_template *t; + + operand_type_set (&mask, 0); + operand_type_set (&allowed, 0); + + for (t = current_templates->start; + t < current_templates->end; + ++t) + allowed = operand_type_or (allowed, + t->operand_types[op]); + switch (guess_suffix) + { + case QWORD_MNEM_SUFFIX: + mask.bitfield.imm64 = 1; + mask.bitfield.imm32s = 1; + break; + case LONG_MNEM_SUFFIX: + mask.bitfield.imm32 = 1; + break; + case WORD_MNEM_SUFFIX: + mask.bitfield.imm16 = 1; + break; + case BYTE_MNEM_SUFFIX: + mask.bitfield.imm8 = 1; + break; + default: + break; + } + allowed = operand_type_and (mask, allowed); + if (!operand_type_all_zero (&allowed)) + i.types[op] = operand_type_and (i.types[op], mask); + } + break; + } + } +} + +/* Try to use the smallest displacement type too. */ +static void +optimize_disp (void) +{ + int op; + + for (op = i.operands; --op >= 0;) + if (operand_type_check (i.types[op], disp)) + { + if (i.op[op].disps->X_op == O_constant) + { + offsetT op_disp = i.op[op].disps->X_add_number; + + if (i.types[op].bitfield.disp16 + && (op_disp & ~(offsetT) 0xffff) == 0) + { + /* If this operand is at most 16 bits, convert + to a signed 16 bit number and don't use 64bit + displacement. */ + op_disp = (((op_disp & 0xffff) ^ 0x8000) - 0x8000); + i.types[op].bitfield.disp64 = 0; + } + if (i.types[op].bitfield.disp32 + && (op_disp & ~(((offsetT) 2 << 31) - 1)) == 0) + { + /* If this operand is at most 32 bits, convert + to a signed 32 bit number and don't use 64bit + displacement. */ + op_disp &= (((offsetT) 2 << 31) - 1); + op_disp = (op_disp ^ ((offsetT) 1 << 31)) - ((addressT) 1 << 31); + i.types[op].bitfield.disp64 = 0; + } + if (!op_disp && i.types[op].bitfield.baseindex) + { + i.types[op].bitfield.disp8 = 0; + i.types[op].bitfield.disp16 = 0; + i.types[op].bitfield.disp32 = 0; + i.types[op].bitfield.disp32s = 0; + i.types[op].bitfield.disp64 = 0; + i.op[op].disps = 0; + i.disp_operands--; + } + else if (flag_code == CODE_64BIT) + { + if (fits_in_signed_long (op_disp)) + { + i.types[op].bitfield.disp64 = 0; + i.types[op].bitfield.disp32s = 1; + } + if (i.prefix[ADDR_PREFIX] + && fits_in_unsigned_long (op_disp)) + i.types[op].bitfield.disp32 = 1; + } + if ((i.types[op].bitfield.disp32 + || i.types[op].bitfield.disp32s + || i.types[op].bitfield.disp16) + && fits_in_signed_byte (op_disp)) + i.types[op].bitfield.disp8 = 1; + } + else if (i.reloc[op] == BFD_RELOC_386_TLS_DESC_CALL + || i.reloc[op] == BFD_RELOC_X86_64_TLSDESC_CALL) + { + fix_new_exp (frag_now, frag_more (0) - frag_now->fr_literal, 0, + i.op[op].disps, 0, i.reloc[op]); + i.types[op].bitfield.disp8 = 0; + i.types[op].bitfield.disp16 = 0; + i.types[op].bitfield.disp32 = 0; + i.types[op].bitfield.disp32s = 0; + i.types[op].bitfield.disp64 = 0; + } + else + /* We only support 64bit displacement on constants. */ + i.types[op].bitfield.disp64 = 0; + } +} + +/* Check if operands are valid for the instruction. */ + +static int +check_VecOperands (const insn_template *t) +{ + unsigned int op; + + /* Without VSIB byte, we can't have a vector register for index. */ + if (!t->opcode_modifier.vecsib + && i.index_reg + && (i.index_reg->reg_type.bitfield.regxmm + || i.index_reg->reg_type.bitfield.regymm + || i.index_reg->reg_type.bitfield.regzmm)) + { + i.error = unsupported_vector_index_register; + return 1; + } + + /* Check if default mask is allowed. */ + if (t->opcode_modifier.nodefmask + && (!i.mask || i.mask->mask->reg_num == 0)) + { + i.error = no_default_mask; + return 1; + } + + /* For VSIB byte, we need a vector register for index, and all vector + registers must be distinct. */ + if (t->opcode_modifier.vecsib) + { + if (!i.index_reg + || !((t->opcode_modifier.vecsib == VecSIB128 + && i.index_reg->reg_type.bitfield.regxmm) + || (t->opcode_modifier.vecsib == VecSIB256 + && i.index_reg->reg_type.bitfield.regymm) + || (t->opcode_modifier.vecsib == VecSIB512 + && i.index_reg->reg_type.bitfield.regzmm))) + { + i.error = invalid_vsib_address; + return 1; + } + + gas_assert (i.reg_operands == 2 || i.mask); + if (i.reg_operands == 2 && !i.mask) + { + gas_assert (i.types[0].bitfield.regxmm + || i.types[0].bitfield.regymm + || i.types[0].bitfield.regzmm); + gas_assert (i.types[2].bitfield.regxmm + || i.types[2].bitfield.regymm + || i.types[2].bitfield.regzmm); + if (operand_check == check_none) + return 0; + if (register_number (i.op[0].regs) + != register_number (i.index_reg) + && register_number (i.op[2].regs) + != register_number (i.index_reg) + && register_number (i.op[0].regs) + != register_number (i.op[2].regs)) + return 0; + if (operand_check == check_error) + { + i.error = invalid_vector_register_set; + return 1; + } + as_warn (_("mask, index, and destination registers should be distinct")); + } + } + + /* Check if broadcast is supported by the instruction and is applied + to the memory operand. */ + if (i.broadcast) + { + int broadcasted_opnd_size; + + /* Check if specified broadcast is supported in this instruction, + and it's applied to memory operand of DWORD or QWORD type, + depending on VecESize. */ + if (i.broadcast->type != t->opcode_modifier.broadcast + || !i.types[i.broadcast->operand].bitfield.mem + || (t->opcode_modifier.vecesize == 0 + && !i.types[i.broadcast->operand].bitfield.dword + && !i.types[i.broadcast->operand].bitfield.unspecified) + || (t->opcode_modifier.vecesize == 1 + && !i.types[i.broadcast->operand].bitfield.qword + && !i.types[i.broadcast->operand].bitfield.unspecified)) + goto bad_broadcast; + + broadcasted_opnd_size = t->opcode_modifier.vecesize ? 64 : 32; + if (i.broadcast->type == BROADCAST_1TO16) + broadcasted_opnd_size <<= 4; /* Broadcast 1to16. */ + else if (i.broadcast->type == BROADCAST_1TO8) + broadcasted_opnd_size <<= 3; /* Broadcast 1to8. */ + else + goto bad_broadcast; + + if ((broadcasted_opnd_size == 256 + && !t->operand_types[i.broadcast->operand].bitfield.ymmword) + || (broadcasted_opnd_size == 512 + && !t->operand_types[i.broadcast->operand].bitfield.zmmword)) + { + bad_broadcast: + i.error = unsupported_broadcast; + return 1; + } + } + /* If broadcast is supported in this instruction, we need to check if + operand of one-element size isn't specified without broadcast. */ + else if (t->opcode_modifier.broadcast && i.mem_operands) + { + /* Find memory operand. */ + for (op = 0; op < i.operands; op++) + if (operand_type_check (i.types[op], anymem)) + break; + gas_assert (op < i.operands); + /* Check size of the memory operand. */ + if ((t->opcode_modifier.vecesize == 0 + && i.types[op].bitfield.dword) + || (t->opcode_modifier.vecesize == 1 + && i.types[op].bitfield.qword)) + { + i.error = broadcast_needed; + return 1; + } + } + + /* Check if requested masking is supported. */ + if (i.mask + && (!t->opcode_modifier.masking + || (i.mask->zeroing + && t->opcode_modifier.masking == MERGING_MASKING))) + { + i.error = unsupported_masking; + return 1; + } + + /* Check if masking is applied to dest operand. */ + if (i.mask && (i.mask->operand != (int) (i.operands - 1))) + { + i.error = mask_not_on_destination; + return 1; + } + + /* Check RC/SAE. */ + if (i.rounding) + { + if ((i.rounding->type != saeonly + && !t->opcode_modifier.staticrounding) + || (i.rounding->type == saeonly + && (t->opcode_modifier.staticrounding + || !t->opcode_modifier.sae))) + { + i.error = unsupported_rc_sae; + return 1; + } + /* If the instruction has several immediate operands and one of + them is rounding, the rounding operand should be the last + immediate operand. */ + if (i.imm_operands > 1 + && i.rounding->operand != (int) (i.imm_operands - 1)) + { + i.error = rc_sae_operand_not_last_imm; + return 1; + } + } + + /* Check vector Disp8 operand. */ + if (t->opcode_modifier.disp8memshift) + { + if (i.broadcast) + i.memshift = t->opcode_modifier.vecesize ? 3 : 2; + else + i.memshift = t->opcode_modifier.disp8memshift; + + for (op = 0; op < i.operands; op++) + if (operand_type_check (i.types[op], disp) + && i.op[op].disps->X_op == O_constant) + { + offsetT value = i.op[op].disps->X_add_number; + int vec_disp8_ok = fits_in_vec_disp8 (value); + if (t->operand_types [op].bitfield.vec_disp8) + { + if (vec_disp8_ok) + i.types[op].bitfield.vec_disp8 = 1; + else + { + /* Vector insn can only have Vec_Disp8/Disp32 in + 32/64bit modes, and Vec_Disp8/Disp16 in 16bit + mode. */ + i.types[op].bitfield.disp8 = 0; + if (flag_code != CODE_16BIT) + i.types[op].bitfield.disp16 = 0; + } + } + else if (flag_code != CODE_16BIT) + { + /* One form of this instruction supports vector Disp8. + Try vector Disp8 if we need to use Disp32. */ + if (vec_disp8_ok && !fits_in_signed_byte (value)) + { + i.error = try_vector_disp8; + return 1; + } + } + } + } + else + i.memshift = -1; + + return 0; +} + +/* Check if operands are valid for the instruction. Update VEX + operand types. */ + +static int +VEX_check_operands (const insn_template *t) +{ + /* VREX is only valid with EVEX prefix. */ + if (i.need_vrex && !t->opcode_modifier.evex) + { + i.error = invalid_register_operand; + return 1; + } + + if (!t->opcode_modifier.vex) + return 0; + + /* Only check VEX_Imm4, which must be the first operand. */ + if (t->operand_types[0].bitfield.vec_imm4) + { + if (i.op[0].imms->X_op != O_constant + || !fits_in_imm4 (i.op[0].imms->X_add_number)) + { + i.error = bad_imm4; + return 1; + } + + /* Turn off Imm8 so that update_imm won't complain. */ + i.types[0] = vec_imm4; + } + + return 0; +} + +static const insn_template * +match_template (void) +{ + /* Points to template once we've found it. */ + const insn_template *t; + i386_operand_type overlap0, overlap1, overlap2, overlap3; + i386_operand_type overlap4; + unsigned int found_reverse_match; + i386_opcode_modifier suffix_check; + i386_operand_type operand_types [MAX_OPERANDS]; + int addr_prefix_disp; + unsigned int j; + unsigned int found_cpu_match; + unsigned int check_register; + enum i386_error specific_error = 0; + +#if MAX_OPERANDS != 5 +# error "MAX_OPERANDS must be 5." +#endif + + found_reverse_match = 0; + addr_prefix_disp = -1; + + memset (&suffix_check, 0, sizeof (suffix_check)); + if (i.suffix == BYTE_MNEM_SUFFIX) + suffix_check.no_bsuf = 1; + else if (i.suffix == WORD_MNEM_SUFFIX) + suffix_check.no_wsuf = 1; + else if (i.suffix == SHORT_MNEM_SUFFIX) + suffix_check.no_ssuf = 1; + else if (i.suffix == LONG_MNEM_SUFFIX) + suffix_check.no_lsuf = 1; + else if (i.suffix == QWORD_MNEM_SUFFIX) + suffix_check.no_qsuf = 1; + else if (i.suffix == LONG_DOUBLE_MNEM_SUFFIX) + suffix_check.no_ldsuf = 1; + + /* Must have right number of operands. */ + i.error = number_of_operands_mismatch; + + for (t = current_templates->start; t < current_templates->end; t++) + { + addr_prefix_disp = -1; + + if (i.operands != t->operands) + continue; + + /* Check processor support. */ + i.error = unsupported; + found_cpu_match = (cpu_flags_match (t) + == CPU_FLAGS_PERFECT_MATCH); + if (!found_cpu_match) + continue; + + /* Check old gcc support. */ + i.error = old_gcc_only; + if (!old_gcc && t->opcode_modifier.oldgcc) + continue; + + /* Check AT&T mnemonic. */ + i.error = unsupported_with_intel_mnemonic; + if (intel_mnemonic && t->opcode_modifier.attmnemonic) + continue; + + /* Check AT&T/Intel syntax. */ + i.error = unsupported_syntax; + if ((intel_syntax && t->opcode_modifier.attsyntax) + || (!intel_syntax && t->opcode_modifier.intelsyntax)) + continue; + + /* Check the suffix, except for some instructions in intel mode. */ + i.error = invalid_instruction_suffix; + if ((!intel_syntax || !t->opcode_modifier.ignoresize) + && ((t->opcode_modifier.no_bsuf && suffix_check.no_bsuf) + || (t->opcode_modifier.no_wsuf && suffix_check.no_wsuf) + || (t->opcode_modifier.no_lsuf && suffix_check.no_lsuf) + || (t->opcode_modifier.no_ssuf && suffix_check.no_ssuf) + || (t->opcode_modifier.no_qsuf && suffix_check.no_qsuf) + || (t->opcode_modifier.no_ldsuf && suffix_check.no_ldsuf))) + continue; + + if (!operand_size_match (t)) + continue; + + for (j = 0; j < MAX_OPERANDS; j++) + operand_types[j] = t->operand_types[j]; + + /* In general, don't allow 64-bit operands in 32-bit mode. */ + if (i.suffix == QWORD_MNEM_SUFFIX + && flag_code != CODE_64BIT + && (intel_syntax + ? (!t->opcode_modifier.ignoresize + && !intel_float_operand (t->name)) + : intel_float_operand (t->name) != 2) + && ((!operand_types[0].bitfield.regmmx + && !operand_types[0].bitfield.regxmm + && !operand_types[0].bitfield.regymm + && !operand_types[0].bitfield.regzmm) + || (!operand_types[t->operands > 1].bitfield.regmmx + && !!operand_types[t->operands > 1].bitfield.regxmm + && !!operand_types[t->operands > 1].bitfield.regymm + && !!operand_types[t->operands > 1].bitfield.regzmm)) + && (t->base_opcode != 0x0fc7 + || t->extension_opcode != 1 /* cmpxchg8b */)) + continue; + + /* In general, don't allow 32-bit operands on pre-386. */ + else if (i.suffix == LONG_MNEM_SUFFIX + && !cpu_arch_flags.bitfield.cpui386 + && (intel_syntax + ? (!t->opcode_modifier.ignoresize + && !intel_float_operand (t->name)) + : intel_float_operand (t->name) != 2) + && ((!operand_types[0].bitfield.regmmx + && !operand_types[0].bitfield.regxmm) + || (!operand_types[t->operands > 1].bitfield.regmmx + && !!operand_types[t->operands > 1].bitfield.regxmm))) + continue; + + /* Do not verify operands when there are none. */ + else + { + if (!t->operands) + /* We've found a match; break out of loop. */ + break; + } + + /* Address size prefix will turn Disp64/Disp32/Disp16 operand + into Disp32/Disp16/Disp32 operand. */ + if (i.prefix[ADDR_PREFIX] != 0) + { + /* There should be only one Disp operand. */ + switch (flag_code) + { + case CODE_16BIT: + for (j = 0; j < MAX_OPERANDS; j++) + { + if (operand_types[j].bitfield.disp16) + { + addr_prefix_disp = j; + operand_types[j].bitfield.disp32 = 1; + operand_types[j].bitfield.disp16 = 0; + break; + } + } + break; + case CODE_32BIT: + for (j = 0; j < MAX_OPERANDS; j++) + { + if (operand_types[j].bitfield.disp32) + { + addr_prefix_disp = j; + operand_types[j].bitfield.disp32 = 0; + operand_types[j].bitfield.disp16 = 1; + break; + } + } + break; + case CODE_64BIT: + for (j = 0; j < MAX_OPERANDS; j++) + { + if (operand_types[j].bitfield.disp64) + { + addr_prefix_disp = j; + operand_types[j].bitfield.disp64 = 0; + operand_types[j].bitfield.disp32 = 1; + break; + } + } + break; + } + } + + /* We check register size if needed. */ + check_register = t->opcode_modifier.checkregsize; + overlap0 = operand_type_and (i.types[0], operand_types[0]); + switch (t->operands) + { + case 1: + if (!operand_type_match (overlap0, i.types[0])) + continue; + break; + case 2: + /* xchg %eax, %eax is a special case. It is an aliase for nop + only in 32bit mode and we can use opcode 0x90. In 64bit + mode, we can't use 0x90 for xchg %eax, %eax since it should + zero-extend %eax to %rax. */ + if (flag_code == CODE_64BIT + && t->base_opcode == 0x90 + && operand_type_equal (&i.types [0], &acc32) + && operand_type_equal (&i.types [1], &acc32)) + continue; + if (i.swap_operand) + { + /* If we swap operand in encoding, we either match + the next one or reverse direction of operands. */ + if (t->opcode_modifier.s) + continue; + else if (t->opcode_modifier.d) + goto check_reverse; + } + + case 3: + /* If we swap operand in encoding, we match the next one. */ + if (i.swap_operand && t->opcode_modifier.s) + continue; + case 4: + case 5: + overlap1 = operand_type_and (i.types[1], operand_types[1]); + if (!operand_type_match (overlap0, i.types[0]) + || !operand_type_match (overlap1, i.types[1]) + || (check_register + && !operand_type_register_match (overlap0, i.types[0], + operand_types[0], + overlap1, i.types[1], + operand_types[1]))) + { + /* Check if other direction is valid ... */ + if (!t->opcode_modifier.d && !t->opcode_modifier.floatd) + continue; + +check_reverse: + /* Try reversing direction of operands. */ + overlap0 = operand_type_and (i.types[0], operand_types[1]); + overlap1 = operand_type_and (i.types[1], operand_types[0]); + if (!operand_type_match (overlap0, i.types[0]) + || !operand_type_match (overlap1, i.types[1]) + || (check_register + && !operand_type_register_match (overlap0, + i.types[0], + operand_types[1], + overlap1, + i.types[1], + operand_types[0]))) + { + /* Does not match either direction. */ + continue; + } + /* found_reverse_match holds which of D or FloatDR + we've found. */ + if (t->opcode_modifier.d) + found_reverse_match = Opcode_D; + else if (t->opcode_modifier.floatd) + found_reverse_match = Opcode_FloatD; + else + found_reverse_match = 0; + if (t->opcode_modifier.floatr) + found_reverse_match |= Opcode_FloatR; + } + else + { + /* Found a forward 2 operand match here. */ + switch (t->operands) + { + case 5: + overlap4 = operand_type_and (i.types[4], + operand_types[4]); + case 4: + overlap3 = operand_type_and (i.types[3], + operand_types[3]); + case 3: + overlap2 = operand_type_and (i.types[2], + operand_types[2]); + break; + } + + switch (t->operands) + { + case 5: + if (!operand_type_match (overlap4, i.types[4]) + || !operand_type_register_match (overlap3, + i.types[3], + operand_types[3], + overlap4, + i.types[4], + operand_types[4])) + continue; + case 4: + if (!operand_type_match (overlap3, i.types[3]) + || (check_register + && !operand_type_register_match (overlap2, + i.types[2], + operand_types[2], + overlap3, + i.types[3], + operand_types[3]))) + continue; + case 3: + /* Here we make use of the fact that there are no + reverse match 3 operand instructions, and all 3 + operand instructions only need to be checked for + register consistency between operands 2 and 3. */ + if (!operand_type_match (overlap2, i.types[2]) + || (check_register + && !operand_type_register_match (overlap1, + i.types[1], + operand_types[1], + overlap2, + i.types[2], + operand_types[2]))) + continue; + break; + } + } + /* Found either forward/reverse 2, 3 or 4 operand match here: + slip through to break. */ + } + if (!found_cpu_match) + { + found_reverse_match = 0; + continue; + } + + /* Check if vector and VEX operands are valid. */ + if (check_VecOperands (t) || VEX_check_operands (t)) + { + specific_error = i.error; + continue; + } + + /* We've found a match; break out of loop. */ + break; + } + + if (t == current_templates->end) + { + /* We found no match. */ + const char *err_msg; + switch (specific_error ? specific_error : i.error) + { + default: + abort (); + case operand_size_mismatch: + err_msg = _("operand size mismatch"); + break; + case operand_type_mismatch: + err_msg = _("operand type mismatch"); + break; + case register_type_mismatch: + err_msg = _("register type mismatch"); + break; + case number_of_operands_mismatch: + err_msg = _("number of operands mismatch"); + break; + case invalid_instruction_suffix: + err_msg = _("invalid instruction suffix"); + break; + case bad_imm4: + err_msg = _("constant doesn't fit in 4 bits"); + break; + case old_gcc_only: + err_msg = _("only supported with old gcc"); + break; + case unsupported_with_intel_mnemonic: + err_msg = _("unsupported with Intel mnemonic"); + break; + case unsupported_syntax: + err_msg = _("unsupported syntax"); + break; + case unsupported: + as_bad (_("unsupported instruction `%s'"), + current_templates->start->name); + return NULL; + case invalid_vsib_address: + err_msg = _("invalid VSIB address"); + break; + case invalid_vector_register_set: + err_msg = _("mask, index, and destination registers must be distinct"); + break; + case unsupported_vector_index_register: + err_msg = _("unsupported vector index register"); + break; + case unsupported_broadcast: + err_msg = _("unsupported broadcast"); + break; + case broadcast_not_on_src_operand: + err_msg = _("broadcast not on source memory operand"); + break; + case broadcast_needed: + err_msg = _("broadcast is needed for operand of such type"); + break; + case unsupported_masking: + err_msg = _("unsupported masking"); + break; + case mask_not_on_destination: + err_msg = _("mask not on destination operand"); + break; + case no_default_mask: + err_msg = _("default mask isn't allowed"); + break; + case unsupported_rc_sae: + err_msg = _("unsupported static rounding/sae"); + break; + case rc_sae_operand_not_last_imm: + if (intel_syntax) + err_msg = _("RC/SAE operand must precede immediate operands"); + else + err_msg = _("RC/SAE operand must follow immediate operands"); + break; + case invalid_register_operand: + err_msg = _("invalid register operand"); + break; + } + as_bad (_("%s for `%s'"), err_msg, + current_templates->start->name); + return NULL; + } + + if (!quiet_warnings) + { + if (!intel_syntax + && (i.types[0].bitfield.jumpabsolute + != operand_types[0].bitfield.jumpabsolute)) + { + as_warn (_("indirect %s without `*'"), t->name); + } + + if (t->opcode_modifier.isprefix + && t->opcode_modifier.ignoresize) + { + /* Warn them that a data or address size prefix doesn't + affect assembly of the next line of code. */ + as_warn (_("stand-alone `%s' prefix"), t->name); + } + } + + /* Copy the template we found. */ + i.tm = *t; + + if (addr_prefix_disp != -1) + i.tm.operand_types[addr_prefix_disp] + = operand_types[addr_prefix_disp]; + + if (found_reverse_match) + { + /* If we found a reverse match we must alter the opcode + direction bit. found_reverse_match holds bits to change + (different for int & float insns). */ + + i.tm.base_opcode ^= found_reverse_match; + + i.tm.operand_types[0] = operand_types[1]; + i.tm.operand_types[1] = operand_types[0]; + } + + return t; +} + +static int +check_string (void) +{ + int mem_op = operand_type_check (i.types[0], anymem) ? 0 : 1; + if (i.tm.operand_types[mem_op].bitfield.esseg) + { + if (i.seg[0] != NULL && i.seg[0] != &es) + { + as_bad (_("`%s' operand %d must use `%ses' segment"), + i.tm.name, + mem_op + 1, + register_prefix); + return 0; + } + /* There's only ever one segment override allowed per instruction. + This instruction possibly has a legal segment override on the + second operand, so copy the segment to where non-string + instructions store it, allowing common code. */ + i.seg[0] = i.seg[1]; + } + else if (i.tm.operand_types[mem_op + 1].bitfield.esseg) + { + if (i.seg[1] != NULL && i.seg[1] != &es) + { + as_bad (_("`%s' operand %d must use `%ses' segment"), + i.tm.name, + mem_op + 2, + register_prefix); + return 0; + } + } + return 1; +} + +static int +process_suffix (void) +{ + /* If matched instruction specifies an explicit instruction mnemonic + suffix, use it. */ + if (i.tm.opcode_modifier.size16) + i.suffix = WORD_MNEM_SUFFIX; + else if (i.tm.opcode_modifier.size32) + i.suffix = LONG_MNEM_SUFFIX; + else if (i.tm.opcode_modifier.size64) + i.suffix = QWORD_MNEM_SUFFIX; + else if (i.reg_operands) + { + /* If there's no instruction mnemonic suffix we try to invent one + based on register operands. */ + if (!i.suffix) + { + /* We take i.suffix from the last register operand specified, + Destination register type is more significant than source + register type. crc32 in SSE4.2 prefers source register + type. */ + if (i.tm.base_opcode == 0xf20f38f1) + { + if (i.types[0].bitfield.reg16) + i.suffix = WORD_MNEM_SUFFIX; + else if (i.types[0].bitfield.reg32) + i.suffix = LONG_MNEM_SUFFIX; + else if (i.types[0].bitfield.reg64) + i.suffix = QWORD_MNEM_SUFFIX; + } + else if (i.tm.base_opcode == 0xf20f38f0) + { + if (i.types[0].bitfield.reg8) + i.suffix = BYTE_MNEM_SUFFIX; + } + + if (!i.suffix) + { + int op; + + if (i.tm.base_opcode == 0xf20f38f1 + || i.tm.base_opcode == 0xf20f38f0) + { + /* We have to know the operand size for crc32. */ + as_bad (_("ambiguous memory operand size for `%s`"), + i.tm.name); + return 0; + } + + for (op = i.operands; --op >= 0;) + if (!i.tm.operand_types[op].bitfield.inoutportreg) + { + if (i.types[op].bitfield.reg8) + { + i.suffix = BYTE_MNEM_SUFFIX; + break; + } + else if (i.types[op].bitfield.reg16) + { + i.suffix = WORD_MNEM_SUFFIX; + break; + } + else if (i.types[op].bitfield.reg32) + { + i.suffix = LONG_MNEM_SUFFIX; + break; + } + else if (i.types[op].bitfield.reg64) + { + i.suffix = QWORD_MNEM_SUFFIX; + break; + } + } + } + } + else if (i.suffix == BYTE_MNEM_SUFFIX) + { + if (intel_syntax + && i.tm.opcode_modifier.ignoresize + && i.tm.opcode_modifier.no_bsuf) + i.suffix = 0; + else if (!check_byte_reg ()) + return 0; + } + else if (i.suffix == LONG_MNEM_SUFFIX) + { + if (intel_syntax + && i.tm.opcode_modifier.ignoresize + && i.tm.opcode_modifier.no_lsuf) + i.suffix = 0; + else if (!check_long_reg ()) + return 0; + } + else if (i.suffix == QWORD_MNEM_SUFFIX) + { + if (intel_syntax + && i.tm.opcode_modifier.ignoresize + && i.tm.opcode_modifier.no_qsuf) + i.suffix = 0; + else if (!check_qword_reg ()) + return 0; + } + else if (i.suffix == WORD_MNEM_SUFFIX) + { + if (intel_syntax + && i.tm.opcode_modifier.ignoresize + && i.tm.opcode_modifier.no_wsuf) + i.suffix = 0; + else if (!check_word_reg ()) + return 0; + } + else if (i.suffix == XMMWORD_MNEM_SUFFIX + || i.suffix == YMMWORD_MNEM_SUFFIX + || i.suffix == ZMMWORD_MNEM_SUFFIX) + { + /* Skip if the instruction has x/y/z suffix. match_template + should check if it is a valid suffix. */ + } + else if (intel_syntax && i.tm.opcode_modifier.ignoresize) + /* Do nothing if the instruction is going to ignore the prefix. */ + ; + else + abort (); + } + else if (i.tm.opcode_modifier.defaultsize + && !i.suffix + /* exclude fldenv/frstor/fsave/fstenv */ + && i.tm.opcode_modifier.no_ssuf) + { + i.suffix = stackop_size; + } + else if (intel_syntax + && !i.suffix + && (i.tm.operand_types[0].bitfield.jumpabsolute + || i.tm.opcode_modifier.jumpbyte + || i.tm.opcode_modifier.jumpintersegment + || (i.tm.base_opcode == 0x0f01 /* [ls][gi]dt */ + && i.tm.extension_opcode <= 3))) + { + switch (flag_code) + { + case CODE_64BIT: + if (!i.tm.opcode_modifier.no_qsuf) + { + i.suffix = QWORD_MNEM_SUFFIX; + break; + } + case CODE_32BIT: + if (!i.tm.opcode_modifier.no_lsuf) + i.suffix = LONG_MNEM_SUFFIX; + break; + case CODE_16BIT: + if (!i.tm.opcode_modifier.no_wsuf) + i.suffix = WORD_MNEM_SUFFIX; + break; + } + } + + if (!i.suffix) + { + if (!intel_syntax) + { + if (i.tm.opcode_modifier.w) + { + as_bad (_("no instruction mnemonic suffix given and " + "no register operands; can't size instruction")); + return 0; + } + } + else + { + unsigned int suffixes; + + suffixes = !i.tm.opcode_modifier.no_bsuf; + if (!i.tm.opcode_modifier.no_wsuf) + suffixes |= 1 << 1; + if (!i.tm.opcode_modifier.no_lsuf) + suffixes |= 1 << 2; + if (!i.tm.opcode_modifier.no_ldsuf) + suffixes |= 1 << 3; + if (!i.tm.opcode_modifier.no_ssuf) + suffixes |= 1 << 4; + if (!i.tm.opcode_modifier.no_qsuf) + suffixes |= 1 << 5; + + /* There are more than suffix matches. */ + if (i.tm.opcode_modifier.w + || ((suffixes & (suffixes - 1)) + && !i.tm.opcode_modifier.defaultsize + && !i.tm.opcode_modifier.ignoresize)) + { + as_bad (_("ambiguous operand size for `%s'"), i.tm.name); + return 0; + } + } + } + + /* Change the opcode based on the operand size given by i.suffix; + We don't need to change things for byte insns. */ + + if (i.suffix + && i.suffix != BYTE_MNEM_SUFFIX + && i.suffix != XMMWORD_MNEM_SUFFIX + && i.suffix != YMMWORD_MNEM_SUFFIX + && i.suffix != ZMMWORD_MNEM_SUFFIX) + { + /* It's not a byte, select word/dword operation. */ + if (i.tm.opcode_modifier.w) + { + if (i.tm.opcode_modifier.shortform) + i.tm.base_opcode |= 8; + else + i.tm.base_opcode |= 1; + } + + /* Now select between word & dword operations via the operand + size prefix, except for instructions that will ignore this + prefix anyway. */ + if (i.tm.opcode_modifier.addrprefixop0) + { + /* The address size override prefix changes the size of the + first operand. */ + if ((flag_code == CODE_32BIT + && i.op->regs[0].reg_type.bitfield.reg16) + || (flag_code != CODE_32BIT + && i.op->regs[0].reg_type.bitfield.reg32)) + if (!add_prefix (ADDR_PREFIX_OPCODE)) + return 0; + } + else if (i.suffix != QWORD_MNEM_SUFFIX + && i.suffix != LONG_DOUBLE_MNEM_SUFFIX + && !i.tm.opcode_modifier.ignoresize + && !i.tm.opcode_modifier.floatmf + && ((i.suffix == LONG_MNEM_SUFFIX) == (flag_code == CODE_16BIT) + || (flag_code == CODE_64BIT + && i.tm.opcode_modifier.jumpbyte))) + { + unsigned int prefix = DATA_PREFIX_OPCODE; + + if (i.tm.opcode_modifier.jumpbyte) /* jcxz, loop */ + prefix = ADDR_PREFIX_OPCODE; + + if (!add_prefix (prefix)) + return 0; + } + + /* Set mode64 for an operand. */ + if (i.suffix == QWORD_MNEM_SUFFIX + && flag_code == CODE_64BIT + && !i.tm.opcode_modifier.norex64) + { + /* Special case for xchg %rax,%rax. It is NOP and doesn't + need rex64. cmpxchg8b is also a special case. */ + if (! (i.operands == 2 + && i.tm.base_opcode == 0x90 + && i.tm.extension_opcode == None + && operand_type_equal (&i.types [0], &acc64) + && operand_type_equal (&i.types [1], &acc64)) + && ! (i.operands == 1 + && i.tm.base_opcode == 0xfc7 + && i.tm.extension_opcode == 1 + && !operand_type_check (i.types [0], reg) + && operand_type_check (i.types [0], anymem))) + i.rex |= REX_W; + } + + /* Size floating point instruction. */ + if (i.suffix == LONG_MNEM_SUFFIX) + if (i.tm.opcode_modifier.floatmf) + i.tm.base_opcode ^= 4; + } + + return 1; +} + +static int +check_byte_reg (void) +{ + int op; + + for (op = i.operands; --op >= 0;) + { + /* If this is an eight bit register, it's OK. If it's the 16 or + 32 bit version of an eight bit register, we will just use the + low portion, and that's OK too. */ + if (i.types[op].bitfield.reg8) + continue; + + /* I/O port address operands are OK too. */ + if (i.tm.operand_types[op].bitfield.inoutportreg) + continue; + + /* crc32 doesn't generate this warning. */ + if (i.tm.base_opcode == 0xf20f38f0) + continue; + + if ((i.types[op].bitfield.reg16 + || i.types[op].bitfield.reg32 + || i.types[op].bitfield.reg64) + && i.op[op].regs->reg_num < 4 + /* Prohibit these changes in 64bit mode, since the lowering + would be more complicated. */ + && flag_code != CODE_64BIT) + { +#if REGISTER_WARNINGS + if (!quiet_warnings) + as_warn (_("using `%s%s' instead of `%s%s' due to `%c' suffix"), + register_prefix, + (i.op[op].regs + (i.types[op].bitfield.reg16 + ? REGNAM_AL - REGNAM_AX + : REGNAM_AL - REGNAM_EAX))->reg_name, + register_prefix, + i.op[op].regs->reg_name, + i.suffix); +#endif + continue; + } + /* Any other register is bad. */ + if (i.types[op].bitfield.reg16 + || i.types[op].bitfield.reg32 + || i.types[op].bitfield.reg64 + || i.types[op].bitfield.regmmx + || i.types[op].bitfield.regxmm + || i.types[op].bitfield.regymm + || i.types[op].bitfield.regzmm + || i.types[op].bitfield.sreg2 + || i.types[op].bitfield.sreg3 + || i.types[op].bitfield.control + || i.types[op].bitfield.debug + || i.types[op].bitfield.test + || i.types[op].bitfield.floatreg + || i.types[op].bitfield.floatacc) + { + as_bad (_("`%s%s' not allowed with `%s%c'"), + register_prefix, + i.op[op].regs->reg_name, + i.tm.name, + i.suffix); + return 0; + } + } + return 1; +} + +static int +check_long_reg (void) +{ + int op; + + for (op = i.operands; --op >= 0;) + /* Reject eight bit registers, except where the template requires + them. (eg. movzb) */ + if (i.types[op].bitfield.reg8 + && (i.tm.operand_types[op].bitfield.reg16 + || i.tm.operand_types[op].bitfield.reg32 + || i.tm.operand_types[op].bitfield.acc)) + { + as_bad (_("`%s%s' not allowed with `%s%c'"), + register_prefix, + i.op[op].regs->reg_name, + i.tm.name, + i.suffix); + return 0; + } + /* Warn if the e prefix on a general reg is missing. */ + else if ((!quiet_warnings || flag_code == CODE_64BIT) + && i.types[op].bitfield.reg16 + && (i.tm.operand_types[op].bitfield.reg32 + || i.tm.operand_types[op].bitfield.acc)) + { + /* Prohibit these changes in the 64bit mode, since the + lowering is more complicated. */ + if (flag_code == CODE_64BIT) + { + as_bad (_("incorrect register `%s%s' used with `%c' suffix"), + register_prefix, i.op[op].regs->reg_name, + i.suffix); + return 0; + } +#if REGISTER_WARNINGS + else + as_warn (_("using `%s%s' instead of `%s%s' due to `%c' suffix"), + register_prefix, + (i.op[op].regs + REGNAM_EAX - REGNAM_AX)->reg_name, + register_prefix, + i.op[op].regs->reg_name, + i.suffix); +#endif + } + /* Warn if the r prefix on a general reg is missing. */ + else if (i.types[op].bitfield.reg64 + && (i.tm.operand_types[op].bitfield.reg32 + || i.tm.operand_types[op].bitfield.acc)) + { + if (intel_syntax + && i.tm.opcode_modifier.toqword + && !i.types[0].bitfield.regxmm) + { + /* Convert to QWORD. We want REX byte. */ + i.suffix = QWORD_MNEM_SUFFIX; + } + else + { + as_bad (_("incorrect register `%s%s' used with `%c' suffix"), + register_prefix, i.op[op].regs->reg_name, + i.suffix); + return 0; + } + } + return 1; +} + +static int +check_qword_reg (void) +{ + int op; + + for (op = i.operands; --op >= 0; ) + /* Reject eight bit registers, except where the template requires + them. (eg. movzb) */ + if (i.types[op].bitfield.reg8 + && (i.tm.operand_types[op].bitfield.reg16 + || i.tm.operand_types[op].bitfield.reg32 + || i.tm.operand_types[op].bitfield.acc)) + { + as_bad (_("`%s%s' not allowed with `%s%c'"), + register_prefix, + i.op[op].regs->reg_name, + i.tm.name, + i.suffix); + return 0; + } + /* Warn if the e prefix on a general reg is missing. */ + else if ((i.types[op].bitfield.reg16 + || i.types[op].bitfield.reg32) + && (i.tm.operand_types[op].bitfield.reg32 + || i.tm.operand_types[op].bitfield.acc)) + { + /* Prohibit these changes in the 64bit mode, since the + lowering is more complicated. */ + if (intel_syntax + && i.tm.opcode_modifier.todword + && !i.types[0].bitfield.regxmm) + { + /* Convert to DWORD. We don't want REX byte. */ + i.suffix = LONG_MNEM_SUFFIX; + } + else + { + as_bad (_("incorrect register `%s%s' used with `%c' suffix"), + register_prefix, i.op[op].regs->reg_name, + i.suffix); + return 0; + } + } + return 1; +} + +static int +check_word_reg (void) +{ + int op; + for (op = i.operands; --op >= 0;) + /* Reject eight bit registers, except where the template requires + them. (eg. movzb) */ + if (i.types[op].bitfield.reg8 + && (i.tm.operand_types[op].bitfield.reg16 + || i.tm.operand_types[op].bitfield.reg32 + || i.tm.operand_types[op].bitfield.acc)) + { + as_bad (_("`%s%s' not allowed with `%s%c'"), + register_prefix, + i.op[op].regs->reg_name, + i.tm.name, + i.suffix); + return 0; + } + /* Warn if the e prefix on a general reg is present. */ + else if ((!quiet_warnings || flag_code == CODE_64BIT) + && i.types[op].bitfield.reg32 + && (i.tm.operand_types[op].bitfield.reg16 + || i.tm.operand_types[op].bitfield.acc)) + { + /* Prohibit these changes in the 64bit mode, since the + lowering is more complicated. */ + if (flag_code == CODE_64BIT) + { + as_bad (_("incorrect register `%s%s' used with `%c' suffix"), + register_prefix, i.op[op].regs->reg_name, + i.suffix); + return 0; + } + else +#if REGISTER_WARNINGS + as_warn (_("using `%s%s' instead of `%s%s' due to `%c' suffix"), + register_prefix, + (i.op[op].regs + REGNAM_AX - REGNAM_EAX)->reg_name, + register_prefix, + i.op[op].regs->reg_name, + i.suffix); +#endif + } + return 1; +} + +static int +update_imm (unsigned int j) +{ + i386_operand_type overlap = i.types[j]; + if ((overlap.bitfield.imm8 + || overlap.bitfield.imm8s + || overlap.bitfield.imm16 + || overlap.bitfield.imm32 + || overlap.bitfield.imm32s + || overlap.bitfield.imm64) + && !operand_type_equal (&overlap, &imm8) + && !operand_type_equal (&overlap, &imm8s) + && !operand_type_equal (&overlap, &imm16) + && !operand_type_equal (&overlap, &imm32) + && !operand_type_equal (&overlap, &imm32s) + && !operand_type_equal (&overlap, &imm64)) + { + if (i.suffix) + { + i386_operand_type temp; + + operand_type_set (&temp, 0); + if (i.suffix == BYTE_MNEM_SUFFIX) + { + temp.bitfield.imm8 = overlap.bitfield.imm8; + temp.bitfield.imm8s = overlap.bitfield.imm8s; + } + else if (i.suffix == WORD_MNEM_SUFFIX) + temp.bitfield.imm16 = overlap.bitfield.imm16; + else if (i.suffix == QWORD_MNEM_SUFFIX) + { + temp.bitfield.imm64 = overlap.bitfield.imm64; + temp.bitfield.imm32s = overlap.bitfield.imm32s; + } + else + temp.bitfield.imm32 = overlap.bitfield.imm32; + overlap = temp; + } + else if (operand_type_equal (&overlap, &imm16_32_32s) + || operand_type_equal (&overlap, &imm16_32) + || operand_type_equal (&overlap, &imm16_32s)) + { + if ((flag_code == CODE_16BIT) ^ (i.prefix[DATA_PREFIX] != 0)) + overlap = imm16; + else + overlap = imm32s; + } + if (!operand_type_equal (&overlap, &imm8) + && !operand_type_equal (&overlap, &imm8s) + && !operand_type_equal (&overlap, &imm16) + && !operand_type_equal (&overlap, &imm32) + && !operand_type_equal (&overlap, &imm32s) + && !operand_type_equal (&overlap, &imm64)) + { + as_bad (_("no instruction mnemonic suffix given; " + "can't determine immediate size")); + return 0; + } + } + i.types[j] = overlap; + + return 1; +} + +static int +finalize_imm (void) +{ + unsigned int j, n; + + /* Update the first 2 immediate operands. */ + n = i.operands > 2 ? 2 : i.operands; + if (n) + { + for (j = 0; j < n; j++) + if (update_imm (j) == 0) + return 0; + + /* The 3rd operand can't be immediate operand. */ + gas_assert (operand_type_check (i.types[2], imm) == 0); + } + + return 1; +} + +static int +bad_implicit_operand (int xmm) +{ + const char *ireg = xmm ? "xmm0" : "ymm0"; + + if (intel_syntax) + as_bad (_("the last operand of `%s' must be `%s%s'"), + i.tm.name, register_prefix, ireg); + else + as_bad (_("the first operand of `%s' must be `%s%s'"), + i.tm.name, register_prefix, ireg); + return 0; +} + +static int +process_operands (void) +{ + /* Default segment register this instruction will use for memory + accesses. 0 means unknown. This is only for optimizing out + unnecessary segment overrides. */ + const seg_entry *default_seg = 0; + + if (i.tm.opcode_modifier.sse2avx && i.tm.opcode_modifier.vexvvvv) + { + unsigned int dupl = i.operands; + unsigned int dest = dupl - 1; + unsigned int j; + + /* The destination must be an xmm register. */ + gas_assert (i.reg_operands + && MAX_OPERANDS > dupl + && operand_type_equal (&i.types[dest], ®xmm)); + + if (i.tm.opcode_modifier.firstxmm0) + { + /* The first operand is implicit and must be xmm0. */ + gas_assert (operand_type_equal (&i.types[0], ®xmm)); + if (register_number (i.op[0].regs) != 0) + return bad_implicit_operand (1); + + if (i.tm.opcode_modifier.vexsources == VEX3SOURCES) + { + /* Keep xmm0 for instructions with VEX prefix and 3 + sources. */ + goto duplicate; + } + else + { + /* We remove the first xmm0 and keep the number of + operands unchanged, which in fact duplicates the + destination. */ + for (j = 1; j < i.operands; j++) + { + i.op[j - 1] = i.op[j]; + i.types[j - 1] = i.types[j]; + i.tm.operand_types[j - 1] = i.tm.operand_types[j]; + } + } + } + else if (i.tm.opcode_modifier.implicit1stxmm0) + { + gas_assert ((MAX_OPERANDS - 1) > dupl + && (i.tm.opcode_modifier.vexsources + == VEX3SOURCES)); + + /* Add the implicit xmm0 for instructions with VEX prefix + and 3 sources. */ + for (j = i.operands; j > 0; j--) + { + i.op[j] = i.op[j - 1]; + i.types[j] = i.types[j - 1]; + i.tm.operand_types[j] = i.tm.operand_types[j - 1]; + } + i.op[0].regs + = (const reg_entry *) hash_find (reg_hash, "xmm0"); + i.types[0] = regxmm; + i.tm.operand_types[0] = regxmm; + + i.operands += 2; + i.reg_operands += 2; + i.tm.operands += 2; + + dupl++; + dest++; + i.op[dupl] = i.op[dest]; + i.types[dupl] = i.types[dest]; + i.tm.operand_types[dupl] = i.tm.operand_types[dest]; + } + else + { +duplicate: + i.operands++; + i.reg_operands++; + i.tm.operands++; + + i.op[dupl] = i.op[dest]; + i.types[dupl] = i.types[dest]; + i.tm.operand_types[dupl] = i.tm.operand_types[dest]; + } + + if (i.tm.opcode_modifier.immext) + process_immext (); + } + else if (i.tm.opcode_modifier.firstxmm0) + { + unsigned int j; + + /* The first operand is implicit and must be xmm0/ymm0/zmm0. */ + gas_assert (i.reg_operands + && (operand_type_equal (&i.types[0], ®xmm) + || operand_type_equal (&i.types[0], ®ymm) + || operand_type_equal (&i.types[0], ®zmm))); + if (register_number (i.op[0].regs) != 0) + return bad_implicit_operand (i.types[0].bitfield.regxmm); + + for (j = 1; j < i.operands; j++) + { + i.op[j - 1] = i.op[j]; + i.types[j - 1] = i.types[j]; + + /* We need to adjust fields in i.tm since they are used by + build_modrm_byte. */ + i.tm.operand_types [j - 1] = i.tm.operand_types [j]; + } + + i.operands--; + i.reg_operands--; + i.tm.operands--; + } + else if (i.tm.opcode_modifier.regkludge) + { + /* The imul $imm, %reg instruction is converted into + imul $imm, %reg, %reg, and the clr %reg instruction + is converted into xor %reg, %reg. */ + + unsigned int first_reg_op; + + if (operand_type_check (i.types[0], reg)) + first_reg_op = 0; + else + first_reg_op = 1; + /* Pretend we saw the extra register operand. */ + gas_assert (i.reg_operands == 1 + && i.op[first_reg_op + 1].regs == 0); + i.op[first_reg_op + 1].regs = i.op[first_reg_op].regs; + i.types[first_reg_op + 1] = i.types[first_reg_op]; + i.operands++; + i.reg_operands++; + } + + if (i.tm.opcode_modifier.shortform) + { + if (i.types[0].bitfield.sreg2 + || i.types[0].bitfield.sreg3) + { + if (i.tm.base_opcode == POP_SEG_SHORT + && i.op[0].regs->reg_num == 1) + { + as_bad (_("you can't `pop %scs'"), register_prefix); + return 0; + } + i.tm.base_opcode |= (i.op[0].regs->reg_num << 3); + if ((i.op[0].regs->reg_flags & RegRex) != 0) + i.rex |= REX_B; + } + else + { + /* The register or float register operand is in operand + 0 or 1. */ + unsigned int op; + + if (i.types[0].bitfield.floatreg + || operand_type_check (i.types[0], reg)) + op = 0; + else + op = 1; + /* Register goes in low 3 bits of opcode. */ + i.tm.base_opcode |= i.op[op].regs->reg_num; + if ((i.op[op].regs->reg_flags & RegRex) != 0) + i.rex |= REX_B; + if (!quiet_warnings && i.tm.opcode_modifier.ugh) + { + /* Warn about some common errors, but press on regardless. + The first case can be generated by gcc (<= 2.8.1). */ + if (i.operands == 2) + { + /* Reversed arguments on faddp, fsubp, etc. */ + as_warn (_("translating to `%s %s%s,%s%s'"), i.tm.name, + register_prefix, i.op[!intel_syntax].regs->reg_name, + register_prefix, i.op[intel_syntax].regs->reg_name); + } + else + { + /* Extraneous `l' suffix on fp insn. */ + as_warn (_("translating to `%s %s%s'"), i.tm.name, + register_prefix, i.op[0].regs->reg_name); + } + } + } + } + else if (i.tm.opcode_modifier.modrm) + { + /* The opcode is completed (modulo i.tm.extension_opcode which + must be put into the modrm byte). Now, we make the modrm and + index base bytes based on all the info we've collected. */ + + default_seg = build_modrm_byte (); + } + else if ((i.tm.base_opcode & ~0x3) == MOV_AX_DISP32) + { + default_seg = &ds; + } + else if (i.tm.opcode_modifier.isstring) + { + /* For the string instructions that allow a segment override + on one of their operands, the default segment is ds. */ + default_seg = &ds; + } + + if (i.tm.base_opcode == 0x8d /* lea */ + && i.seg[0] + && !quiet_warnings) + as_warn (_("segment override on `%s' is ineffectual"), i.tm.name); + + /* If a segment was explicitly specified, and the specified segment + is not the default, use an opcode prefix to select it. If we + never figured out what the default segment is, then default_seg + will be zero at this point, and the specified segment prefix will + always be used. */ + if ((i.seg[0]) && (i.seg[0] != default_seg)) + { + if (!add_prefix (i.seg[0]->seg_prefix)) + return 0; + } + return 1; +} + +static const seg_entry * +build_modrm_byte (void) +{ + const seg_entry *default_seg = 0; + unsigned int source, dest; + int vex_3_sources; + + /* The first operand of instructions with VEX prefix and 3 sources + must be VEX_Imm4. */ + vex_3_sources = i.tm.opcode_modifier.vexsources == VEX3SOURCES; + if (vex_3_sources) + { + unsigned int nds, reg_slot; + expressionS *exp; + + if (i.tm.opcode_modifier.veximmext + && i.tm.opcode_modifier.immext) + { + dest = i.operands - 2; + gas_assert (dest == 3); + } + else + dest = i.operands - 1; + nds = dest - 1; + + /* There are 2 kinds of instructions: + 1. 5 operands: 4 register operands or 3 register operands + plus 1 memory operand plus one Vec_Imm4 operand, VexXDS, and + VexW0 or VexW1. The destination must be either XMM, YMM or + ZMM register. + 2. 4 operands: 4 register operands or 3 register operands + plus 1 memory operand, VexXDS, and VexImmExt */ + gas_assert ((i.reg_operands == 4 + || (i.reg_operands == 3 && i.mem_operands == 1)) + && i.tm.opcode_modifier.vexvvvv == VEXXDS + && (i.tm.opcode_modifier.veximmext + || (i.imm_operands == 1 + && i.types[0].bitfield.vec_imm4 + && (i.tm.opcode_modifier.vexw == VEXW0 + || i.tm.opcode_modifier.vexw == VEXW1) + && (operand_type_equal (&i.tm.operand_types[dest], ®xmm) + || operand_type_equal (&i.tm.operand_types[dest], ®ymm) + || operand_type_equal (&i.tm.operand_types[dest], ®zmm))))); + + if (i.imm_operands == 0) + { + /* When there is no immediate operand, generate an 8bit + immediate operand to encode the first operand. */ + exp = &im_expressions[i.imm_operands++]; + i.op[i.operands].imms = exp; + i.types[i.operands] = imm8; + i.operands++; + /* If VexW1 is set, the first operand is the source and + the second operand is encoded in the immediate operand. */ + if (i.tm.opcode_modifier.vexw == VEXW1) + { + source = 0; + reg_slot = 1; + } + else + { + source = 1; + reg_slot = 0; + } + + /* FMA swaps REG and NDS. */ + if (i.tm.cpu_flags.bitfield.cpufma) + { + unsigned int tmp; + tmp = reg_slot; + reg_slot = nds; + nds = tmp; + } + + gas_assert (operand_type_equal (&i.tm.operand_types[reg_slot], + ®xmm) + || operand_type_equal (&i.tm.operand_types[reg_slot], + ®ymm) + || operand_type_equal (&i.tm.operand_types[reg_slot], + ®zmm)); + exp->X_op = O_constant; + exp->X_add_number = register_number (i.op[reg_slot].regs) << 4; + gas_assert ((i.op[reg_slot].regs->reg_flags & RegVRex) == 0); + } + else + { + unsigned int imm_slot; + + if (i.tm.opcode_modifier.vexw == VEXW0) + { + /* If VexW0 is set, the third operand is the source and + the second operand is encoded in the immediate + operand. */ + source = 2; + reg_slot = 1; + } + else + { + /* VexW1 is set, the second operand is the source and + the third operand is encoded in the immediate + operand. */ + source = 1; + reg_slot = 2; + } + + if (i.tm.opcode_modifier.immext) + { + /* When ImmExt is set, the immdiate byte is the last + operand. */ + imm_slot = i.operands - 1; + source--; + reg_slot--; + } + else + { + imm_slot = 0; + + /* Turn on Imm8 so that output_imm will generate it. */ + i.types[imm_slot].bitfield.imm8 = 1; + } + + gas_assert (operand_type_equal (&i.tm.operand_types[reg_slot], + ®xmm) + || operand_type_equal (&i.tm.operand_types[reg_slot], + ®ymm) + || operand_type_equal (&i.tm.operand_types[reg_slot], + ®zmm)); + i.op[imm_slot].imms->X_add_number + |= register_number (i.op[reg_slot].regs) << 4; + gas_assert ((i.op[reg_slot].regs->reg_flags & RegVRex) == 0); + } + + gas_assert (operand_type_equal (&i.tm.operand_types[nds], ®xmm) + || operand_type_equal (&i.tm.operand_types[nds], + ®ymm) + || operand_type_equal (&i.tm.operand_types[nds], + ®zmm)); + i.vex.register_specifier = i.op[nds].regs; + } + else + source = dest = 0; + + /* i.reg_operands MUST be the number of real register operands; + implicit registers do not count. If there are 3 register + operands, it must be a instruction with VexNDS. For a + instruction with VexNDD, the destination register is encoded + in VEX prefix. If there are 4 register operands, it must be + a instruction with VEX prefix and 3 sources. */ + if (i.mem_operands == 0 + && ((i.reg_operands == 2 + && i.tm.opcode_modifier.vexvvvv <= VEXXDS) + || (i.reg_operands == 3 + && i.tm.opcode_modifier.vexvvvv == VEXXDS) + || (i.reg_operands == 4 && vex_3_sources))) + { + switch (i.operands) + { + case 2: + source = 0; + break; + case 3: + /* When there are 3 operands, one of them may be immediate, + which may be the first or the last operand. Otherwise, + the first operand must be shift count register (cl) or it + is an instruction with VexNDS. */ + gas_assert (i.imm_operands == 1 + || (i.imm_operands == 0 + && (i.tm.opcode_modifier.vexvvvv == VEXXDS + || i.types[0].bitfield.shiftcount))); + if (operand_type_check (i.types[0], imm) + || i.types[0].bitfield.shiftcount) + source = 1; + else + source = 0; + break; + case 4: + /* When there are 4 operands, the first two must be 8bit + immediate operands. The source operand will be the 3rd + one. + + For instructions with VexNDS, if the first operand + an imm8, the source operand is the 2nd one. If the last + operand is imm8, the source operand is the first one. */ + gas_assert ((i.imm_operands == 2 + && i.types[0].bitfield.imm8 + && i.types[1].bitfield.imm8) + || (i.tm.opcode_modifier.vexvvvv == VEXXDS + && i.imm_operands == 1 + && (i.types[0].bitfield.imm8 + || i.types[i.operands - 1].bitfield.imm8 + || i.rounding))); + if (i.imm_operands == 2) + source = 2; + else + { + if (i.types[0].bitfield.imm8) + source = 1; + else + source = 0; + } + break; + case 5: + if (i.tm.opcode_modifier.evex) + { + /* For EVEX instructions, when there are 5 operands, the + first one must be immediate operand. If the second one + is immediate operand, the source operand is the 3th + one. If the last one is immediate operand, the source + operand is the 2nd one. */ + gas_assert (i.imm_operands == 2 + && i.tm.opcode_modifier.sae + && operand_type_check (i.types[0], imm)); + if (operand_type_check (i.types[1], imm)) + source = 2; + else if (operand_type_check (i.types[4], imm)) + source = 1; + else + abort (); + } + break; + default: + abort (); + } + + if (!vex_3_sources) + { + dest = source + 1; + + /* RC/SAE operand could be between DEST and SRC. That happens + when one operand is GPR and the other one is XMM/YMM/ZMM + register. */ + if (i.rounding && i.rounding->operand == (int) dest) + dest++; + + if (i.tm.opcode_modifier.vexvvvv == VEXXDS) + { + /* For instructions with VexNDS, the register-only source + operand must be 32/64bit integer, XMM, YMM or ZMM + register. It is encoded in VEX prefix. We need to + clear RegMem bit before calling operand_type_equal. */ + + i386_operand_type op; + unsigned int vvvv; + + /* Check register-only source operand when two source + operands are swapped. */ + if (!i.tm.operand_types[source].bitfield.baseindex + && i.tm.operand_types[dest].bitfield.baseindex) + { + vvvv = source; + source = dest; + } + else + vvvv = dest; + + op = i.tm.operand_types[vvvv]; + op.bitfield.regmem = 0; + if ((dest + 1) >= i.operands + || (op.bitfield.reg32 != 1 + && !op.bitfield.reg64 != 1 + && !operand_type_equal (&op, ®xmm) + && !operand_type_equal (&op, ®ymm) + && !operand_type_equal (&op, ®zmm) + && !operand_type_equal (&op, ®mask))) + abort (); + i.vex.register_specifier = i.op[vvvv].regs; + dest++; + } + } + + i.rm.mode = 3; + /* One of the register operands will be encoded in the i.tm.reg + field, the other in the combined i.tm.mode and i.tm.regmem + fields. If no form of this instruction supports a memory + destination operand, then we assume the source operand may + sometimes be a memory operand and so we need to store the + destination in the i.rm.reg field. */ + if (!i.tm.operand_types[dest].bitfield.regmem + && operand_type_check (i.tm.operand_types[dest], anymem) == 0) + { + i.rm.reg = i.op[dest].regs->reg_num; + i.rm.regmem = i.op[source].regs->reg_num; + if ((i.op[dest].regs->reg_flags & RegRex) != 0) + i.rex |= REX_R; + if ((i.op[dest].regs->reg_flags & RegVRex) != 0) + i.vrex |= REX_R; + if ((i.op[source].regs->reg_flags & RegRex) != 0) + i.rex |= REX_B; + if ((i.op[source].regs->reg_flags & RegVRex) != 0) + i.vrex |= REX_B; + } + else + { + i.rm.reg = i.op[source].regs->reg_num; + i.rm.regmem = i.op[dest].regs->reg_num; + if ((i.op[dest].regs->reg_flags & RegRex) != 0) + i.rex |= REX_B; + if ((i.op[dest].regs->reg_flags & RegVRex) != 0) + i.vrex |= REX_B; + if ((i.op[source].regs->reg_flags & RegRex) != 0) + i.rex |= REX_R; + if ((i.op[source].regs->reg_flags & RegVRex) != 0) + i.vrex |= REX_R; + } + if (flag_code != CODE_64BIT && (i.rex & (REX_R | REX_B))) + { + if (!i.types[0].bitfield.control + && !i.types[1].bitfield.control) + abort (); + i.rex &= ~(REX_R | REX_B); + add_prefix (LOCK_PREFIX_OPCODE); + } + } + else + { /* If it's not 2 reg operands... */ + unsigned int mem; + + if (i.mem_operands) + { + unsigned int fake_zero_displacement = 0; + unsigned int op; + + for (op = 0; op < i.operands; op++) + if (operand_type_check (i.types[op], anymem)) + break; + gas_assert (op < i.operands); + + if (i.tm.opcode_modifier.vecsib) + { + if (i.index_reg->reg_num == RegEiz + || i.index_reg->reg_num == RegRiz) + abort (); + + i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING; + if (!i.base_reg) + { + i.sib.base = NO_BASE_REGISTER; + i.sib.scale = i.log2_scale_factor; + /* No Vec_Disp8 if there is no base. */ + i.types[op].bitfield.vec_disp8 = 0; + i.types[op].bitfield.disp8 = 0; + i.types[op].bitfield.disp16 = 0; + i.types[op].bitfield.disp64 = 0; + if (flag_code != CODE_64BIT) + { + /* Must be 32 bit */ + i.types[op].bitfield.disp32 = 1; + i.types[op].bitfield.disp32s = 0; + } + else + { + i.types[op].bitfield.disp32 = 0; + i.types[op].bitfield.disp32s = 1; + } + } + i.sib.index = i.index_reg->reg_num; + if ((i.index_reg->reg_flags & RegRex) != 0) + i.rex |= REX_X; + if ((i.index_reg->reg_flags & RegVRex) != 0) + i.vrex |= REX_X; + } + + default_seg = &ds; + + if (i.base_reg == 0) + { + i.rm.mode = 0; + if (!i.disp_operands) + { + fake_zero_displacement = 1; + /* Instructions with VSIB byte need 32bit displacement + if there is no base register. */ + if (i.tm.opcode_modifier.vecsib) + i.types[op].bitfield.disp32 = 1; + } + if (i.index_reg == 0) + { + gas_assert (!i.tm.opcode_modifier.vecsib); + /* Operand is just */ + if (flag_code == CODE_64BIT) + { + /* 64bit mode overwrites the 32bit absolute + addressing by RIP relative addressing and + absolute addressing is encoded by one of the + redundant SIB forms. */ + i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING; + i.sib.base = NO_BASE_REGISTER; + i.sib.index = NO_INDEX_REGISTER; + i.types[op] = ((i.prefix[ADDR_PREFIX] == 0) + ? disp32s : disp32); + } + else if ((flag_code == CODE_16BIT) + ^ (i.prefix[ADDR_PREFIX] != 0)) + { + i.rm.regmem = NO_BASE_REGISTER_16; + i.types[op] = disp16; + } + else + { + i.rm.regmem = NO_BASE_REGISTER; + i.types[op] = disp32; + } + } + else if (!i.tm.opcode_modifier.vecsib) + { + /* !i.base_reg && i.index_reg */ + if (i.index_reg->reg_num == RegEiz + || i.index_reg->reg_num == RegRiz) + i.sib.index = NO_INDEX_REGISTER; + else + i.sib.index = i.index_reg->reg_num; + i.sib.base = NO_BASE_REGISTER; + i.sib.scale = i.log2_scale_factor; + i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING; + /* No Vec_Disp8 if there is no base. */ + i.types[op].bitfield.vec_disp8 = 0; + i.types[op].bitfield.disp8 = 0; + i.types[op].bitfield.disp16 = 0; + i.types[op].bitfield.disp64 = 0; + if (flag_code != CODE_64BIT) + { + /* Must be 32 bit */ + i.types[op].bitfield.disp32 = 1; + i.types[op].bitfield.disp32s = 0; + } + else + { + i.types[op].bitfield.disp32 = 0; + i.types[op].bitfield.disp32s = 1; + } + if ((i.index_reg->reg_flags & RegRex) != 0) + i.rex |= REX_X; + } + } + /* RIP addressing for 64bit mode. */ + else if (i.base_reg->reg_num == RegRip || + i.base_reg->reg_num == RegEip) + { + gas_assert (!i.tm.opcode_modifier.vecsib); + i.rm.regmem = NO_BASE_REGISTER; + i.types[op].bitfield.disp8 = 0; + i.types[op].bitfield.disp16 = 0; + i.types[op].bitfield.disp32 = 0; + i.types[op].bitfield.disp32s = 1; + i.types[op].bitfield.disp64 = 0; + i.types[op].bitfield.vec_disp8 = 0; + i.flags[op] |= Operand_PCrel; + if (! i.disp_operands) + fake_zero_displacement = 1; + } + else if (i.base_reg->reg_type.bitfield.reg16) + { + gas_assert (!i.tm.opcode_modifier.vecsib); + switch (i.base_reg->reg_num) + { + case 3: /* (%bx) */ + if (i.index_reg == 0) + i.rm.regmem = 7; + else /* (%bx,%si) -> 0, or (%bx,%di) -> 1 */ + i.rm.regmem = i.index_reg->reg_num - 6; + break; + case 5: /* (%bp) */ + default_seg = &ss; + if (i.index_reg == 0) + { + i.rm.regmem = 6; + if (operand_type_check (i.types[op], disp) == 0) + { + /* fake (%bp) into 0(%bp) */ + if (i.tm.operand_types[op].bitfield.vec_disp8) + i.types[op].bitfield.vec_disp8 = 1; + else + i.types[op].bitfield.disp8 = 1; + fake_zero_displacement = 1; + } + } + else /* (%bp,%si) -> 2, or (%bp,%di) -> 3 */ + i.rm.regmem = i.index_reg->reg_num - 6 + 2; + break; + default: /* (%si) -> 4 or (%di) -> 5 */ + i.rm.regmem = i.base_reg->reg_num - 6 + 4; + } + i.rm.mode = mode_from_disp_size (i.types[op]); + } + else /* i.base_reg and 32/64 bit mode */ + { + if (flag_code == CODE_64BIT + && operand_type_check (i.types[op], disp)) + { + i386_operand_type temp; + operand_type_set (&temp, 0); + temp.bitfield.disp8 = i.types[op].bitfield.disp8; + temp.bitfield.vec_disp8 + = i.types[op].bitfield.vec_disp8; + i.types[op] = temp; + if (i.prefix[ADDR_PREFIX] == 0) + i.types[op].bitfield.disp32s = 1; + else + i.types[op].bitfield.disp32 = 1; + } + + if (!i.tm.opcode_modifier.vecsib) + i.rm.regmem = i.base_reg->reg_num; + if ((i.base_reg->reg_flags & RegRex) != 0) + i.rex |= REX_B; + i.sib.base = i.base_reg->reg_num; + /* x86-64 ignores REX prefix bit here to avoid decoder + complications. */ + if (!(i.base_reg->reg_flags & RegRex) + && (i.base_reg->reg_num == EBP_REG_NUM + || i.base_reg->reg_num == ESP_REG_NUM)) + default_seg = &ss; + if (i.base_reg->reg_num == 5 && i.disp_operands == 0) + { + fake_zero_displacement = 1; + if (i.tm.operand_types [op].bitfield.vec_disp8) + i.types[op].bitfield.vec_disp8 = 1; + else + i.types[op].bitfield.disp8 = 1; + } + i.sib.scale = i.log2_scale_factor; + if (i.index_reg == 0) + { + gas_assert (!i.tm.opcode_modifier.vecsib); + /* (%esp) becomes two byte modrm with no index + register. We've already stored the code for esp + in i.rm.regmem ie. ESCAPE_TO_TWO_BYTE_ADDRESSING. + Any base register besides %esp will not use the + extra modrm byte. */ + i.sib.index = NO_INDEX_REGISTER; + } + else if (!i.tm.opcode_modifier.vecsib) + { + if (i.index_reg->reg_num == RegEiz + || i.index_reg->reg_num == RegRiz) + i.sib.index = NO_INDEX_REGISTER; + else + i.sib.index = i.index_reg->reg_num; + i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING; + if ((i.index_reg->reg_flags & RegRex) != 0) + i.rex |= REX_X; + } + + if (i.disp_operands + && (i.reloc[op] == BFD_RELOC_386_TLS_DESC_CALL + || i.reloc[op] == BFD_RELOC_X86_64_TLSDESC_CALL)) + i.rm.mode = 0; + else + { + if (!fake_zero_displacement + && !i.disp_operands + && i.disp_encoding) + { + fake_zero_displacement = 1; + if (i.disp_encoding == disp_encoding_8bit) + i.types[op].bitfield.disp8 = 1; + else + i.types[op].bitfield.disp32 = 1; + } + i.rm.mode = mode_from_disp_size (i.types[op]); + } + } + + if (fake_zero_displacement) + { + /* Fakes a zero displacement assuming that i.types[op] + holds the correct displacement size. */ + expressionS *exp; + + gas_assert (i.op[op].disps == 0); + exp = &disp_expressions[i.disp_operands++]; + i.op[op].disps = exp; + exp->X_op = O_constant; + exp->X_add_number = 0; + exp->X_add_symbol = (symbolS *) 0; + exp->X_op_symbol = (symbolS *) 0; + } + + mem = op; + } + else + mem = ~0; + + if (i.tm.opcode_modifier.vexsources == XOP2SOURCES) + { + if (operand_type_check (i.types[0], imm)) + i.vex.register_specifier = NULL; + else + { + /* VEX.vvvv encodes one of the sources when the first + operand is not an immediate. */ + if (i.tm.opcode_modifier.vexw == VEXW0) + i.vex.register_specifier = i.op[0].regs; + else + i.vex.register_specifier = i.op[1].regs; + } + + /* Destination is a XMM register encoded in the ModRM.reg + and VEX.R bit. */ + i.rm.reg = i.op[2].regs->reg_num; + if ((i.op[2].regs->reg_flags & RegRex) != 0) + i.rex |= REX_R; + + /* ModRM.rm and VEX.B encodes the other source. */ + if (!i.mem_operands) + { + i.rm.mode = 3; + + if (i.tm.opcode_modifier.vexw == VEXW0) + i.rm.regmem = i.op[1].regs->reg_num; + else + i.rm.regmem = i.op[0].regs->reg_num; + + if ((i.op[1].regs->reg_flags & RegRex) != 0) + i.rex |= REX_B; + } + } + else if (i.tm.opcode_modifier.vexvvvv == VEXLWP) + { + i.vex.register_specifier = i.op[2].regs; + if (!i.mem_operands) + { + i.rm.mode = 3; + i.rm.regmem = i.op[1].regs->reg_num; + if ((i.op[1].regs->reg_flags & RegRex) != 0) + i.rex |= REX_B; + } + } + /* Fill in i.rm.reg or i.rm.regmem field with register operand + (if any) based on i.tm.extension_opcode. Again, we must be + careful to make sure that segment/control/debug/test/MMX + registers are coded into the i.rm.reg field. */ + else if (i.reg_operands) + { + unsigned int op; + unsigned int vex_reg = ~0; + + for (op = 0; op < i.operands; op++) + if (i.types[op].bitfield.reg8 + || i.types[op].bitfield.reg16 + || i.types[op].bitfield.reg32 + || i.types[op].bitfield.reg64 + || i.types[op].bitfield.regmmx + || i.types[op].bitfield.regxmm + || i.types[op].bitfield.regymm + || i.types[op].bitfield.regbnd + || i.types[op].bitfield.regzmm + || i.types[op].bitfield.regmask + || i.types[op].bitfield.sreg2 + || i.types[op].bitfield.sreg3 + || i.types[op].bitfield.control + || i.types[op].bitfield.debug + || i.types[op].bitfield.test) + break; + + if (vex_3_sources) + op = dest; + else if (i.tm.opcode_modifier.vexvvvv == VEXXDS) + { + /* For instructions with VexNDS, the register-only + source operand is encoded in VEX prefix. */ + gas_assert (mem != (unsigned int) ~0); + + if (op > mem) + { + vex_reg = op++; + gas_assert (op < i.operands); + } + else + { + /* Check register-only source operand when two source + operands are swapped. */ + if (!i.tm.operand_types[op].bitfield.baseindex + && i.tm.operand_types[op + 1].bitfield.baseindex) + { + vex_reg = op; + op += 2; + gas_assert (mem == (vex_reg + 1) + && op < i.operands); + } + else + { + vex_reg = op + 1; + gas_assert (vex_reg < i.operands); + } + } + } + else if (i.tm.opcode_modifier.vexvvvv == VEXNDD) + { + /* For instructions with VexNDD, the register destination + is encoded in VEX prefix. */ + if (i.mem_operands == 0) + { + /* There is no memory operand. */ + gas_assert ((op + 2) == i.operands); + vex_reg = op + 1; + } + else + { + /* There are only 2 operands. */ + gas_assert (op < 2 && i.operands == 2); + vex_reg = 1; + } + } + else + gas_assert (op < i.operands); + + if (vex_reg != (unsigned int) ~0) + { + i386_operand_type *type = &i.tm.operand_types[vex_reg]; + + if (type->bitfield.reg32 != 1 + && type->bitfield.reg64 != 1 + && !operand_type_equal (type, ®xmm) + && !operand_type_equal (type, ®ymm) + && !operand_type_equal (type, ®zmm) + && !operand_type_equal (type, ®mask)) + abort (); + + i.vex.register_specifier = i.op[vex_reg].regs; + } + + /* Don't set OP operand twice. */ + if (vex_reg != op) + { + /* If there is an extension opcode to put here, the + register number must be put into the regmem field. */ + if (i.tm.extension_opcode != None) + { + i.rm.regmem = i.op[op].regs->reg_num; + if ((i.op[op].regs->reg_flags & RegRex) != 0) + i.rex |= REX_B; + if ((i.op[op].regs->reg_flags & RegVRex) != 0) + i.vrex |= REX_B; + } + else + { + i.rm.reg = i.op[op].regs->reg_num; + if ((i.op[op].regs->reg_flags & RegRex) != 0) + i.rex |= REX_R; + if ((i.op[op].regs->reg_flags & RegVRex) != 0) + i.vrex |= REX_R; + } + } + + /* Now, if no memory operand has set i.rm.mode = 0, 1, 2 we + must set it to 3 to indicate this is a register operand + in the regmem field. */ + if (!i.mem_operands) + i.rm.mode = 3; + } + + /* Fill in i.rm.reg field with extension opcode (if any). */ + if (i.tm.extension_opcode != None) + i.rm.reg = i.tm.extension_opcode; + } + return default_seg; +} + +static void +output_branch (void) +{ + char *p; + int size; + int code16; + int prefix; + relax_substateT subtype; + symbolS *sym; + offsetT off; + + code16 = flag_code == CODE_16BIT ? CODE16 : 0; + size = i.disp_encoding == disp_encoding_32bit ? BIG : SMALL; + + prefix = 0; + if (i.prefix[DATA_PREFIX] != 0) + { + prefix = 1; + i.prefixes -= 1; + code16 ^= CODE16; + } + /* Pentium4 branch hints. */ + if (i.prefix[SEG_PREFIX] == CS_PREFIX_OPCODE /* not taken */ + || i.prefix[SEG_PREFIX] == DS_PREFIX_OPCODE /* taken */) + { + prefix++; + i.prefixes--; + } + if (i.prefix[REX_PREFIX] != 0) + { + prefix++; + i.prefixes--; + } + + /* BND prefixed jump. */ + if (i.prefix[BND_PREFIX] != 0) + { + FRAG_APPEND_1_CHAR (i.prefix[BND_PREFIX]); + i.prefixes -= 1; + } + + if (i.prefixes != 0 && !intel_syntax) + as_warn (_("skipping prefixes on this instruction")); + + /* It's always a symbol; End frag & setup for relax. + Make sure there is enough room in this frag for the largest + instruction we may generate in md_convert_frag. This is 2 + bytes for the opcode and room for the prefix and largest + displacement. */ + frag_grow (prefix + 2 + 4); + /* Prefix and 1 opcode byte go in fr_fix. */ + p = frag_more (prefix + 1); + if (i.prefix[DATA_PREFIX] != 0) + *p++ = DATA_PREFIX_OPCODE; + if (i.prefix[SEG_PREFIX] == CS_PREFIX_OPCODE + || i.prefix[SEG_PREFIX] == DS_PREFIX_OPCODE) + *p++ = i.prefix[SEG_PREFIX]; + if (i.prefix[REX_PREFIX] != 0) + *p++ = i.prefix[REX_PREFIX]; + *p = i.tm.base_opcode; + + if ((unsigned char) *p == JUMP_PC_RELATIVE) + subtype = ENCODE_RELAX_STATE (UNCOND_JUMP, size); + else if (cpu_arch_flags.bitfield.cpui386) + subtype = ENCODE_RELAX_STATE (COND_JUMP, size); + else + subtype = ENCODE_RELAX_STATE (COND_JUMP86, size); + subtype |= code16; + + sym = i.op[0].disps->X_add_symbol; + off = i.op[0].disps->X_add_number; + + if (i.op[0].disps->X_op != O_constant + && i.op[0].disps->X_op != O_symbol) + { + /* Handle complex expressions. */ + sym = make_expr_symbol (i.op[0].disps); + off = 0; + } + + /* 1 possible extra opcode + 4 byte displacement go in var part. + Pass reloc in fr_var. */ + frag_var (rs_machine_dependent, 5, + ((!object_64bit + || i.reloc[0] != NO_RELOC + || (i.bnd_prefix == NULL && !add_bnd_prefix)) + ? i.reloc[0] + : BFD_RELOC_X86_64_PC32_BND), + subtype, sym, off, p); +} + +static void +output_jump (void) +{ + char *p; + int size; + fixS *fixP; + + if (i.tm.opcode_modifier.jumpbyte) + { + /* This is a loop or jecxz type instruction. */ + size = 1; + if (i.prefix[ADDR_PREFIX] != 0) + { + FRAG_APPEND_1_CHAR (ADDR_PREFIX_OPCODE); + i.prefixes -= 1; + } + /* Pentium4 branch hints. */ + if (i.prefix[SEG_PREFIX] == CS_PREFIX_OPCODE /* not taken */ + || i.prefix[SEG_PREFIX] == DS_PREFIX_OPCODE /* taken */) + { + FRAG_APPEND_1_CHAR (i.prefix[SEG_PREFIX]); + i.prefixes--; + } + } + else + { + int code16; + + code16 = 0; + if (flag_code == CODE_16BIT) + code16 = CODE16; + + if (i.prefix[DATA_PREFIX] != 0) + { + FRAG_APPEND_1_CHAR (DATA_PREFIX_OPCODE); + i.prefixes -= 1; + code16 ^= CODE16; + } + + size = 4; + if (code16) + size = 2; + } + + if (i.prefix[REX_PREFIX] != 0) + { + FRAG_APPEND_1_CHAR (i.prefix[REX_PREFIX]); + i.prefixes -= 1; + } + + /* BND prefixed jump. */ + if (i.prefix[BND_PREFIX] != 0) + { + FRAG_APPEND_1_CHAR (i.prefix[BND_PREFIX]); + i.prefixes -= 1; + } + + if (i.prefixes != 0 && !intel_syntax) + as_warn (_("skipping prefixes on this instruction")); + + p = frag_more (i.tm.opcode_length + size); + switch (i.tm.opcode_length) + { + case 2: + *p++ = i.tm.base_opcode >> 8; + case 1: + *p++ = i.tm.base_opcode; + break; + default: + abort (); + } + + fixP = fix_new_exp (frag_now, p - frag_now->fr_literal, size, + i.op[0].disps, 1, reloc (size, 1, 1, + (i.bnd_prefix != NULL + || add_bnd_prefix), + i.reloc[0])); + + /* All jumps handled here are signed, but don't use a signed limit + check for 32 and 16 bit jumps as we want to allow wrap around at + 4G and 64k respectively. */ + if (size == 1) + fixP->fx_signed = 1; +} + +static void +output_interseg_jump (void) +{ + char *p; + int size; + int prefix; + int code16; + + code16 = 0; + if (flag_code == CODE_16BIT) + code16 = CODE16; + + prefix = 0; + if (i.prefix[DATA_PREFIX] != 0) + { + prefix = 1; + i.prefixes -= 1; + code16 ^= CODE16; + } + if (i.prefix[REX_PREFIX] != 0) + { + prefix++; + i.prefixes -= 1; + } + + size = 4; + if (code16) + size = 2; + + if (i.prefixes != 0 && !intel_syntax) + as_warn (_("skipping prefixes on this instruction")); + + /* 1 opcode; 2 segment; offset */ + p = frag_more (prefix + 1 + 2 + size); + + if (i.prefix[DATA_PREFIX] != 0) + *p++ = DATA_PREFIX_OPCODE; + + if (i.prefix[REX_PREFIX] != 0) + *p++ = i.prefix[REX_PREFIX]; + + *p++ = i.tm.base_opcode; + if (i.op[1].imms->X_op == O_constant) + { + offsetT n = i.op[1].imms->X_add_number; + + if (size == 2 + && !fits_in_unsigned_word (n) + && !fits_in_signed_word (n)) + { + as_bad (_("16-bit jump out of range")); + return; + } + md_number_to_chars (p, n, size); + } + else + fix_new_exp (frag_now, p - frag_now->fr_literal, size, + i.op[1].imms, 0, reloc (size, 0, 0, 0, i.reloc[1])); + if (i.op[0].imms->X_op != O_constant) + as_bad (_("can't handle non absolute segment in `%s'"), + i.tm.name); + md_number_to_chars (p + size, (valueT) i.op[0].imms->X_add_number, 2); +} + +static void +output_insn (void) +{ + fragS *insn_start_frag; + offsetT insn_start_off; + + /* Tie dwarf2 debug info to the address at the start of the insn. + We can't do this after the insn has been output as the current + frag may have been closed off. eg. by frag_var. */ + dwarf2_emit_insn (0); + + insn_start_frag = frag_now; + insn_start_off = frag_now_fix (); + + /* Output jumps. */ + if (i.tm.opcode_modifier.jump) + output_branch (); + else if (i.tm.opcode_modifier.jumpbyte + || i.tm.opcode_modifier.jumpdword) + output_jump (); + else if (i.tm.opcode_modifier.jumpintersegment) + output_interseg_jump (); + else + { + /* Output normal instructions here. */ + char *p; + unsigned char *q; + unsigned int j; + unsigned int prefix; + + /* Since the VEX/EVEX prefix contains the implicit prefix, we + don't need the explicit prefix. */ + if (!i.tm.opcode_modifier.vex && !i.tm.opcode_modifier.evex) + { + switch (i.tm.opcode_length) + { + case 3: + if (i.tm.base_opcode & 0xff000000) + { + prefix = (i.tm.base_opcode >> 24) & 0xff; + goto check_prefix; + } + break; + case 2: + if ((i.tm.base_opcode & 0xff0000) != 0) + { + prefix = (i.tm.base_opcode >> 16) & 0xff; + if (i.tm.cpu_flags.bitfield.cpupadlock) + { +check_prefix: + if (prefix != REPE_PREFIX_OPCODE + || (i.prefix[REP_PREFIX] + != REPE_PREFIX_OPCODE)) + add_prefix (prefix); + } + else + add_prefix (prefix); + } + break; + case 1: + break; + default: + abort (); + } + + /* The prefix bytes. */ + for (j = ARRAY_SIZE (i.prefix), q = i.prefix; j > 0; j--, q++) + if (*q) + FRAG_APPEND_1_CHAR (*q); + } + else + { + for (j = 0, q = i.prefix; j < ARRAY_SIZE (i.prefix); j++, q++) + if (*q) + switch (j) + { + case REX_PREFIX: + /* REX byte is encoded in VEX prefix. */ + break; + case SEG_PREFIX: + case ADDR_PREFIX: + FRAG_APPEND_1_CHAR (*q); + break; + default: + /* There should be no other prefixes for instructions + with VEX prefix. */ + abort (); + } + + /* For EVEX instructions i.vrex should become 0 after + build_evex_prefix. For VEX instructions upper 16 registers + aren't available, so VREX should be 0. */ + if (i.vrex) + abort (); + /* Now the VEX prefix. */ + p = frag_more (i.vex.length); + for (j = 0; j < i.vex.length; j++) + p[j] = i.vex.bytes[j]; + } + + /* Now the opcode; be careful about word order here! */ + if (i.tm.opcode_length == 1) + { + FRAG_APPEND_1_CHAR (i.tm.base_opcode); + } + else + { + switch (i.tm.opcode_length) + { + case 4: + p = frag_more (4); + *p++ = (i.tm.base_opcode >> 24) & 0xff; + *p++ = (i.tm.base_opcode >> 16) & 0xff; + break; + case 3: + p = frag_more (3); + *p++ = (i.tm.base_opcode >> 16) & 0xff; + break; + case 2: + p = frag_more (2); + break; + default: + abort (); + break; + } + + /* Put out high byte first: can't use md_number_to_chars! */ + *p++ = (i.tm.base_opcode >> 8) & 0xff; + *p = i.tm.base_opcode & 0xff; + } + + /* Now the modrm byte and sib byte (if present). */ + if (i.tm.opcode_modifier.modrm) + { + FRAG_APPEND_1_CHAR ((i.rm.regmem << 0 + | i.rm.reg << 3 + | i.rm.mode << 6)); + /* If i.rm.regmem == ESP (4) + && i.rm.mode != (Register mode) + && not 16 bit + ==> need second modrm byte. */ + if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING + && i.rm.mode != 3 + && !(i.base_reg && i.base_reg->reg_type.bitfield.reg16)) + FRAG_APPEND_1_CHAR ((i.sib.base << 0 + | i.sib.index << 3 + | i.sib.scale << 6)); + } + + if (i.disp_operands) + output_disp (insn_start_frag, insn_start_off); + + if (i.imm_operands) + output_imm (insn_start_frag, insn_start_off); + } + +#ifdef DEBUG386 + if (flag_debug) + { + pi ("" /*line*/, &i); + } +#endif /* DEBUG386 */ +} + +/* Return the size of the displacement operand N. */ + +static int +disp_size (unsigned int n) +{ + int size = 4; + + /* Vec_Disp8 has to be 8bit. */ + if (i.types[n].bitfield.vec_disp8) + size = 1; + else if (i.types[n].bitfield.disp64) + size = 8; + else if (i.types[n].bitfield.disp8) + size = 1; + else if (i.types[n].bitfield.disp16) + size = 2; + return size; +} + +/* Return the size of the immediate operand N. */ + +static int +imm_size (unsigned int n) +{ + int size = 4; + if (i.types[n].bitfield.imm64) + size = 8; + else if (i.types[n].bitfield.imm8 || i.types[n].bitfield.imm8s) + size = 1; + else if (i.types[n].bitfield.imm16) + size = 2; + return size; +} + +static void +output_disp (fragS *insn_start_frag, offsetT insn_start_off) +{ + char *p; + unsigned int n; + + for (n = 0; n < i.operands; n++) + { + if (i.types[n].bitfield.vec_disp8 + || operand_type_check (i.types[n], disp)) + { + if (i.op[n].disps->X_op == O_constant) + { + int size = disp_size (n); + offsetT val = i.op[n].disps->X_add_number; + + if (i.types[n].bitfield.vec_disp8) + val >>= i.memshift; + val = offset_in_range (val, size); + p = frag_more (size); + md_number_to_chars (p, val, size); + } + else + { + enum bfd_reloc_code_real reloc_type; + int size = disp_size (n); + int sign = i.types[n].bitfield.disp32s; + int pcrel = (i.flags[n] & Operand_PCrel) != 0; + + /* We can't have 8 bit displacement here. */ + gas_assert (!i.types[n].bitfield.disp8); + + /* The PC relative address is computed relative + to the instruction boundary, so in case immediate + fields follows, we need to adjust the value. */ + if (pcrel && i.imm_operands) + { + unsigned int n1; + int sz = 0; + + for (n1 = 0; n1 < i.operands; n1++) + if (operand_type_check (i.types[n1], imm)) + { + /* Only one immediate is allowed for PC + relative address. */ + gas_assert (sz == 0); + sz = imm_size (n1); + i.op[n].disps->X_add_number -= sz; + } + /* We should find the immediate. */ + gas_assert (sz != 0); + } + + p = frag_more (size); + reloc_type = reloc (size, pcrel, sign, + (i.bnd_prefix != NULL + || add_bnd_prefix), + i.reloc[n]); + if (GOT_symbol + && GOT_symbol == i.op[n].disps->X_add_symbol + && (((reloc_type == BFD_RELOC_32 + || reloc_type == BFD_RELOC_X86_64_32S + || (reloc_type == BFD_RELOC_64 + && object_64bit)) + && (i.op[n].disps->X_op == O_symbol + || (i.op[n].disps->X_op == O_add + && ((symbol_get_value_expression + (i.op[n].disps->X_op_symbol)->X_op) + == O_subtract)))) + || reloc_type == BFD_RELOC_32_PCREL)) + { + offsetT add; + + if (insn_start_frag == frag_now) + add = (p - frag_now->fr_literal) - insn_start_off; + else + { + fragS *fr; + + add = insn_start_frag->fr_fix - insn_start_off; + for (fr = insn_start_frag->fr_next; + fr && fr != frag_now; fr = fr->fr_next) + add += fr->fr_fix; + add += p - frag_now->fr_literal; + } + + if (!object_64bit) + { + reloc_type = BFD_RELOC_386_GOTPC; + i.op[n].imms->X_add_number += add; + } + else if (reloc_type == BFD_RELOC_64) + reloc_type = BFD_RELOC_X86_64_GOTPC64; + else + /* Don't do the adjustment for x86-64, as there + the pcrel addressing is relative to the _next_ + insn, and that is taken care of in other code. */ + reloc_type = BFD_RELOC_X86_64_GOTPC32; + } + fix_new_exp (frag_now, p - frag_now->fr_literal, size, + i.op[n].disps, pcrel, reloc_type); + } + } + } +} + +static void +output_imm (fragS *insn_start_frag, offsetT insn_start_off) +{ + char *p; + unsigned int n; + + for (n = 0; n < i.operands; n++) + { + /* Skip SAE/RC Imm operand in EVEX. They are already handled. */ + if (i.rounding && (int) n == i.rounding->operand) + continue; + + if (operand_type_check (i.types[n], imm)) + { + if (i.op[n].imms->X_op == O_constant) + { + int size = imm_size (n); + offsetT val; + + val = offset_in_range (i.op[n].imms->X_add_number, + size); + p = frag_more (size); + md_number_to_chars (p, val, size); + } + else + { + /* Not absolute_section. + Need a 32-bit fixup (don't support 8bit + non-absolute imms). Try to support other + sizes ... */ + enum bfd_reloc_code_real reloc_type; + int size = imm_size (n); + int sign; + + if (i.types[n].bitfield.imm32s + && (i.suffix == QWORD_MNEM_SUFFIX + || (!i.suffix && i.tm.opcode_modifier.no_lsuf))) + sign = 1; + else + sign = 0; + + p = frag_more (size); + reloc_type = reloc (size, 0, sign, 0, i.reloc[n]); + + /* This is tough to explain. We end up with this one if we + * have operands that look like + * "_GLOBAL_OFFSET_TABLE_+[.-.L284]". The goal here is to + * obtain the absolute address of the GOT, and it is strongly + * preferable from a performance point of view to avoid using + * a runtime relocation for this. The actual sequence of + * instructions often look something like: + * + * call .L66 + * .L66: + * popl %ebx + * addl $_GLOBAL_OFFSET_TABLE_+[.-.L66],%ebx + * + * The call and pop essentially return the absolute address + * of the label .L66 and store it in %ebx. The linker itself + * will ultimately change the first operand of the addl so + * that %ebx points to the GOT, but to keep things simple, the + * .o file must have this operand set so that it generates not + * the absolute address of .L66, but the absolute address of + * itself. This allows the linker itself simply treat a GOTPC + * relocation as asking for a pcrel offset to the GOT to be + * added in, and the addend of the relocation is stored in the + * operand field for the instruction itself. + * + * Our job here is to fix the operand so that it would add + * the correct offset so that %ebx would point to itself. The + * thing that is tricky is that .-.L66 will point to the + * beginning of the instruction, so we need to further modify + * the operand so that it will point to itself. There are + * other cases where you have something like: + * + * .long $_GLOBAL_OFFSET_TABLE_+[.-.L66] + * + * and here no correction would be required. Internally in + * the assembler we treat operands of this form as not being + * pcrel since the '.' is explicitly mentioned, and I wonder + * whether it would simplify matters to do it this way. Who + * knows. In earlier versions of the PIC patches, the + * pcrel_adjust field was used to store the correction, but + * since the expression is not pcrel, I felt it would be + * confusing to do it this way. */ + + if ((reloc_type == BFD_RELOC_32 + || reloc_type == BFD_RELOC_X86_64_32S + || reloc_type == BFD_RELOC_64) + && GOT_symbol + && GOT_symbol == i.op[n].imms->X_add_symbol + && (i.op[n].imms->X_op == O_symbol + || (i.op[n].imms->X_op == O_add + && ((symbol_get_value_expression + (i.op[n].imms->X_op_symbol)->X_op) + == O_subtract)))) + { + offsetT add; + + if (insn_start_frag == frag_now) + add = (p - frag_now->fr_literal) - insn_start_off; + else + { + fragS *fr; + + add = insn_start_frag->fr_fix - insn_start_off; + for (fr = insn_start_frag->fr_next; + fr && fr != frag_now; fr = fr->fr_next) + add += fr->fr_fix; + add += p - frag_now->fr_literal; + } + + if (!object_64bit) + reloc_type = BFD_RELOC_386_GOTPC; + else if (size == 4) + reloc_type = BFD_RELOC_X86_64_GOTPC32; + else if (size == 8) + reloc_type = BFD_RELOC_X86_64_GOTPC64; + i.op[n].imms->X_add_number += add; + } + fix_new_exp (frag_now, p - frag_now->fr_literal, size, + i.op[n].imms, 0, reloc_type); + } + } + } +} + +/* x86_cons_fix_new is called via the expression parsing code when a + reloc is needed. We use this hook to get the correct .got reloc. */ +static enum bfd_reloc_code_real got_reloc = NO_RELOC; +static int cons_sign = -1; + +void +x86_cons_fix_new (fragS *frag, unsigned int off, unsigned int len, + expressionS *exp) +{ + enum bfd_reloc_code_real r = reloc (len, 0, cons_sign, 0, got_reloc); + + got_reloc = NO_RELOC; + +#ifdef TE_PE + if (exp->X_op == O_secrel) + { + exp->X_op = O_symbol; + r = BFD_RELOC_32_SECREL; + } +#endif + + fix_new_exp (frag, off, len, exp, 0, r); +} + +/* Export the ABI address size for use by TC_ADDRESS_BYTES for the + purpose of the `.dc.a' internal pseudo-op. */ + +int +x86_address_bytes (void) +{ + if ((stdoutput->arch_info->mach & bfd_mach_x64_32)) + return 4; + return stdoutput->arch_info->bits_per_address / 8; +} + +#if !(defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) || defined (OBJ_MACH_O)) \ + || defined (LEX_AT) +# define lex_got(reloc, adjust, types, bnd_prefix) NULL +#else +/* Parse operands of the form + @GOTOFF+ + and similar .plt or .got references. + + If we find one, set up the correct relocation in RELOC and copy the + input string, minus the `@GOTOFF' into a malloc'd buffer for + parsing by the calling routine. Return this buffer, and if ADJUST + is non-null set it to the length of the string we removed from the + input line. Otherwise return NULL. */ +static char * +lex_got (enum bfd_reloc_code_real *rel, + int *adjust, + i386_operand_type *types, + int bnd_prefix) +{ + /* Some of the relocations depend on the size of what field is to + be relocated. But in our callers i386_immediate and i386_displacement + we don't yet know the operand size (this will be set by insn + matching). Hence we record the word32 relocation here, + and adjust the reloc according to the real size in reloc(). */ + static const struct { + const char *str; + int len; + const enum bfd_reloc_code_real rel[2]; + const i386_operand_type types64; + } gotrel[] = { +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + { STRING_COMMA_LEN ("SIZE"), { BFD_RELOC_SIZE32, + BFD_RELOC_SIZE32 }, + OPERAND_TYPE_IMM32_64 }, +#endif + { STRING_COMMA_LEN ("PLTOFF"), { _dummy_first_bfd_reloc_code_real, + BFD_RELOC_X86_64_PLTOFF64 }, + OPERAND_TYPE_IMM64 }, + { STRING_COMMA_LEN ("PLT"), { BFD_RELOC_386_PLT32, + BFD_RELOC_X86_64_PLT32 }, + OPERAND_TYPE_IMM32_32S_DISP32 }, + { STRING_COMMA_LEN ("GOTPLT"), { _dummy_first_bfd_reloc_code_real, + BFD_RELOC_X86_64_GOTPLT64 }, + OPERAND_TYPE_IMM64_DISP64 }, + { STRING_COMMA_LEN ("GOTOFF"), { BFD_RELOC_386_GOTOFF, + BFD_RELOC_X86_64_GOTOFF64 }, + OPERAND_TYPE_IMM64_DISP64 }, + { STRING_COMMA_LEN ("GOTPCREL"), { _dummy_first_bfd_reloc_code_real, + BFD_RELOC_X86_64_GOTPCREL }, + OPERAND_TYPE_IMM32_32S_DISP32 }, + { STRING_COMMA_LEN ("TLSGD"), { BFD_RELOC_386_TLS_GD, + BFD_RELOC_X86_64_TLSGD }, + OPERAND_TYPE_IMM32_32S_DISP32 }, + { STRING_COMMA_LEN ("TLSLDM"), { BFD_RELOC_386_TLS_LDM, + _dummy_first_bfd_reloc_code_real }, + OPERAND_TYPE_NONE }, + { STRING_COMMA_LEN ("TLSLD"), { _dummy_first_bfd_reloc_code_real, + BFD_RELOC_X86_64_TLSLD }, + OPERAND_TYPE_IMM32_32S_DISP32 }, + { STRING_COMMA_LEN ("GOTTPOFF"), { BFD_RELOC_386_TLS_IE_32, + BFD_RELOC_X86_64_GOTTPOFF }, + OPERAND_TYPE_IMM32_32S_DISP32 }, + { STRING_COMMA_LEN ("TPOFF"), { BFD_RELOC_386_TLS_LE_32, + BFD_RELOC_X86_64_TPOFF32 }, + OPERAND_TYPE_IMM32_32S_64_DISP32_64 }, + { STRING_COMMA_LEN ("NTPOFF"), { BFD_RELOC_386_TLS_LE, + _dummy_first_bfd_reloc_code_real }, + OPERAND_TYPE_NONE }, + { STRING_COMMA_LEN ("DTPOFF"), { BFD_RELOC_386_TLS_LDO_32, + BFD_RELOC_X86_64_DTPOFF32 }, + OPERAND_TYPE_IMM32_32S_64_DISP32_64 }, + { STRING_COMMA_LEN ("GOTNTPOFF"),{ BFD_RELOC_386_TLS_GOTIE, + _dummy_first_bfd_reloc_code_real }, + OPERAND_TYPE_NONE }, + { STRING_COMMA_LEN ("INDNTPOFF"),{ BFD_RELOC_386_TLS_IE, + _dummy_first_bfd_reloc_code_real }, + OPERAND_TYPE_NONE }, + { STRING_COMMA_LEN ("GOT"), { BFD_RELOC_386_GOT32, + BFD_RELOC_X86_64_GOT32 }, + OPERAND_TYPE_IMM32_32S_64_DISP32 }, + { STRING_COMMA_LEN ("TLSDESC"), { BFD_RELOC_386_TLS_GOTDESC, + BFD_RELOC_X86_64_GOTPC32_TLSDESC }, + OPERAND_TYPE_IMM32_32S_DISP32 }, + { STRING_COMMA_LEN ("TLSCALL"), { BFD_RELOC_386_TLS_DESC_CALL, + BFD_RELOC_X86_64_TLSDESC_CALL }, + OPERAND_TYPE_IMM32_32S_DISP32 }, + }; + char *cp; + unsigned int j; + +#if defined (OBJ_MAYBE_ELF) + if (!IS_ELF) + return NULL; +#endif + + for (cp = input_line_pointer; *cp != '@'; cp++) + if (is_end_of_line[(unsigned char) *cp] || *cp == ',') + return NULL; + + for (j = 0; j < ARRAY_SIZE (gotrel); j++) + { + int len = gotrel[j].len; + if (strncasecmp (cp + 1, gotrel[j].str, len) == 0) + { + if (gotrel[j].rel[object_64bit] != 0) + { + int first, second; + char *tmpbuf, *past_reloc; + + *rel = gotrel[j].rel[object_64bit]; + + if (types) + { + if (flag_code != CODE_64BIT) + { + types->bitfield.imm32 = 1; + types->bitfield.disp32 = 1; + } + else + *types = gotrel[j].types64; + } + + if (j != 0 && GOT_symbol == NULL) + GOT_symbol = symbol_find_or_make (GLOBAL_OFFSET_TABLE_NAME); + + /* The length of the first part of our input line. */ + first = cp - input_line_pointer; + + /* The second part goes from after the reloc token until + (and including) an end_of_line char or comma. */ + past_reloc = cp + 1 + len; + cp = past_reloc; + while (!is_end_of_line[(unsigned char) *cp] && *cp != ',') + ++cp; + second = cp + 1 - past_reloc; + + /* Allocate and copy string. The trailing NUL shouldn't + be necessary, but be safe. */ + tmpbuf = (char *) xmalloc (first + second + 2); + memcpy (tmpbuf, input_line_pointer, first); + if (second != 0 && *past_reloc != ' ') + /* Replace the relocation token with ' ', so that + errors like foo@GOTOFF1 will be detected. */ + tmpbuf[first++] = ' '; + else + /* Increment length by 1 if the relocation token is + removed. */ + len++; + if (adjust) + *adjust = len; + memcpy (tmpbuf + first, past_reloc, second); + tmpbuf[first + second] = '\0'; + if (bnd_prefix && *rel == BFD_RELOC_X86_64_PLT32) + *rel = BFD_RELOC_X86_64_PLT32_BND; + return tmpbuf; + } + + as_bad (_("@%s reloc is not supported with %d-bit output format"), + gotrel[j].str, 1 << (5 + object_64bit)); + return NULL; + } + } + + /* Might be a symbol version string. Don't as_bad here. */ + return NULL; +} +#endif + +#ifdef TE_PE +#ifdef lex_got +#undef lex_got +#endif +/* Parse operands of the form + @SECREL32+ + + If we find one, set up the correct relocation in RELOC and copy the + input string, minus the `@SECREL32' into a malloc'd buffer for + parsing by the calling routine. Return this buffer, and if ADJUST + is non-null set it to the length of the string we removed from the + input line. Otherwise return NULL. + + This function is copied from the ELF version above adjusted for PE targets. */ + +static char * +lex_got (enum bfd_reloc_code_real *rel ATTRIBUTE_UNUSED, + int *adjust ATTRIBUTE_UNUSED, + i386_operand_type *types, + int bnd_prefix ATTRIBUTE_UNUSED) +{ + static const struct + { + const char *str; + int len; + const enum bfd_reloc_code_real rel[2]; + const i386_operand_type types64; + } + gotrel[] = + { + { STRING_COMMA_LEN ("SECREL32"), { BFD_RELOC_32_SECREL, + BFD_RELOC_32_SECREL }, + OPERAND_TYPE_IMM32_32S_64_DISP32_64 }, + }; + + char *cp; + unsigned j; + + for (cp = input_line_pointer; *cp != '@'; cp++) + if (is_end_of_line[(unsigned char) *cp] || *cp == ',') + return NULL; + + for (j = 0; j < ARRAY_SIZE (gotrel); j++) + { + int len = gotrel[j].len; + + if (strncasecmp (cp + 1, gotrel[j].str, len) == 0) + { + if (gotrel[j].rel[object_64bit] != 0) + { + int first, second; + char *tmpbuf, *past_reloc; + + *rel = gotrel[j].rel[object_64bit]; + if (adjust) + *adjust = len; + + if (types) + { + if (flag_code != CODE_64BIT) + { + types->bitfield.imm32 = 1; + types->bitfield.disp32 = 1; + } + else + *types = gotrel[j].types64; + } + + /* The length of the first part of our input line. */ + first = cp - input_line_pointer; + + /* The second part goes from after the reloc token until + (and including) an end_of_line char or comma. */ + past_reloc = cp + 1 + len; + cp = past_reloc; + while (!is_end_of_line[(unsigned char) *cp] && *cp != ',') + ++cp; + second = cp + 1 - past_reloc; + + /* Allocate and copy string. The trailing NUL shouldn't + be necessary, but be safe. */ + tmpbuf = (char *) xmalloc (first + second + 2); + memcpy (tmpbuf, input_line_pointer, first); + if (second != 0 && *past_reloc != ' ') + /* Replace the relocation token with ' ', so that + errors like foo@SECLREL321 will be detected. */ + tmpbuf[first++] = ' '; + memcpy (tmpbuf + first, past_reloc, second); + tmpbuf[first + second] = '\0'; + return tmpbuf; + } + + as_bad (_("@%s reloc is not supported with %d-bit output format"), + gotrel[j].str, 1 << (5 + object_64bit)); + return NULL; + } + } + + /* Might be a symbol version string. Don't as_bad here. */ + return NULL; +} + +#endif /* TE_PE */ + +void +x86_cons (expressionS *exp, int size) +{ + intel_syntax = -intel_syntax; + + exp->X_md = 0; + if (size == 4 || (object_64bit && size == 8)) + { + /* Handle @GOTOFF and the like in an expression. */ + char *save; + char *gotfree_input_line; + int adjust = 0; + + save = input_line_pointer; + gotfree_input_line = lex_got (&got_reloc, &adjust, NULL, 0); + if (gotfree_input_line) + input_line_pointer = gotfree_input_line; + + expression (exp); + + if (gotfree_input_line) + { + /* expression () has merrily parsed up to the end of line, + or a comma - in the wrong buffer. Transfer how far + input_line_pointer has moved to the right buffer. */ + input_line_pointer = (save + + (input_line_pointer - gotfree_input_line) + + adjust); + free (gotfree_input_line); + if (exp->X_op == O_constant + || exp->X_op == O_absent + || exp->X_op == O_illegal + || exp->X_op == O_register + || exp->X_op == O_big) + { + char c = *input_line_pointer; + *input_line_pointer = 0; + as_bad (_("missing or invalid expression `%s'"), save); + *input_line_pointer = c; + } + } + } + else + expression (exp); + + intel_syntax = -intel_syntax; + + if (intel_syntax) + i386_intel_simplify (exp); +} + +static void +signed_cons (int size) +{ + if (flag_code == CODE_64BIT) + cons_sign = 1; + cons (size); + cons_sign = -1; +} + +#ifdef TE_PE +static void +pe_directive_secrel (int dummy ATTRIBUTE_UNUSED) +{ + expressionS exp; + + do + { + expression (&exp); + if (exp.X_op == O_symbol) + exp.X_op = O_secrel; + + emit_expr (&exp, 4); + } + while (*input_line_pointer++ == ','); + + input_line_pointer--; + demand_empty_rest_of_line (); +} +#endif + +/* Handle Vector operations. */ + +static char * +check_VecOperations (char *op_string, char *op_end) +{ + const reg_entry *mask; + const char *saved; + char *end_op; + + while (*op_string + && (op_end == NULL || op_string < op_end)) + { + saved = op_string; + if (*op_string == '{') + { + op_string++; + + /* Check broadcasts. */ + if (strncmp (op_string, "1to", 3) == 0) + { + int bcst_type; + + if (i.broadcast) + goto duplicated_vec_op; + + op_string += 3; + if (*op_string == '8') + bcst_type = BROADCAST_1TO8; + else if (*op_string == '1' + && *(op_string+1) == '6') + { + bcst_type = BROADCAST_1TO16; + op_string++; + } + else + { + as_bad (_("Unsupported broadcast: `%s'"), saved); + return NULL; + } + op_string++; + + broadcast_op.type = bcst_type; + broadcast_op.operand = this_operand; + i.broadcast = &broadcast_op; + } + /* Check masking operation. */ + else if ((mask = parse_register (op_string, &end_op)) != NULL) + { + /* k0 can't be used for write mask. */ + if (mask->reg_num == 0) + { + as_bad (_("`%s' can't be used for write mask"), + op_string); + return NULL; + } + + if (!i.mask) + { + mask_op.mask = mask; + mask_op.zeroing = 0; + mask_op.operand = this_operand; + i.mask = &mask_op; + } + else + { + if (i.mask->mask) + goto duplicated_vec_op; + + i.mask->mask = mask; + + /* Only "{z}" is allowed here. No need to check + zeroing mask explicitly. */ + if (i.mask->operand != this_operand) + { + as_bad (_("invalid write mask `%s'"), saved); + return NULL; + } + } + + op_string = end_op; + } + /* Check zeroing-flag for masking operation. */ + else if (*op_string == 'z') + { + if (!i.mask) + { + mask_op.mask = NULL; + mask_op.zeroing = 1; + mask_op.operand = this_operand; + i.mask = &mask_op; + } + else + { + if (i.mask->zeroing) + { + duplicated_vec_op: + as_bad (_("duplicated `%s'"), saved); + return NULL; + } + + i.mask->zeroing = 1; + + /* Only "{%k}" is allowed here. No need to check mask + register explicitly. */ + if (i.mask->operand != this_operand) + { + as_bad (_("invalid zeroing-masking `%s'"), + saved); + return NULL; + } + } + + op_string++; + } + else + goto unknown_vec_op; + + if (*op_string != '}') + { + as_bad (_("missing `}' in `%s'"), saved); + return NULL; + } + op_string++; + continue; + } + unknown_vec_op: + /* We don't know this one. */ + as_bad (_("unknown vector operation: `%s'"), saved); + return NULL; + } + + return op_string; +} + +static int +i386_immediate (char *imm_start) +{ + char *save_input_line_pointer; + char *gotfree_input_line; + segT exp_seg = 0; + expressionS *exp; + i386_operand_type types; + + operand_type_set (&types, ~0); + + if (i.imm_operands == MAX_IMMEDIATE_OPERANDS) + { + as_bad (_("at most %d immediate operands are allowed"), + MAX_IMMEDIATE_OPERANDS); + return 0; + } + + exp = &im_expressions[i.imm_operands++]; + i.op[this_operand].imms = exp; + + if (is_space_char (*imm_start)) + ++imm_start; + + save_input_line_pointer = input_line_pointer; + input_line_pointer = imm_start; + + gotfree_input_line = lex_got (&i.reloc[this_operand], NULL, &types, + (i.bnd_prefix != NULL + || add_bnd_prefix)); + if (gotfree_input_line) + input_line_pointer = gotfree_input_line; + + exp_seg = expression (exp); + + SKIP_WHITESPACE (); + + /* Handle vector operations. */ + if (*input_line_pointer == '{') + { + input_line_pointer = check_VecOperations (input_line_pointer, + NULL); + if (input_line_pointer == NULL) + return 0; + } + + if (*input_line_pointer) + as_bad (_("junk `%s' after expression"), input_line_pointer); + + input_line_pointer = save_input_line_pointer; + if (gotfree_input_line) + { + free (gotfree_input_line); + + if (exp->X_op == O_constant || exp->X_op == O_register) + exp->X_op = O_illegal; + } + + return i386_finalize_immediate (exp_seg, exp, types, imm_start); +} + +static int +i386_finalize_immediate (segT exp_seg ATTRIBUTE_UNUSED, expressionS *exp, + i386_operand_type types, const char *imm_start) +{ + if (exp->X_op == O_absent || exp->X_op == O_illegal || exp->X_op == O_big) + { + if (imm_start) + as_bad (_("missing or invalid immediate expression `%s'"), + imm_start); + return 0; + } + else if (exp->X_op == O_constant) + { + /* Size it properly later. */ + i.types[this_operand].bitfield.imm64 = 1; + /* If not 64bit, sign extend val. */ + if (flag_code != CODE_64BIT + && (exp->X_add_number & ~(((addressT) 2 << 31) - 1)) == 0) + exp->X_add_number + = (exp->X_add_number ^ ((addressT) 1 << 31)) - ((addressT) 1 << 31); + } +#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT)) + else if (OUTPUT_FLAVOR == bfd_target_aout_flavour + && exp_seg != absolute_section + && exp_seg != text_section + && exp_seg != data_section + && exp_seg != bss_section + && exp_seg != undefined_section + && !bfd_is_com_section (exp_seg)) + { + as_bad (_("unimplemented segment %s in operand"), exp_seg->name); + return 0; + } +#endif + else if (!intel_syntax && exp->X_op == O_register) + { + if (imm_start) + as_bad (_("illegal immediate register operand %s"), imm_start); + return 0; + } + else + { + /* This is an address. The size of the address will be + determined later, depending on destination register, + suffix, or the default for the section. */ + i.types[this_operand].bitfield.imm8 = 1; + i.types[this_operand].bitfield.imm16 = 1; + i.types[this_operand].bitfield.imm32 = 1; + i.types[this_operand].bitfield.imm32s = 1; + i.types[this_operand].bitfield.imm64 = 1; + i.types[this_operand] = operand_type_and (i.types[this_operand], + types); + } + + return 1; +} + +static char * +i386_scale (char *scale) +{ + offsetT val; + char *save = input_line_pointer; + + input_line_pointer = scale; + val = get_absolute_expression (); + + switch (val) + { + case 1: + i.log2_scale_factor = 0; + break; + case 2: + i.log2_scale_factor = 1; + break; + case 4: + i.log2_scale_factor = 2; + break; + case 8: + i.log2_scale_factor = 3; + break; + default: + { + char sep = *input_line_pointer; + + *input_line_pointer = '\0'; + as_bad (_("expecting scale factor of 1, 2, 4, or 8: got `%s'"), + scale); + *input_line_pointer = sep; + input_line_pointer = save; + return NULL; + } + } + if (i.log2_scale_factor != 0 && i.index_reg == 0) + { + as_warn (_("scale factor of %d without an index register"), + 1 << i.log2_scale_factor); + i.log2_scale_factor = 0; + } + scale = input_line_pointer; + input_line_pointer = save; + return scale; +} + +static int +i386_displacement (char *disp_start, char *disp_end) +{ + expressionS *exp; + segT exp_seg = 0; + char *save_input_line_pointer; + char *gotfree_input_line; + int override; + i386_operand_type bigdisp, types = anydisp; + int ret; + + if (i.disp_operands == MAX_MEMORY_OPERANDS) + { + as_bad (_("at most %d displacement operands are allowed"), + MAX_MEMORY_OPERANDS); + return 0; + } + + operand_type_set (&bigdisp, 0); + if ((i.types[this_operand].bitfield.jumpabsolute) + || (!current_templates->start->opcode_modifier.jump + && !current_templates->start->opcode_modifier.jumpdword)) + { + bigdisp.bitfield.disp32 = 1; + override = (i.prefix[ADDR_PREFIX] != 0); + if (flag_code == CODE_64BIT) + { + if (!override) + { + bigdisp.bitfield.disp32s = 1; + bigdisp.bitfield.disp64 = 1; + } + } + else if ((flag_code == CODE_16BIT) ^ override) + { + bigdisp.bitfield.disp32 = 0; + bigdisp.bitfield.disp16 = 1; + } + } + else + { + /* For PC-relative branches, the width of the displacement + is dependent upon data size, not address size. */ + override = (i.prefix[DATA_PREFIX] != 0); + if (flag_code == CODE_64BIT) + { + if (override || i.suffix == WORD_MNEM_SUFFIX) + bigdisp.bitfield.disp16 = 1; + else + { + bigdisp.bitfield.disp32 = 1; + bigdisp.bitfield.disp32s = 1; + } + } + else + { + if (!override) + override = (i.suffix == (flag_code != CODE_16BIT + ? WORD_MNEM_SUFFIX + : LONG_MNEM_SUFFIX)); + bigdisp.bitfield.disp32 = 1; + if ((flag_code == CODE_16BIT) ^ override) + { + bigdisp.bitfield.disp32 = 0; + bigdisp.bitfield.disp16 = 1; + } + } + } + i.types[this_operand] = operand_type_or (i.types[this_operand], + bigdisp); + + exp = &disp_expressions[i.disp_operands]; + i.op[this_operand].disps = exp; + i.disp_operands++; + save_input_line_pointer = input_line_pointer; + input_line_pointer = disp_start; + END_STRING_AND_SAVE (disp_end); + +#ifndef GCC_ASM_O_HACK +#define GCC_ASM_O_HACK 0 +#endif +#if GCC_ASM_O_HACK + END_STRING_AND_SAVE (disp_end + 1); + if (i.types[this_operand].bitfield.baseIndex + && displacement_string_end[-1] == '+') + { + /* This hack is to avoid a warning when using the "o" + constraint within gcc asm statements. + For instance: + + #define _set_tssldt_desc(n,addr,limit,type) \ + __asm__ __volatile__ ( \ + "movw %w2,%0\n\t" \ + "movw %w1,2+%0\n\t" \ + "rorl $16,%1\n\t" \ + "movb %b1,4+%0\n\t" \ + "movb %4,5+%0\n\t" \ + "movb $0,6+%0\n\t" \ + "movb %h1,7+%0\n\t" \ + "rorl $16,%1" \ + : "=o"(*(n)) : "q" (addr), "ri"(limit), "i"(type)) + + This works great except that the output assembler ends + up looking a bit weird if it turns out that there is + no offset. You end up producing code that looks like: + + #APP + movw $235,(%eax) + movw %dx,2+(%eax) + rorl $16,%edx + movb %dl,4+(%eax) + movb $137,5+(%eax) + movb $0,6+(%eax) + movb %dh,7+(%eax) + rorl $16,%edx + #NO_APP + + So here we provide the missing zero. */ + + *displacement_string_end = '0'; + } +#endif + gotfree_input_line = lex_got (&i.reloc[this_operand], NULL, &types, + (i.bnd_prefix != NULL + || add_bnd_prefix)); + if (gotfree_input_line) + input_line_pointer = gotfree_input_line; + + exp_seg = expression (exp); + + SKIP_WHITESPACE (); + if (*input_line_pointer) + as_bad (_("junk `%s' after expression"), input_line_pointer); +#if GCC_ASM_O_HACK + RESTORE_END_STRING (disp_end + 1); +#endif + input_line_pointer = save_input_line_pointer; + if (gotfree_input_line) + { + free (gotfree_input_line); + + if (exp->X_op == O_constant || exp->X_op == O_register) + exp->X_op = O_illegal; + } + + ret = i386_finalize_displacement (exp_seg, exp, types, disp_start); + + RESTORE_END_STRING (disp_end); + + return ret; +} + +static int +i386_finalize_displacement (segT exp_seg ATTRIBUTE_UNUSED, expressionS *exp, + i386_operand_type types, const char *disp_start) +{ + i386_operand_type bigdisp; + int ret = 1; + + /* We do this to make sure that the section symbol is in + the symbol table. We will ultimately change the relocation + to be relative to the beginning of the section. */ + if (i.reloc[this_operand] == BFD_RELOC_386_GOTOFF + || i.reloc[this_operand] == BFD_RELOC_X86_64_GOTPCREL + || i.reloc[this_operand] == BFD_RELOC_X86_64_GOTOFF64) + { + if (exp->X_op != O_symbol) + goto inv_disp; + + if (S_IS_LOCAL (exp->X_add_symbol) + && S_GET_SEGMENT (exp->X_add_symbol) != undefined_section + && S_GET_SEGMENT (exp->X_add_symbol) != expr_section) + section_symbol (S_GET_SEGMENT (exp->X_add_symbol)); + exp->X_op = O_subtract; + exp->X_op_symbol = GOT_symbol; + if (i.reloc[this_operand] == BFD_RELOC_X86_64_GOTPCREL) + i.reloc[this_operand] = BFD_RELOC_32_PCREL; + else if (i.reloc[this_operand] == BFD_RELOC_X86_64_GOTOFF64) + i.reloc[this_operand] = BFD_RELOC_64; + else + i.reloc[this_operand] = BFD_RELOC_32; + } + + else if (exp->X_op == O_absent + || exp->X_op == O_illegal + || exp->X_op == O_big) + { + inv_disp: + as_bad (_("missing or invalid displacement expression `%s'"), + disp_start); + ret = 0; + } + + else if (flag_code == CODE_64BIT + && !i.prefix[ADDR_PREFIX] + && exp->X_op == O_constant) + { + /* Since displacement is signed extended to 64bit, don't allow + disp32 and turn off disp32s if they are out of range. */ + i.types[this_operand].bitfield.disp32 = 0; + if (!fits_in_signed_long (exp->X_add_number)) + { + i.types[this_operand].bitfield.disp32s = 0; + if (i.types[this_operand].bitfield.baseindex) + { + as_bad (_("0x%lx out range of signed 32bit displacement"), + (long) exp->X_add_number); + ret = 0; + } + } + } + +#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT)) + else if (exp->X_op != O_constant + && OUTPUT_FLAVOR == bfd_target_aout_flavour + && exp_seg != absolute_section + && exp_seg != text_section + && exp_seg != data_section + && exp_seg != bss_section + && exp_seg != undefined_section + && !bfd_is_com_section (exp_seg)) + { + as_bad (_("unimplemented segment %s in operand"), exp_seg->name); + ret = 0; + } +#endif + + /* Check if this is a displacement only operand. */ + bigdisp = i.types[this_operand]; + bigdisp.bitfield.disp8 = 0; + bigdisp.bitfield.disp16 = 0; + bigdisp.bitfield.disp32 = 0; + bigdisp.bitfield.disp32s = 0; + bigdisp.bitfield.disp64 = 0; + if (operand_type_all_zero (&bigdisp)) + i.types[this_operand] = operand_type_and (i.types[this_operand], + types); + + return ret; +} + +/* Make sure the memory operand we've been dealt is valid. + Return 1 on success, 0 on a failure. */ + +static int +i386_index_check (const char *operand_string) +{ + const char *kind = "base/index"; + enum flag_code addr_mode; + + if (i.prefix[ADDR_PREFIX]) + addr_mode = flag_code == CODE_32BIT ? CODE_16BIT : CODE_32BIT; + else + { + addr_mode = flag_code; + +#if INFER_ADDR_PREFIX + if (i.mem_operands == 0) + { + /* Infer address prefix from the first memory operand. */ + const reg_entry *addr_reg = i.base_reg; + + if (addr_reg == NULL) + addr_reg = i.index_reg; + + if (addr_reg) + { + if (addr_reg->reg_num == RegEip + || addr_reg->reg_num == RegEiz + || addr_reg->reg_type.bitfield.reg32) + addr_mode = CODE_32BIT; + else if (flag_code != CODE_64BIT + && addr_reg->reg_type.bitfield.reg16) + addr_mode = CODE_16BIT; + + if (addr_mode != flag_code) + { + i.prefix[ADDR_PREFIX] = ADDR_PREFIX_OPCODE; + i.prefixes += 1; + /* Change the size of any displacement too. At most one + of Disp16 or Disp32 is set. + FIXME. There doesn't seem to be any real need for + separate Disp16 and Disp32 flags. The same goes for + Imm16 and Imm32. Removing them would probably clean + up the code quite a lot. */ + if (flag_code != CODE_64BIT + && (i.types[this_operand].bitfield.disp16 + || i.types[this_operand].bitfield.disp32)) + i.types[this_operand] + = operand_type_xor (i.types[this_operand], disp16_32); + } + } + } +#endif + } + + if (current_templates->start->opcode_modifier.isstring + && !current_templates->start->opcode_modifier.immext + && (current_templates->end[-1].opcode_modifier.isstring + || i.mem_operands)) + { + /* Memory operands of string insns are special in that they only allow + a single register (rDI, rSI, or rBX) as their memory address. */ + const reg_entry *expected_reg; + static const char *di_si[][2] = + { + { "esi", "edi" }, + { "si", "di" }, + { "rsi", "rdi" } + }; + static const char *bx[] = { "ebx", "bx", "rbx" }; + + kind = "string address"; + + if (current_templates->start->opcode_modifier.w) + { + i386_operand_type type = current_templates->end[-1].operand_types[0]; + + if (!type.bitfield.baseindex + || ((!i.mem_operands != !intel_syntax) + && current_templates->end[-1].operand_types[1] + .bitfield.baseindex)) + type = current_templates->end[-1].operand_types[1]; + expected_reg = hash_find (reg_hash, + di_si[addr_mode][type.bitfield.esseg]); + + } + else + expected_reg = hash_find (reg_hash, bx[addr_mode]); + + if (i.base_reg != expected_reg + || i.index_reg + || operand_type_check (i.types[this_operand], disp)) + { + /* The second memory operand must have the same size as + the first one. */ + if (i.mem_operands + && i.base_reg + && !((addr_mode == CODE_64BIT + && i.base_reg->reg_type.bitfield.reg64) + || (addr_mode == CODE_32BIT + ? i.base_reg->reg_type.bitfield.reg32 + : i.base_reg->reg_type.bitfield.reg16))) + goto bad_address; + + as_warn (_("`%s' is not valid here (expected `%c%s%s%c')"), + operand_string, + intel_syntax ? '[' : '(', + register_prefix, + expected_reg->reg_name, + intel_syntax ? ']' : ')'); + return 1; + } + else + return 1; + +bad_address: + as_bad (_("`%s' is not a valid %s expression"), + operand_string, kind); + return 0; + } + else + { + if (addr_mode != CODE_16BIT) + { + /* 32-bit/64-bit checks. */ + if ((i.base_reg + && (addr_mode == CODE_64BIT + ? !i.base_reg->reg_type.bitfield.reg64 + : !i.base_reg->reg_type.bitfield.reg32) + && (i.index_reg + || (i.base_reg->reg_num + != (addr_mode == CODE_64BIT ? RegRip : RegEip)))) + || (i.index_reg + && !i.index_reg->reg_type.bitfield.regxmm + && !i.index_reg->reg_type.bitfield.regymm + && !i.index_reg->reg_type.bitfield.regzmm + && ((addr_mode == CODE_64BIT + ? !(i.index_reg->reg_type.bitfield.reg64 + || i.index_reg->reg_num == RegRiz) + : !(i.index_reg->reg_type.bitfield.reg32 + || i.index_reg->reg_num == RegEiz)) + || !i.index_reg->reg_type.bitfield.baseindex))) + goto bad_address; + } + else + { + /* 16-bit checks. */ + if ((i.base_reg + && (!i.base_reg->reg_type.bitfield.reg16 + || !i.base_reg->reg_type.bitfield.baseindex)) + || (i.index_reg + && (!i.index_reg->reg_type.bitfield.reg16 + || !i.index_reg->reg_type.bitfield.baseindex + || !(i.base_reg + && i.base_reg->reg_num < 6 + && i.index_reg->reg_num >= 6 + && i.log2_scale_factor == 0)))) + goto bad_address; + } + } + return 1; +} + +/* Handle vector immediates. */ + +static int +RC_SAE_immediate (const char *imm_start) +{ + unsigned int match_found, j; + const char *pstr = imm_start; + expressionS *exp; + + if (*pstr != '{') + return 0; + + pstr++; + match_found = 0; + for (j = 0; j < ARRAY_SIZE (RC_NamesTable); j++) + { + if (!strncmp (pstr, RC_NamesTable[j].name, RC_NamesTable[j].len)) + { + if (!i.rounding) + { + rc_op.type = RC_NamesTable[j].type; + rc_op.operand = this_operand; + i.rounding = &rc_op; + } + else + { + as_bad (_("duplicated `%s'"), imm_start); + return 0; + } + pstr += RC_NamesTable[j].len; + match_found = 1; + break; + } + } + if (!match_found) + return 0; + + if (*pstr++ != '}') + { + as_bad (_("Missing '}': '%s'"), imm_start); + return 0; + } + /* RC/SAE immediate string should contain nothing more. */; + if (*pstr != 0) + { + as_bad (_("Junk after '}': '%s'"), imm_start); + return 0; + } + + exp = &im_expressions[i.imm_operands++]; + i.op[this_operand].imms = exp; + + exp->X_op = O_constant; + exp->X_add_number = 0; + exp->X_add_symbol = (symbolS *) 0; + exp->X_op_symbol = (symbolS *) 0; + + i.types[this_operand].bitfield.imm8 = 1; + return 1; +} + +/* Parse OPERAND_STRING into the i386_insn structure I. Returns zero + on error. */ + +static int +i386_att_operand (char *operand_string) +{ + const reg_entry *r; + char *end_op; + char *op_string = operand_string; + + if (is_space_char (*op_string)) + ++op_string; + + /* We check for an absolute prefix (differentiating, + for example, 'jmp pc_relative_label' from 'jmp *absolute_label'. */ + if (*op_string == ABSOLUTE_PREFIX) + { + ++op_string; + if (is_space_char (*op_string)) + ++op_string; + i.types[this_operand].bitfield.jumpabsolute = 1; + } + + /* Check if operand is a register. */ + if ((r = parse_register (op_string, &end_op)) != NULL) + { + i386_operand_type temp; + + /* Check for a segment override by searching for ':' after a + segment register. */ + op_string = end_op; + if (is_space_char (*op_string)) + ++op_string; + if (*op_string == ':' + && (r->reg_type.bitfield.sreg2 + || r->reg_type.bitfield.sreg3)) + { + switch (r->reg_num) + { + case 0: + i.seg[i.mem_operands] = &es; + break; + case 1: + i.seg[i.mem_operands] = &cs; + break; + case 2: + i.seg[i.mem_operands] = &ss; + break; + case 3: + i.seg[i.mem_operands] = &ds; + break; + case 4: + i.seg[i.mem_operands] = &fs; + break; + case 5: + i.seg[i.mem_operands] = &gs; + break; + } + + /* Skip the ':' and whitespace. */ + ++op_string; + if (is_space_char (*op_string)) + ++op_string; + + if (!is_digit_char (*op_string) + && !is_identifier_char (*op_string) + && *op_string != '(' + && *op_string != ABSOLUTE_PREFIX) + { + as_bad (_("bad memory operand `%s'"), op_string); + return 0; + } + /* Handle case of %es:*foo. */ + if (*op_string == ABSOLUTE_PREFIX) + { + ++op_string; + if (is_space_char (*op_string)) + ++op_string; + i.types[this_operand].bitfield.jumpabsolute = 1; + } + goto do_memory_reference; + } + + /* Handle vector operations. */ + if (*op_string == '{') + { + op_string = check_VecOperations (op_string, NULL); + if (op_string == NULL) + return 0; + } + + if (*op_string) + { + as_bad (_("junk `%s' after register"), op_string); + return 0; + } + temp = r->reg_type; + temp.bitfield.baseindex = 0; + i.types[this_operand] = operand_type_or (i.types[this_operand], + temp); + i.types[this_operand].bitfield.unspecified = 0; + i.op[this_operand].regs = r; + i.reg_operands++; + } + else if (*op_string == REGISTER_PREFIX) + { + as_bad (_("bad register name `%s'"), op_string); + return 0; + } + else if (*op_string == IMMEDIATE_PREFIX) + { + ++op_string; + if (i.types[this_operand].bitfield.jumpabsolute) + { + as_bad (_("immediate operand illegal with absolute jump")); + return 0; + } + if (!i386_immediate (op_string)) + return 0; + } + else if (RC_SAE_immediate (operand_string)) + { + /* If it is a RC or SAE immediate, do nothing. */ + ; + } + else if (is_digit_char (*op_string) + || is_identifier_char (*op_string) + || *op_string == '(') + { + /* This is a memory reference of some sort. */ + char *base_string; + + /* Start and end of displacement string expression (if found). */ + char *displacement_string_start; + char *displacement_string_end; + char *vop_start; + + do_memory_reference: + if ((i.mem_operands == 1 + && !current_templates->start->opcode_modifier.isstring) + || i.mem_operands == 2) + { + as_bad (_("too many memory references for `%s'"), + current_templates->start->name); + return 0; + } + + /* Check for base index form. We detect the base index form by + looking for an ')' at the end of the operand, searching + for the '(' matching it, and finding a REGISTER_PREFIX or ',' + after the '('. */ + base_string = op_string + strlen (op_string); + + /* Handle vector operations. */ + vop_start = strchr (op_string, '{'); + if (vop_start && vop_start < base_string) + { + if (check_VecOperations (vop_start, base_string) == NULL) + return 0; + base_string = vop_start; + } + + --base_string; + if (is_space_char (*base_string)) + --base_string; + + /* If we only have a displacement, set-up for it to be parsed later. */ + displacement_string_start = op_string; + displacement_string_end = base_string + 1; + + if (*base_string == ')') + { + char *temp_string; + unsigned int parens_balanced = 1; + /* We've already checked that the number of left & right ()'s are + equal, so this loop will not be infinite. */ + do + { + base_string--; + if (*base_string == ')') + parens_balanced++; + if (*base_string == '(') + parens_balanced--; + } + while (parens_balanced); + + temp_string = base_string; + + /* Skip past '(' and whitespace. */ + ++base_string; + if (is_space_char (*base_string)) + ++base_string; + + if (*base_string == ',' + || ((i.base_reg = parse_register (base_string, &end_op)) + != NULL)) + { + displacement_string_end = temp_string; + + i.types[this_operand].bitfield.baseindex = 1; + + if (i.base_reg) + { + base_string = end_op; + if (is_space_char (*base_string)) + ++base_string; + } + + /* There may be an index reg or scale factor here. */ + if (*base_string == ',') + { + ++base_string; + if (is_space_char (*base_string)) + ++base_string; + + if ((i.index_reg = parse_register (base_string, &end_op)) + != NULL) + { + base_string = end_op; + if (is_space_char (*base_string)) + ++base_string; + if (*base_string == ',') + { + ++base_string; + if (is_space_char (*base_string)) + ++base_string; + } + else if (*base_string != ')') + { + as_bad (_("expecting `,' or `)' " + "after index register in `%s'"), + operand_string); + return 0; + } + } + else if (*base_string == REGISTER_PREFIX) + { + end_op = strchr (base_string, ','); + if (end_op) + *end_op = '\0'; + as_bad (_("bad register name `%s'"), base_string); + return 0; + } + + /* Check for scale factor. */ + if (*base_string != ')') + { + char *end_scale = i386_scale (base_string); + + if (!end_scale) + return 0; + + base_string = end_scale; + if (is_space_char (*base_string)) + ++base_string; + if (*base_string != ')') + { + as_bad (_("expecting `)' " + "after scale factor in `%s'"), + operand_string); + return 0; + } + } + else if (!i.index_reg) + { + as_bad (_("expecting index register or scale factor " + "after `,'; got '%c'"), + *base_string); + return 0; + } + } + else if (*base_string != ')') + { + as_bad (_("expecting `,' or `)' " + "after base register in `%s'"), + operand_string); + return 0; + } + } + else if (*base_string == REGISTER_PREFIX) + { + end_op = strchr (base_string, ','); + if (end_op) + *end_op = '\0'; + as_bad (_("bad register name `%s'"), base_string); + return 0; + } + } + + /* If there's an expression beginning the operand, parse it, + assuming displacement_string_start and + displacement_string_end are meaningful. */ + if (displacement_string_start != displacement_string_end) + { + if (!i386_displacement (displacement_string_start, + displacement_string_end)) + return 0; + } + + /* Special case for (%dx) while doing input/output op. */ + if (i.base_reg + && operand_type_equal (&i.base_reg->reg_type, + ®16_inoutportreg) + && i.index_reg == 0 + && i.log2_scale_factor == 0 + && i.seg[i.mem_operands] == 0 + && !operand_type_check (i.types[this_operand], disp)) + { + i.types[this_operand] = inoutportreg; + return 1; + } + + if (i386_index_check (operand_string) == 0) + return 0; + i.types[this_operand].bitfield.mem = 1; + i.mem_operands++; + } + else + { + /* It's not a memory operand; argh! */ + as_bad (_("invalid char %s beginning operand %d `%s'"), + output_invalid (*op_string), + this_operand + 1, + op_string); + return 0; + } + return 1; /* Normal return. */ +} + +/* Calculate the maximum variable size (i.e., excluding fr_fix) + that an rs_machine_dependent frag may reach. */ + +unsigned int +i386_frag_max_var (fragS *frag) +{ + /* The only relaxable frags are for jumps. + Unconditional jumps can grow by 4 bytes and others by 5 bytes. */ + gas_assert (frag->fr_type == rs_machine_dependent); + return TYPE_FROM_RELAX_STATE (frag->fr_subtype) == UNCOND_JUMP ? 4 : 5; +} + +/* md_estimate_size_before_relax() + + Called just before relax() for rs_machine_dependent frags. The x86 + assembler uses these frags to handle variable size jump + instructions. + + Any symbol that is now undefined will not become defined. + Return the correct fr_subtype in the frag. + Return the initial "guess for variable size of frag" to caller. + The guess is actually the growth beyond the fixed part. Whatever + we do to grow the fixed or variable part contributes to our + returned value. */ + +int +md_estimate_size_before_relax (fragS *fragP, segT segment) +{ + /* We've already got fragP->fr_subtype right; all we have to do is + check for un-relaxable symbols. On an ELF system, we can't relax + an externally visible symbol, because it may be overridden by a + shared library. */ + if (S_GET_SEGMENT (fragP->fr_symbol) != segment +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + || (IS_ELF + && (S_IS_EXTERNAL (fragP->fr_symbol) + || S_IS_WEAK (fragP->fr_symbol) + || ((symbol_get_bfdsym (fragP->fr_symbol)->flags + & BSF_GNU_INDIRECT_FUNCTION)))) +#endif +#if defined (OBJ_COFF) && defined (TE_PE) + || (OUTPUT_FLAVOR == bfd_target_coff_flavour + && S_IS_WEAK (fragP->fr_symbol)) +#endif + ) + { + /* Symbol is undefined in this segment, or we need to keep a + reloc so that weak symbols can be overridden. */ + int size = (fragP->fr_subtype & CODE16) ? 2 : 4; + enum bfd_reloc_code_real reloc_type; + unsigned char *opcode; + int old_fr_fix; + + if (fragP->fr_var != NO_RELOC) + reloc_type = (enum bfd_reloc_code_real) fragP->fr_var; + else if (size == 2) + reloc_type = BFD_RELOC_16_PCREL; + else + reloc_type = BFD_RELOC_32_PCREL; + + old_fr_fix = fragP->fr_fix; + opcode = (unsigned char *) fragP->fr_opcode; + + switch (TYPE_FROM_RELAX_STATE (fragP->fr_subtype)) + { + case UNCOND_JUMP: + /* Make jmp (0xeb) a (d)word displacement jump. */ + opcode[0] = 0xe9; + fragP->fr_fix += size; + fix_new (fragP, old_fr_fix, size, + fragP->fr_symbol, + fragP->fr_offset, 1, + reloc_type); + break; + + case COND_JUMP86: + if (size == 2 + && (!no_cond_jump_promotion || fragP->fr_var != NO_RELOC)) + { + /* Negate the condition, and branch past an + unconditional jump. */ + opcode[0] ^= 1; + opcode[1] = 3; + /* Insert an unconditional jump. */ + opcode[2] = 0xe9; + /* We added two extra opcode bytes, and have a two byte + offset. */ + fragP->fr_fix += 2 + 2; + fix_new (fragP, old_fr_fix + 2, 2, + fragP->fr_symbol, + fragP->fr_offset, 1, + reloc_type); + break; + } + /* Fall through. */ + + case COND_JUMP: + if (no_cond_jump_promotion && fragP->fr_var == NO_RELOC) + { + fixS *fixP; + + fragP->fr_fix += 1; + fixP = fix_new (fragP, old_fr_fix, 1, + fragP->fr_symbol, + fragP->fr_offset, 1, + BFD_RELOC_8_PCREL); + fixP->fx_signed = 1; + break; + } + + /* This changes the byte-displacement jump 0x7N + to the (d)word-displacement jump 0x0f,0x8N. */ + opcode[1] = opcode[0] + 0x10; + opcode[0] = TWO_BYTE_OPCODE_ESCAPE; + /* We've added an opcode byte. */ + fragP->fr_fix += 1 + size; + fix_new (fragP, old_fr_fix + 1, size, + fragP->fr_symbol, + fragP->fr_offset, 1, + reloc_type); + break; + + default: + BAD_CASE (fragP->fr_subtype); + break; + } + frag_wane (fragP); + return fragP->fr_fix - old_fr_fix; + } + + /* Guess size depending on current relax state. Initially the relax + state will correspond to a short jump and we return 1, because + the variable part of the frag (the branch offset) is one byte + long. However, we can relax a section more than once and in that + case we must either set fr_subtype back to the unrelaxed state, + or return the value for the appropriate branch. */ + return md_relax_table[fragP->fr_subtype].rlx_length; +} + +/* Called after relax() is finished. + + In: Address of frag. + fr_type == rs_machine_dependent. + fr_subtype is what the address relaxed to. + + Out: Any fixSs and constants are set up. + Caller will turn frag into a ".space 0". */ + +void +md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED, + fragS *fragP) +{ + unsigned char *opcode; + unsigned char *where_to_put_displacement = NULL; + offsetT target_address; + offsetT opcode_address; + unsigned int extension = 0; + offsetT displacement_from_opcode_start; + + opcode = (unsigned char *) fragP->fr_opcode; + + /* Address we want to reach in file space. */ + target_address = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset; + + /* Address opcode resides at in file space. */ + opcode_address = fragP->fr_address + fragP->fr_fix; + + /* Displacement from opcode start to fill into instruction. */ + displacement_from_opcode_start = target_address - opcode_address; + + if ((fragP->fr_subtype & BIG) == 0) + { + /* Don't have to change opcode. */ + extension = 1; /* 1 opcode + 1 displacement */ + where_to_put_displacement = &opcode[1]; + } + else + { + if (no_cond_jump_promotion + && TYPE_FROM_RELAX_STATE (fragP->fr_subtype) != UNCOND_JUMP) + as_warn_where (fragP->fr_file, fragP->fr_line, + _("long jump required")); + + switch (fragP->fr_subtype) + { + case ENCODE_RELAX_STATE (UNCOND_JUMP, BIG): + extension = 4; /* 1 opcode + 4 displacement */ + opcode[0] = 0xe9; + where_to_put_displacement = &opcode[1]; + break; + + case ENCODE_RELAX_STATE (UNCOND_JUMP, BIG16): + extension = 2; /* 1 opcode + 2 displacement */ + opcode[0] = 0xe9; + where_to_put_displacement = &opcode[1]; + break; + + case ENCODE_RELAX_STATE (COND_JUMP, BIG): + case ENCODE_RELAX_STATE (COND_JUMP86, BIG): + extension = 5; /* 2 opcode + 4 displacement */ + opcode[1] = opcode[0] + 0x10; + opcode[0] = TWO_BYTE_OPCODE_ESCAPE; + where_to_put_displacement = &opcode[2]; + break; + + case ENCODE_RELAX_STATE (COND_JUMP, BIG16): + extension = 3; /* 2 opcode + 2 displacement */ + opcode[1] = opcode[0] + 0x10; + opcode[0] = TWO_BYTE_OPCODE_ESCAPE; + where_to_put_displacement = &opcode[2]; + break; + + case ENCODE_RELAX_STATE (COND_JUMP86, BIG16): + extension = 4; + opcode[0] ^= 1; + opcode[1] = 3; + opcode[2] = 0xe9; + where_to_put_displacement = &opcode[3]; + break; + + default: + BAD_CASE (fragP->fr_subtype); + break; + } + } + + /* If size if less then four we are sure that the operand fits, + but if it's 4, then it could be that the displacement is larger + then -/+ 2GB. */ + if (DISP_SIZE_FROM_RELAX_STATE (fragP->fr_subtype) == 4 + && object_64bit + && ((addressT) (displacement_from_opcode_start - extension + + ((addressT) 1 << 31)) + > (((addressT) 2 << 31) - 1))) + { + as_bad_where (fragP->fr_file, fragP->fr_line, + _("jump target out of range")); + /* Make us emit 0. */ + displacement_from_opcode_start = extension; + } + /* Now put displacement after opcode. */ + md_number_to_chars ((char *) where_to_put_displacement, + (valueT) (displacement_from_opcode_start - extension), + DISP_SIZE_FROM_RELAX_STATE (fragP->fr_subtype)); + fragP->fr_fix += extension; +} + +/* Apply a fixup (fixP) to segment data, once it has been determined + by our caller that we have all the info we need to fix it up. + + Parameter valP is the pointer to the value of the bits. + + On the 386, immediates, displacements, and data pointers are all in + the same (little-endian) format, so we don't need to care about which + we are handling. */ + +void +md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) +{ + char *p = fixP->fx_where + fixP->fx_frag->fr_literal; + valueT value = *valP; + +#if !defined (TE_Mach) + if (fixP->fx_pcrel) + { + switch (fixP->fx_r_type) + { + default: + break; + + case BFD_RELOC_64: + fixP->fx_r_type = BFD_RELOC_64_PCREL; + break; + case BFD_RELOC_32: + case BFD_RELOC_X86_64_32S: + fixP->fx_r_type = BFD_RELOC_32_PCREL; + break; + case BFD_RELOC_16: + fixP->fx_r_type = BFD_RELOC_16_PCREL; + break; + case BFD_RELOC_8: + fixP->fx_r_type = BFD_RELOC_8_PCREL; + break; + } + } + + if (fixP->fx_addsy != NULL + && (fixP->fx_r_type == BFD_RELOC_32_PCREL + || fixP->fx_r_type == BFD_RELOC_64_PCREL + || fixP->fx_r_type == BFD_RELOC_16_PCREL + || fixP->fx_r_type == BFD_RELOC_8_PCREL + || fixP->fx_r_type == BFD_RELOC_X86_64_PC32_BND) + && !use_rela_relocations) + { + /* This is a hack. There should be a better way to handle this. + This covers for the fact that bfd_install_relocation will + subtract the current location (for partial_inplace, PC relative + relocations); see more below. */ +#ifndef OBJ_AOUT + if (IS_ELF +#ifdef TE_PE + || OUTPUT_FLAVOR == bfd_target_coff_flavour +#endif + ) + value += fixP->fx_where + fixP->fx_frag->fr_address; +#endif +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + if (IS_ELF) + { + segT sym_seg = S_GET_SEGMENT (fixP->fx_addsy); + + if ((sym_seg == seg + || (symbol_section_p (fixP->fx_addsy) + && sym_seg != absolute_section)) + && !generic_force_reloc (fixP)) + { + /* Yes, we add the values in twice. This is because + bfd_install_relocation subtracts them out again. I think + bfd_install_relocation is broken, but I don't dare change + it. FIXME. */ + value += fixP->fx_where + fixP->fx_frag->fr_address; + } + } +#endif +#if defined (OBJ_COFF) && defined (TE_PE) + /* For some reason, the PE format does not store a + section address offset for a PC relative symbol. */ + if (S_GET_SEGMENT (fixP->fx_addsy) != seg + || S_IS_WEAK (fixP->fx_addsy)) + value += md_pcrel_from (fixP); +#endif + } +#if defined (OBJ_COFF) && defined (TE_PE) + if (fixP->fx_addsy != NULL && S_IS_WEAK (fixP->fx_addsy)) + { + value -= S_GET_VALUE (fixP->fx_addsy); + } +#endif + + /* Fix a few things - the dynamic linker expects certain values here, + and we must not disappoint it. */ +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + if (IS_ELF && fixP->fx_addsy) + switch (fixP->fx_r_type) + { + case BFD_RELOC_386_PLT32: + case BFD_RELOC_X86_64_PLT32: + case BFD_RELOC_X86_64_PLT32_BND: + /* Make the jump instruction point to the address of the operand. At + runtime we merely add the offset to the actual PLT entry. */ + value = -4; + break; + + case BFD_RELOC_386_TLS_GD: + case BFD_RELOC_386_TLS_LDM: + case BFD_RELOC_386_TLS_IE_32: + case BFD_RELOC_386_TLS_IE: + case BFD_RELOC_386_TLS_GOTIE: + case BFD_RELOC_386_TLS_GOTDESC: + case BFD_RELOC_X86_64_TLSGD: + case BFD_RELOC_X86_64_TLSLD: + case BFD_RELOC_X86_64_GOTTPOFF: + case BFD_RELOC_X86_64_GOTPC32_TLSDESC: + value = 0; /* Fully resolved at runtime. No addend. */ + /* Fallthrough */ + case BFD_RELOC_386_TLS_LE: + case BFD_RELOC_386_TLS_LDO_32: + case BFD_RELOC_386_TLS_LE_32: + case BFD_RELOC_X86_64_DTPOFF32: + case BFD_RELOC_X86_64_DTPOFF64: + case BFD_RELOC_X86_64_TPOFF32: + case BFD_RELOC_X86_64_TPOFF64: + S_SET_THREAD_LOCAL (fixP->fx_addsy); + break; + + case BFD_RELOC_386_TLS_DESC_CALL: + case BFD_RELOC_X86_64_TLSDESC_CALL: + value = 0; /* Fully resolved at runtime. No addend. */ + S_SET_THREAD_LOCAL (fixP->fx_addsy); + fixP->fx_done = 0; + return; + + case BFD_RELOC_386_GOT32: + case BFD_RELOC_X86_64_GOT32: + value = 0; /* Fully resolved at runtime. No addend. */ + break; + + case BFD_RELOC_VTABLE_INHERIT: + case BFD_RELOC_VTABLE_ENTRY: + fixP->fx_done = 0; + return; + + default: + break; + } +#endif /* defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) */ + *valP = value; +#endif /* !defined (TE_Mach) */ + + /* Are we finished with this relocation now? */ + if (fixP->fx_addsy == NULL) + fixP->fx_done = 1; +#if defined (OBJ_COFF) && defined (TE_PE) + else if (fixP->fx_addsy != NULL && S_IS_WEAK (fixP->fx_addsy)) + { + fixP->fx_done = 0; + /* Remember value for tc_gen_reloc. */ + fixP->fx_addnumber = value; + /* Clear out the frag for now. */ + value = 0; + } +#endif + else if (use_rela_relocations) + { + fixP->fx_no_overflow = 1; + /* Remember value for tc_gen_reloc. */ + fixP->fx_addnumber = value; + value = 0; + } + + md_number_to_chars (p, value, fixP->fx_size); +} + +char * +md_atof (int type, char *litP, int *sizeP) +{ + /* This outputs the LITTLENUMs in REVERSE order; + in accord with the bigendian 386. */ + return ieee_md_atof (type, litP, sizeP, FALSE); +} + +static char output_invalid_buf[sizeof (unsigned char) * 2 + 6]; + +static char * +output_invalid (int c) +{ + if (ISPRINT (c)) + snprintf (output_invalid_buf, sizeof (output_invalid_buf), + "'%c'", c); + else + snprintf (output_invalid_buf, sizeof (output_invalid_buf), + "(0x%x)", (unsigned char) c); + return output_invalid_buf; +} + +/* REG_STRING starts *before* REGISTER_PREFIX. */ + +static const reg_entry * +parse_real_register (char *reg_string, char **end_op) +{ + char *s = reg_string; + char *p; + char reg_name_given[MAX_REG_NAME_SIZE + 1]; + const reg_entry *r; + + /* Skip possible REGISTER_PREFIX and possible whitespace. */ + if (*s == REGISTER_PREFIX) + ++s; + + if (is_space_char (*s)) + ++s; + + p = reg_name_given; + while ((*p++ = register_chars[(unsigned char) *s]) != '\0') + { + if (p >= reg_name_given + MAX_REG_NAME_SIZE) + return (const reg_entry *) NULL; + s++; + } + + /* For naked regs, make sure that we are not dealing with an identifier. + This prevents confusing an identifier like `eax_var' with register + `eax'. */ + if (allow_naked_reg && identifier_chars[(unsigned char) *s]) + return (const reg_entry *) NULL; + + *end_op = s; + + r = (const reg_entry *) hash_find (reg_hash, reg_name_given); + + /* Handle floating point regs, allowing spaces in the (i) part. */ + if (r == i386_regtab /* %st is first entry of table */) + { + if (is_space_char (*s)) + ++s; + if (*s == '(') + { + ++s; + if (is_space_char (*s)) + ++s; + if (*s >= '0' && *s <= '7') + { + int fpr = *s - '0'; + ++s; + if (is_space_char (*s)) + ++s; + if (*s == ')') + { + *end_op = s + 1; + r = (const reg_entry *) hash_find (reg_hash, "st(0)"); + know (r); + return r + fpr; + } + } + /* We have "%st(" then garbage. */ + return (const reg_entry *) NULL; + } + } + + if (r == NULL || allow_pseudo_reg) + return r; + + if (operand_type_all_zero (&r->reg_type)) + return (const reg_entry *) NULL; + + if ((r->reg_type.bitfield.reg32 + || r->reg_type.bitfield.sreg3 + || r->reg_type.bitfield.control + || r->reg_type.bitfield.debug + || r->reg_type.bitfield.test) + && !cpu_arch_flags.bitfield.cpui386) + return (const reg_entry *) NULL; + + if (r->reg_type.bitfield.floatreg + && !cpu_arch_flags.bitfield.cpu8087 + && !cpu_arch_flags.bitfield.cpu287 + && !cpu_arch_flags.bitfield.cpu387) + return (const reg_entry *) NULL; + + if (r->reg_type.bitfield.regmmx && !cpu_arch_flags.bitfield.cpummx) + return (const reg_entry *) NULL; + + if (r->reg_type.bitfield.regxmm && !cpu_arch_flags.bitfield.cpusse) + return (const reg_entry *) NULL; + + if (r->reg_type.bitfield.regymm && !cpu_arch_flags.bitfield.cpuavx) + return (const reg_entry *) NULL; + + if ((r->reg_type.bitfield.regzmm || r->reg_type.bitfield.regmask) + && !cpu_arch_flags.bitfield.cpuavx512f) + return (const reg_entry *) NULL; + + /* Don't allow fake index register unless allow_index_reg isn't 0. */ + if (!allow_index_reg + && (r->reg_num == RegEiz || r->reg_num == RegRiz)) + return (const reg_entry *) NULL; + + /* Upper 16 vector register is only available with VREX in 64bit + mode. */ + if ((r->reg_flags & RegVRex)) + { + if (!cpu_arch_flags.bitfield.cpuvrex + || flag_code != CODE_64BIT) + return (const reg_entry *) NULL; + + i.need_vrex = 1; + } + + if (((r->reg_flags & (RegRex64 | RegRex)) + || r->reg_type.bitfield.reg64) + && (!cpu_arch_flags.bitfield.cpulm + || !operand_type_equal (&r->reg_type, &control)) + && flag_code != CODE_64BIT) + return (const reg_entry *) NULL; + + if (r->reg_type.bitfield.sreg3 && r->reg_num == RegFlat && !intel_syntax) + return (const reg_entry *) NULL; + + return r; +} + +/* REG_STRING starts *before* REGISTER_PREFIX. */ + +static const reg_entry * +parse_register (char *reg_string, char **end_op) +{ + const reg_entry *r; + + if (*reg_string == REGISTER_PREFIX || allow_naked_reg) + r = parse_real_register (reg_string, end_op); + else + r = NULL; + if (!r) + { + char *save = input_line_pointer; + char c; + symbolS *symbolP; + + input_line_pointer = reg_string; + c = get_symbol_end (); + symbolP = symbol_find (reg_string); + if (symbolP && S_GET_SEGMENT (symbolP) == reg_section) + { + const expressionS *e = symbol_get_value_expression (symbolP); + + know (e->X_op == O_register); + know (e->X_add_number >= 0 + && (valueT) e->X_add_number < i386_regtab_size); + r = i386_regtab + e->X_add_number; + *end_op = input_line_pointer; + } + *input_line_pointer = c; + input_line_pointer = save; + } + return r; +} + +int +i386_parse_name (char *name, expressionS *e, char *nextcharP) +{ + const reg_entry *r; + char *end = input_line_pointer; + + *end = *nextcharP; + r = parse_register (name, &input_line_pointer); + if (r && end <= input_line_pointer) + { + *nextcharP = *input_line_pointer; + *input_line_pointer = 0; + e->X_op = O_register; + e->X_add_number = r - i386_regtab; + return 1; + } + input_line_pointer = end; + *end = 0; + return intel_syntax ? i386_intel_parse_name (name, e) : 0; +} + +void +md_operand (expressionS *e) +{ + char *end; + const reg_entry *r; + + switch (*input_line_pointer) + { + case REGISTER_PREFIX: + r = parse_real_register (input_line_pointer, &end); + if (r) + { + e->X_op = O_register; + e->X_add_number = r - i386_regtab; + input_line_pointer = end; + } + break; + + case '[': + gas_assert (intel_syntax); + end = input_line_pointer++; + expression (e); + if (*input_line_pointer == ']') + { + ++input_line_pointer; + e->X_op_symbol = make_expr_symbol (e); + e->X_add_symbol = NULL; + e->X_add_number = 0; + e->X_op = O_index; + } + else + { + e->X_op = O_absent; + input_line_pointer = end; + } + break; + } +} + + +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) +const char *md_shortopts = "kVQ:sqn"; +#else +const char *md_shortopts = "qn"; +#endif + +#define OPTION_32 (OPTION_MD_BASE + 0) +#define OPTION_64 (OPTION_MD_BASE + 1) +#define OPTION_DIVIDE (OPTION_MD_BASE + 2) +#define OPTION_MARCH (OPTION_MD_BASE + 3) +#define OPTION_MTUNE (OPTION_MD_BASE + 4) +#define OPTION_MMNEMONIC (OPTION_MD_BASE + 5) +#define OPTION_MSYNTAX (OPTION_MD_BASE + 6) +#define OPTION_MINDEX_REG (OPTION_MD_BASE + 7) +#define OPTION_MNAKED_REG (OPTION_MD_BASE + 8) +#define OPTION_MOLD_GCC (OPTION_MD_BASE + 9) +#define OPTION_MSSE2AVX (OPTION_MD_BASE + 10) +#define OPTION_MSSE_CHECK (OPTION_MD_BASE + 11) +#define OPTION_MOPERAND_CHECK (OPTION_MD_BASE + 12) +#define OPTION_MAVXSCALAR (OPTION_MD_BASE + 13) +#define OPTION_X32 (OPTION_MD_BASE + 14) +#define OPTION_MADD_BND_PREFIX (OPTION_MD_BASE + 15) +#define OPTION_MEVEXLIG (OPTION_MD_BASE + 16) +#define OPTION_MEVEXWIG (OPTION_MD_BASE + 17) + +struct option md_longopts[] = +{ + {"32", no_argument, NULL, OPTION_32}, +#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \ + || defined (TE_PE) || defined (TE_PEP) || defined (OBJ_MACH_O)) + {"64", no_argument, NULL, OPTION_64}, +#endif +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + {"x32", no_argument, NULL, OPTION_X32}, +#endif + {"divide", no_argument, NULL, OPTION_DIVIDE}, + {"march", required_argument, NULL, OPTION_MARCH}, + {"mtune", required_argument, NULL, OPTION_MTUNE}, + {"mmnemonic", required_argument, NULL, OPTION_MMNEMONIC}, + {"msyntax", required_argument, NULL, OPTION_MSYNTAX}, + {"mindex-reg", no_argument, NULL, OPTION_MINDEX_REG}, + {"mnaked-reg", no_argument, NULL, OPTION_MNAKED_REG}, + {"mold-gcc", no_argument, NULL, OPTION_MOLD_GCC}, + {"msse2avx", no_argument, NULL, OPTION_MSSE2AVX}, + {"msse-check", required_argument, NULL, OPTION_MSSE_CHECK}, + {"moperand-check", required_argument, NULL, OPTION_MOPERAND_CHECK}, + {"mavxscalar", required_argument, NULL, OPTION_MAVXSCALAR}, + {"madd-bnd-prefix", no_argument, NULL, OPTION_MADD_BND_PREFIX}, + {"mevexlig", required_argument, NULL, OPTION_MEVEXLIG}, + {"mevexwig", required_argument, NULL, OPTION_MEVEXWIG}, + {NULL, no_argument, NULL, 0} +}; +size_t md_longopts_size = sizeof (md_longopts); + +int +md_parse_option (int c, char *arg) +{ + unsigned int j; + char *arch, *next; + + switch (c) + { + case 'n': + optimize_align_code = 0; + break; + + case 'q': + quiet_warnings = 1; + break; + +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section + should be emitted or not. FIXME: Not implemented. */ + case 'Q': + break; + + /* -V: SVR4 argument to print version ID. */ + case 'V': + print_version_id (); + break; + + /* -k: Ignore for FreeBSD compatibility. */ + case 'k': + break; + + case 's': + /* -s: On i386 Solaris, this tells the native assembler to use + .stab instead of .stab.excl. We always use .stab anyhow. */ + break; +#endif +#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \ + || defined (TE_PE) || defined (TE_PEP) || defined (OBJ_MACH_O)) + case OPTION_64: + { + const char **list, **l; + + list = bfd_target_list (); + for (l = list; *l != NULL; l++) + if (CONST_STRNEQ (*l, "elf64-x86-64") + || strcmp (*l, "coff-x86-64") == 0 + || strcmp (*l, "pe-x86-64") == 0 + || strcmp (*l, "pei-x86-64") == 0 + || strcmp (*l, "mach-o-x86-64") == 0) + { + default_arch = "x86_64"; + break; + } + if (*l == NULL) + as_fatal (_("no compiled in support for x86_64")); + free (list); + } + break; +#endif + +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + case OPTION_X32: + if (IS_ELF) + { + const char **list, **l; + + list = bfd_target_list (); + for (l = list; *l != NULL; l++) + if (CONST_STRNEQ (*l, "elf32-x86-64")) + { + default_arch = "x86_64:32"; + break; + } + if (*l == NULL) + as_fatal (_("no compiled in support for 32bit x86_64")); + free (list); + } + else + as_fatal (_("32bit x86_64 is only supported for ELF")); + break; +#endif + + case OPTION_32: + default_arch = "i386"; + break; + + case OPTION_DIVIDE: +#ifdef SVR4_COMMENT_CHARS + { + char *n, *t; + const char *s; + + n = (char *) xmalloc (strlen (i386_comment_chars) + 1); + t = n; + for (s = i386_comment_chars; *s != '\0'; s++) + if (*s != '/') + *t++ = *s; + *t = '\0'; + i386_comment_chars = n; + } +#endif + break; + + case OPTION_MARCH: + arch = xstrdup (arg); + do + { + if (*arch == '.') + as_fatal (_("invalid -march= option: `%s'"), arg); + next = strchr (arch, '+'); + if (next) + *next++ = '\0'; + for (j = 0; j < ARRAY_SIZE (cpu_arch); j++) + { + if (strcmp (arch, cpu_arch [j].name) == 0) + { + /* Processor. */ + if (! cpu_arch[j].flags.bitfield.cpui386) + continue; + + cpu_arch_name = cpu_arch[j].name; + cpu_sub_arch_name = NULL; + cpu_arch_flags = cpu_arch[j].flags; + cpu_arch_isa = cpu_arch[j].type; + cpu_arch_isa_flags = cpu_arch[j].flags; + if (!cpu_arch_tune_set) + { + cpu_arch_tune = cpu_arch_isa; + cpu_arch_tune_flags = cpu_arch_isa_flags; + } + break; + } + else if (*cpu_arch [j].name == '.' + && strcmp (arch, cpu_arch [j].name + 1) == 0) + { + /* ISA entension. */ + i386_cpu_flags flags; + + if (!cpu_arch[j].negated) + flags = cpu_flags_or (cpu_arch_flags, + cpu_arch[j].flags); + else + flags = cpu_flags_and_not (cpu_arch_flags, + cpu_arch[j].flags); + if (!cpu_flags_equal (&flags, &cpu_arch_flags)) + { + if (cpu_sub_arch_name) + { + char *name = cpu_sub_arch_name; + cpu_sub_arch_name = concat (name, + cpu_arch[j].name, + (const char *) NULL); + free (name); + } + else + cpu_sub_arch_name = xstrdup (cpu_arch[j].name); + cpu_arch_flags = flags; + cpu_arch_isa_flags = flags; + } + break; + } + } + + if (j >= ARRAY_SIZE (cpu_arch)) + as_fatal (_("invalid -march= option: `%s'"), arg); + + arch = next; + } + while (next != NULL ); + break; + + case OPTION_MTUNE: + if (*arg == '.') + as_fatal (_("invalid -mtune= option: `%s'"), arg); + for (j = 0; j < ARRAY_SIZE (cpu_arch); j++) + { + if (strcmp (arg, cpu_arch [j].name) == 0) + { + cpu_arch_tune_set = 1; + cpu_arch_tune = cpu_arch [j].type; + cpu_arch_tune_flags = cpu_arch[j].flags; + break; + } + } + if (j >= ARRAY_SIZE (cpu_arch)) + as_fatal (_("invalid -mtune= option: `%s'"), arg); + break; + + case OPTION_MMNEMONIC: + if (strcasecmp (arg, "att") == 0) + intel_mnemonic = 0; + else if (strcasecmp (arg, "intel") == 0) + intel_mnemonic = 1; + else + as_fatal (_("invalid -mmnemonic= option: `%s'"), arg); + break; + + case OPTION_MSYNTAX: + if (strcasecmp (arg, "att") == 0) + intel_syntax = 0; + else if (strcasecmp (arg, "intel") == 0) + intel_syntax = 1; + else + as_fatal (_("invalid -msyntax= option: `%s'"), arg); + break; + + case OPTION_MINDEX_REG: + allow_index_reg = 1; + break; + + case OPTION_MNAKED_REG: + allow_naked_reg = 1; + break; + + case OPTION_MOLD_GCC: + old_gcc = 1; + break; + + case OPTION_MSSE2AVX: + sse2avx = 1; + break; + + case OPTION_MSSE_CHECK: + if (strcasecmp (arg, "error") == 0) + sse_check = check_error; + else if (strcasecmp (arg, "warning") == 0) + sse_check = check_warning; + else if (strcasecmp (arg, "none") == 0) + sse_check = check_none; + else + as_fatal (_("invalid -msse-check= option: `%s'"), arg); + break; + + case OPTION_MOPERAND_CHECK: + if (strcasecmp (arg, "error") == 0) + operand_check = check_error; + else if (strcasecmp (arg, "warning") == 0) + operand_check = check_warning; + else if (strcasecmp (arg, "none") == 0) + operand_check = check_none; + else + as_fatal (_("invalid -moperand-check= option: `%s'"), arg); + break; + + case OPTION_MAVXSCALAR: + if (strcasecmp (arg, "128") == 0) + avxscalar = vex128; + else if (strcasecmp (arg, "256") == 0) + avxscalar = vex256; + else + as_fatal (_("invalid -mavxscalar= option: `%s'"), arg); + break; + + case OPTION_MADD_BND_PREFIX: + add_bnd_prefix = 1; + break; + + case OPTION_MEVEXLIG: + if (strcmp (arg, "128") == 0) + evexlig = evexl128; + else if (strcmp (arg, "256") == 0) + evexlig = evexl256; + else if (strcmp (arg, "512") == 0) + evexlig = evexl512; + else + as_fatal (_("invalid -mevexlig= option: `%s'"), arg); + break; + + case OPTION_MEVEXWIG: + if (strcmp (arg, "0") == 0) + evexwig = evexw0; + else if (strcmp (arg, "1") == 0) + evexwig = evexw1; + else + as_fatal (_("invalid -mevexwig= option: `%s'"), arg); + break; + + default: + return 0; + } + return 1; +} + +#define MESSAGE_TEMPLATE \ +" " + +static void +show_arch (FILE *stream, int ext, int check) +{ + static char message[] = MESSAGE_TEMPLATE; + char *start = message + 27; + char *p; + int size = sizeof (MESSAGE_TEMPLATE); + int left; + const char *name; + int len; + unsigned int j; + + p = start; + left = size - (start - message); + for (j = 0; j < ARRAY_SIZE (cpu_arch); j++) + { + /* Should it be skipped? */ + if (cpu_arch [j].skip) + continue; + + name = cpu_arch [j].name; + len = cpu_arch [j].len; + if (*name == '.') + { + /* It is an extension. Skip if we aren't asked to show it. */ + if (ext) + { + name++; + len--; + } + else + continue; + } + else if (ext) + { + /* It is an processor. Skip if we show only extension. */ + continue; + } + else if (check && ! cpu_arch[j].flags.bitfield.cpui386) + { + /* It is an impossible processor - skip. */ + continue; + } + + /* Reserve 2 spaces for ", " or ",\0" */ + left -= len + 2; + + /* Check if there is any room. */ + if (left >= 0) + { + if (p != start) + { + *p++ = ','; + *p++ = ' '; + } + p = mempcpy (p, name, len); + } + else + { + /* Output the current message now and start a new one. */ + *p++ = ','; + *p = '\0'; + fprintf (stream, "%s\n", message); + p = start; + left = size - (start - message) - len - 2; + + gas_assert (left >= 0); + + p = mempcpy (p, name, len); + } + } + + *p = '\0'; + fprintf (stream, "%s\n", message); +} + +void +md_show_usage (FILE *stream) +{ +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + fprintf (stream, _("\ + -Q ignored\n\ + -V print assembler version number\n\ + -k ignored\n")); +#endif + fprintf (stream, _("\ + -n Do not optimize code alignment\n\ + -q quieten some warnings\n")); +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + fprintf (stream, _("\ + -s ignored\n")); +#endif +#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \ + || defined (TE_PE) || defined (TE_PEP)) + fprintf (stream, _("\ + --32/--64/--x32 generate 32bit/64bit/x32 code\n")); +#endif +#ifdef SVR4_COMMENT_CHARS + fprintf (stream, _("\ + --divide do not treat `/' as a comment character\n")); +#else + fprintf (stream, _("\ + --divide ignored\n")); +#endif + fprintf (stream, _("\ + -march=CPU[,+EXTENSION...]\n\ + generate code for CPU and EXTENSION, CPU is one of:\n")); + show_arch (stream, 0, 1); + fprintf (stream, _("\ + EXTENSION is combination of:\n")); + show_arch (stream, 1, 0); + fprintf (stream, _("\ + -mtune=CPU optimize for CPU, CPU is one of:\n")); + show_arch (stream, 0, 0); + fprintf (stream, _("\ + -msse2avx encode SSE instructions with VEX prefix\n")); + fprintf (stream, _("\ + -msse-check=[none|error|warning]\n\ + check SSE instructions\n")); + fprintf (stream, _("\ + -moperand-check=[none|error|warning]\n\ + check operand combinations for validity\n")); + fprintf (stream, _("\ + -mavxscalar=[128|256] encode scalar AVX instructions with specific vector\n\ + length\n")); + fprintf (stream, _("\ + -mevexlig=[128|256|512] encode scalar EVEX instructions with specific vector\n\ + length\n")); + fprintf (stream, _("\ + -mevexwig=[0|1] encode EVEX instructions with specific EVEX.W value\n\ + for EVEX.W bit ignored instructions\n")); + fprintf (stream, _("\ + -mmnemonic=[att|intel] use AT&T/Intel mnemonic\n")); + fprintf (stream, _("\ + -msyntax=[att|intel] use AT&T/Intel syntax\n")); + fprintf (stream, _("\ + -mindex-reg support pseudo index registers\n")); + fprintf (stream, _("\ + -mnaked-reg don't require `%%' prefix for registers\n")); + fprintf (stream, _("\ + -mold-gcc support old (<= 2.8.1) versions of gcc\n")); + fprintf (stream, _("\ + -madd-bnd-prefix add BND prefix for all valid branches\n")); +} + +#if ((defined (OBJ_MAYBE_COFF) && defined (OBJ_MAYBE_AOUT)) \ + || defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \ + || defined (TE_PE) || defined (TE_PEP) || defined (OBJ_MACH_O)) + +/* Pick the target format to use. */ + +const char * +i386_target_format (void) +{ + if (!strncmp (default_arch, "x86_64", 6)) + { + update_code_flag (CODE_64BIT, 1); + if (default_arch[6] == '\0') + x86_elf_abi = X86_64_ABI; + else + x86_elf_abi = X86_64_X32_ABI; + } + else if (!strcmp (default_arch, "i386")) + update_code_flag (CODE_32BIT, 1); + else + as_fatal (_("unknown architecture")); + + if (cpu_flags_all_zero (&cpu_arch_isa_flags)) + cpu_arch_isa_flags = cpu_arch[flag_code == CODE_64BIT].flags; + if (cpu_flags_all_zero (&cpu_arch_tune_flags)) + cpu_arch_tune_flags = cpu_arch[flag_code == CODE_64BIT].flags; + + switch (OUTPUT_FLAVOR) + { +#if defined (OBJ_MAYBE_AOUT) || defined (OBJ_AOUT) + case bfd_target_aout_flavour: + return AOUT_TARGET_FORMAT; +#endif +#if defined (OBJ_MAYBE_COFF) || defined (OBJ_COFF) +# if defined (TE_PE) || defined (TE_PEP) + case bfd_target_coff_flavour: + return flag_code == CODE_64BIT ? "pe-x86-64" : "pe-i386"; +# elif defined (TE_GO32) + case bfd_target_coff_flavour: + return "coff-go32"; +# else + case bfd_target_coff_flavour: + return "coff-i386"; +# endif +#endif +#if defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF) + case bfd_target_elf_flavour: + { + const char *format; + + switch (x86_elf_abi) + { + default: + format = ELF_TARGET_FORMAT; + break; + case X86_64_ABI: + use_rela_relocations = 1; + object_64bit = 1; + format = ELF_TARGET_FORMAT64; + break; + case X86_64_X32_ABI: + use_rela_relocations = 1; + object_64bit = 1; + disallow_64bit_reloc = 1; + format = ELF_TARGET_FORMAT32; + break; + } + if (cpu_arch_isa == PROCESSOR_L1OM) + { + if (x86_elf_abi != X86_64_ABI) + as_fatal (_("Intel L1OM is 64bit only")); + return ELF_TARGET_L1OM_FORMAT; + } + if (cpu_arch_isa == PROCESSOR_K1OM) + { + if (x86_elf_abi != X86_64_ABI) + as_fatal (_("Intel K1OM is 64bit only")); + return ELF_TARGET_K1OM_FORMAT; + } + else + return format; + } +#endif +#if defined (OBJ_MACH_O) + case bfd_target_mach_o_flavour: + if (flag_code == CODE_64BIT) + { + use_rela_relocations = 1; + object_64bit = 1; + return "mach-o-x86-64"; + } + else + return "mach-o-i386"; +#endif + default: + abort (); + return NULL; + } +} + +#endif /* OBJ_MAYBE_ more than one */ + +#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) +void +i386_elf_emit_arch_note (void) +{ + if (IS_ELF && cpu_arch_name != NULL) + { + char *p; + asection *seg = now_seg; + subsegT subseg = now_subseg; + Elf_Internal_Note i_note; + Elf_External_Note e_note; + asection *note_secp; + int len; + + /* Create the .note section. */ + note_secp = subseg_new (".note", 0); + bfd_set_section_flags (stdoutput, + note_secp, + SEC_HAS_CONTENTS | SEC_READONLY); + + /* Process the arch string. */ + len = strlen (cpu_arch_name); + + i_note.namesz = len + 1; + i_note.descsz = 0; + i_note.type = NT_ARCH; + p = frag_more (sizeof (e_note.namesz)); + md_number_to_chars (p, (valueT) i_note.namesz, sizeof (e_note.namesz)); + p = frag_more (sizeof (e_note.descsz)); + md_number_to_chars (p, (valueT) i_note.descsz, sizeof (e_note.descsz)); + p = frag_more (sizeof (e_note.type)); + md_number_to_chars (p, (valueT) i_note.type, sizeof (e_note.type)); + p = frag_more (len + 1); + strcpy (p, cpu_arch_name); + + frag_align (2, 0, 0); + + subseg_set (seg, subseg); + } +} +#endif + +symbolS * +md_undefined_symbol (char *name) +{ + if (name[0] == GLOBAL_OFFSET_TABLE_NAME[0] + && name[1] == GLOBAL_OFFSET_TABLE_NAME[1] + && name[2] == GLOBAL_OFFSET_TABLE_NAME[2] + && strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0) + { + if (!GOT_symbol) + { + if (symbol_find (name)) + as_bad (_("GOT already in symbol table")); + GOT_symbol = symbol_new (name, undefined_section, + (valueT) 0, &zero_address_frag); + }; + return GOT_symbol; + } + return 0; +} + +/* Round up a section size to the appropriate boundary. */ + +valueT +md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size) +{ +#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT)) + if (OUTPUT_FLAVOR == bfd_target_aout_flavour) + { + /* For a.out, force the section size to be aligned. If we don't do + this, BFD will align it for us, but it will not write out the + final bytes of the section. This may be a bug in BFD, but it is + easier to fix it here since that is how the other a.out targets + work. */ + int align; + + align = bfd_get_section_alignment (stdoutput, segment); + size = ((size + (1 << align) - 1) & ((valueT) -1 << align)); + } +#endif + + return size; +} + +/* On the i386, PC-relative offsets are relative to the start of the + next instruction. That is, the address of the offset, plus its + size, since the offset is always the last part of the insn. */ + +long +md_pcrel_from (fixS *fixP) +{ + return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address; +} + +#ifndef I386COFF + +static void +s_bss (int ignore ATTRIBUTE_UNUSED) +{ + int temp; + +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + if (IS_ELF) + obj_elf_section_change_hook (); +#endif + temp = get_absolute_expression (); + subseg_set (bss_section, (subsegT) temp); + demand_empty_rest_of_line (); +} + +#endif + +void +i386_validate_fix (fixS *fixp) +{ + if (fixp->fx_subsy && fixp->fx_subsy == GOT_symbol) + { + if (fixp->fx_r_type == BFD_RELOC_32_PCREL) + { + if (!object_64bit) + abort (); + fixp->fx_r_type = BFD_RELOC_X86_64_GOTPCREL; + } + else + { + if (!object_64bit) + fixp->fx_r_type = BFD_RELOC_386_GOTOFF; + else + fixp->fx_r_type = BFD_RELOC_X86_64_GOTOFF64; + } + fixp->fx_subsy = 0; + } +} + +arelent * +tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp) +{ + arelent *rel; + bfd_reloc_code_real_type code; + + switch (fixp->fx_r_type) + { +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + case BFD_RELOC_SIZE32: + case BFD_RELOC_SIZE64: + if (S_IS_DEFINED (fixp->fx_addsy) + && !S_IS_EXTERNAL (fixp->fx_addsy)) + { + /* Resolve size relocation against local symbol to size of + the symbol plus addend. */ + valueT value = S_GET_SIZE (fixp->fx_addsy) + fixp->fx_offset; + if (fixp->fx_r_type == BFD_RELOC_SIZE32 + && !fits_in_unsigned_long (value)) + as_bad_where (fixp->fx_file, fixp->fx_line, + _("symbol size computation overflow")); + fixp->fx_addsy = NULL; + fixp->fx_subsy = NULL; + md_apply_fix (fixp, (valueT *) &value, NULL); + return NULL; + } +#endif + + case BFD_RELOC_X86_64_PLT32: + case BFD_RELOC_X86_64_PLT32_BND: + case BFD_RELOC_X86_64_GOT32: + case BFD_RELOC_X86_64_GOTPCREL: + case BFD_RELOC_386_PLT32: + case BFD_RELOC_386_GOT32: + case BFD_RELOC_386_GOTOFF: + case BFD_RELOC_386_GOTPC: + case BFD_RELOC_386_TLS_GD: + case BFD_RELOC_386_TLS_LDM: + case BFD_RELOC_386_TLS_LDO_32: + case BFD_RELOC_386_TLS_IE_32: + case BFD_RELOC_386_TLS_IE: + case BFD_RELOC_386_TLS_GOTIE: + case BFD_RELOC_386_TLS_LE_32: + case BFD_RELOC_386_TLS_LE: + case BFD_RELOC_386_TLS_GOTDESC: + case BFD_RELOC_386_TLS_DESC_CALL: + case BFD_RELOC_X86_64_TLSGD: + case BFD_RELOC_X86_64_TLSLD: + case BFD_RELOC_X86_64_DTPOFF32: + case BFD_RELOC_X86_64_DTPOFF64: + case BFD_RELOC_X86_64_GOTTPOFF: + case BFD_RELOC_X86_64_TPOFF32: + case BFD_RELOC_X86_64_TPOFF64: + case BFD_RELOC_X86_64_GOTOFF64: + case BFD_RELOC_X86_64_GOTPC32: + case BFD_RELOC_X86_64_GOT64: + case BFD_RELOC_X86_64_GOTPCREL64: + case BFD_RELOC_X86_64_GOTPC64: + case BFD_RELOC_X86_64_GOTPLT64: + case BFD_RELOC_X86_64_PLTOFF64: + case BFD_RELOC_X86_64_GOTPC32_TLSDESC: + case BFD_RELOC_X86_64_TLSDESC_CALL: + case BFD_RELOC_RVA: + case BFD_RELOC_VTABLE_ENTRY: + case BFD_RELOC_VTABLE_INHERIT: +#ifdef TE_PE + case BFD_RELOC_32_SECREL: +#endif + code = fixp->fx_r_type; + break; + case BFD_RELOC_X86_64_32S: + if (!fixp->fx_pcrel) + { + /* Don't turn BFD_RELOC_X86_64_32S into BFD_RELOC_32. */ + code = fixp->fx_r_type; + break; + } + default: + if (fixp->fx_pcrel) + { + switch (fixp->fx_size) + { + default: + as_bad_where (fixp->fx_file, fixp->fx_line, + _("can not do %d byte pc-relative relocation"), + fixp->fx_size); + code = BFD_RELOC_32_PCREL; + break; + case 1: code = BFD_RELOC_8_PCREL; break; + case 2: code = BFD_RELOC_16_PCREL; break; + case 4: + code = (fixp->fx_r_type == BFD_RELOC_X86_64_PC32_BND + ? fixp-> fx_r_type : BFD_RELOC_32_PCREL); + break; +#ifdef BFD64 + case 8: code = BFD_RELOC_64_PCREL; break; +#endif + } + } + else + { + switch (fixp->fx_size) + { + default: + as_bad_where (fixp->fx_file, fixp->fx_line, + _("can not do %d byte relocation"), + fixp->fx_size); + code = BFD_RELOC_32; + break; + case 1: code = BFD_RELOC_8; break; + case 2: code = BFD_RELOC_16; break; + case 4: code = BFD_RELOC_32; break; +#ifdef BFD64 + case 8: code = BFD_RELOC_64; break; +#endif + } + } + break; + } + + if ((code == BFD_RELOC_32 + || code == BFD_RELOC_32_PCREL + || code == BFD_RELOC_X86_64_32S) + && GOT_symbol + && fixp->fx_addsy == GOT_symbol) + { + if (!object_64bit) + code = BFD_RELOC_386_GOTPC; + else + code = BFD_RELOC_X86_64_GOTPC32; + } + if ((code == BFD_RELOC_64 || code == BFD_RELOC_64_PCREL) + && GOT_symbol + && fixp->fx_addsy == GOT_symbol) + { + code = BFD_RELOC_X86_64_GOTPC64; + } + + rel = (arelent *) xmalloc (sizeof (arelent)); + rel->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); + *rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); + + rel->address = fixp->fx_frag->fr_address + fixp->fx_where; + + if (!use_rela_relocations) + { + /* HACK: Since i386 ELF uses Rel instead of Rela, encode the + vtable entry to be used in the relocation's section offset. */ + if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + rel->address = fixp->fx_offset; +#if defined (OBJ_COFF) && defined (TE_PE) + else if (fixp->fx_addsy && S_IS_WEAK (fixp->fx_addsy)) + rel->addend = fixp->fx_addnumber - (S_GET_VALUE (fixp->fx_addsy) * 2); + else +#endif + rel->addend = 0; + } + /* Use the rela in 64bit mode. */ + else + { + if (disallow_64bit_reloc) + switch (code) + { + case BFD_RELOC_X86_64_DTPOFF64: + case BFD_RELOC_X86_64_TPOFF64: + case BFD_RELOC_64_PCREL: + case BFD_RELOC_X86_64_GOTOFF64: + case BFD_RELOC_X86_64_GOT64: + case BFD_RELOC_X86_64_GOTPCREL64: + case BFD_RELOC_X86_64_GOTPC64: + case BFD_RELOC_X86_64_GOTPLT64: + case BFD_RELOC_X86_64_PLTOFF64: + as_bad_where (fixp->fx_file, fixp->fx_line, + _("cannot represent relocation type %s in x32 mode"), + bfd_get_reloc_code_name (code)); + break; + default: + break; + } + + if (!fixp->fx_pcrel) + rel->addend = fixp->fx_offset; + else + switch (code) + { + case BFD_RELOC_X86_64_PLT32: + case BFD_RELOC_X86_64_PLT32_BND: + case BFD_RELOC_X86_64_GOT32: + case BFD_RELOC_X86_64_GOTPCREL: + case BFD_RELOC_X86_64_TLSGD: + case BFD_RELOC_X86_64_TLSLD: + case BFD_RELOC_X86_64_GOTTPOFF: + case BFD_RELOC_X86_64_GOTPC32_TLSDESC: + case BFD_RELOC_X86_64_TLSDESC_CALL: + rel->addend = fixp->fx_offset - fixp->fx_size; + break; + default: + rel->addend = (section->vma + - fixp->fx_size + + fixp->fx_addnumber + + md_pcrel_from (fixp)); + break; + } + } + + rel->howto = bfd_reloc_type_lookup (stdoutput, code); + if (rel->howto == NULL) + { + as_bad_where (fixp->fx_file, fixp->fx_line, + _("cannot represent relocation type %s"), + bfd_get_reloc_code_name (code)); + /* Set howto to a garbage value so that we can keep going. */ + rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32); + gas_assert (rel->howto != NULL); + } + + return rel; +} + +#include "tc-i386-intel.c" + +void +tc_x86_parse_to_dw2regnum (expressionS *exp) +{ + int saved_naked_reg; + char saved_register_dot; + + saved_naked_reg = allow_naked_reg; + allow_naked_reg = 1; + saved_register_dot = register_chars['.']; + register_chars['.'] = '.'; + allow_pseudo_reg = 1; + expression_and_evaluate (exp); + allow_pseudo_reg = 0; + register_chars['.'] = saved_register_dot; + allow_naked_reg = saved_naked_reg; + + if (exp->X_op == O_register && exp->X_add_number >= 0) + { + if ((addressT) exp->X_add_number < i386_regtab_size) + { + exp->X_op = O_constant; + exp->X_add_number = i386_regtab[exp->X_add_number] + .dw2_regnum[flag_code >> 1]; + } + else + exp->X_op = O_illegal; + } +} + +void +tc_x86_frame_initial_instructions (void) +{ + static unsigned int sp_regno[2]; + + if (!sp_regno[flag_code >> 1]) + { + char *saved_input = input_line_pointer; + char sp[][4] = {"esp", "rsp"}; + expressionS exp; + + input_line_pointer = sp[flag_code >> 1]; + tc_x86_parse_to_dw2regnum (&exp); + gas_assert (exp.X_op == O_constant); + sp_regno[flag_code >> 1] = exp.X_add_number; + input_line_pointer = saved_input; + } + + cfi_add_CFA_def_cfa (sp_regno[flag_code >> 1], -x86_cie_data_alignment); + cfi_add_CFA_offset (x86_dwarf2_return_column, x86_cie_data_alignment); +} + +int +x86_dwarf2_addr_size (void) +{ +#if defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF) + if (x86_elf_abi == X86_64_X32_ABI) + return 4; +#endif + return bfd_arch_bits_per_address (stdoutput) / 8; +} + +int +i386_elf_section_type (const char *str, size_t len) +{ + if (flag_code == CODE_64BIT + && len == sizeof ("unwind") - 1 + && strncmp (str, "unwind", 6) == 0) + return SHT_X86_64_UNWIND; + + return -1; +} + +#ifdef TE_SOLARIS +void +i386_solaris_fix_up_eh_frame (segT sec) +{ + if (flag_code == CODE_64BIT) + elf_section_type (sec) = SHT_X86_64_UNWIND; +} +#endif + +#ifdef TE_PE +void +tc_pe_dwarf2_emit_offset (symbolS *symbol, unsigned int size) +{ + expressionS exp; + + exp.X_op = O_secrel; + exp.X_add_symbol = symbol; + exp.X_add_number = 0; + emit_expr (&exp, size); +} +#endif + +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) +/* For ELF on x86-64, add support for SHF_X86_64_LARGE. */ + +bfd_vma +x86_64_section_letter (int letter, char **ptr_msg) +{ + if (flag_code == CODE_64BIT) + { + if (letter == 'l') + return SHF_X86_64_LARGE; + + *ptr_msg = _("bad .section directive: want a,l,w,x,M,S,G,T in string"); + } + else + *ptr_msg = _("bad .section directive: want a,w,x,M,S,G,T in string"); + return -1; +} + +bfd_vma +x86_64_section_word (char *str, size_t len) +{ + if (len == 5 && flag_code == CODE_64BIT && CONST_STRNEQ (str, "large")) + return SHF_X86_64_LARGE; + + return -1; +} + +static void +handle_large_common (int small ATTRIBUTE_UNUSED) +{ + if (flag_code != CODE_64BIT) + { + s_comm_internal (0, elf_common_parse); + as_warn (_(".largecomm supported only in 64bit mode, producing .comm")); + } + else + { + static segT lbss_section; + asection *saved_com_section_ptr = elf_com_section_ptr; + asection *saved_bss_section = bss_section; + + if (lbss_section == NULL) + { + flagword applicable; + segT seg = now_seg; + subsegT subseg = now_subseg; + + /* The .lbss section is for local .largecomm symbols. */ + lbss_section = subseg_new (".lbss", 0); + applicable = bfd_applicable_section_flags (stdoutput); + bfd_set_section_flags (stdoutput, lbss_section, + applicable & SEC_ALLOC); + seg_info (lbss_section)->bss = 1; + + subseg_set (seg, subseg); + } + + elf_com_section_ptr = &_bfd_elf_large_com_section; + bss_section = lbss_section; + + s_comm_internal (0, elf_common_parse); + + elf_com_section_ptr = saved_com_section_ptr; + bss_section = saved_bss_section; + } +} +#endif /* OBJ_ELF || OBJ_MAYBE_ELF */ diff --git a/contrib/toolchain/binutils/gas/config/tc-i386.h b/contrib/toolchain/binutils/gas/config/tc-i386.h new file mode 100644 index 0000000000..de132d69d7 --- /dev/null +++ b/contrib/toolchain/binutils/gas/config/tc-i386.h @@ -0,0 +1,342 @@ +/* tc-i386.h -- Header file for tc-i386.c + Copyright 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef TC_I386 +#define TC_I386 1 + +#include "opcodes/i386-opc.h" + +struct fix; + +#define TARGET_BYTES_BIG_ENDIAN 0 + +#define TARGET_ARCH (i386_arch ()) +#define TARGET_MACH (i386_mach ()) +extern enum bfd_architecture i386_arch (void); +extern unsigned long i386_mach (void); + +#ifdef TE_FreeBSD +#define AOUT_TARGET_FORMAT "a.out-i386-freebsd" +#endif +#ifdef TE_NetBSD +#define AOUT_TARGET_FORMAT "a.out-i386-netbsd" +#endif +#ifdef TE_386BSD +#define AOUT_TARGET_FORMAT "a.out-i386-bsd" +#endif +#ifdef TE_LINUX +#define AOUT_TARGET_FORMAT "a.out-i386-linux" +#endif +#ifdef TE_Mach +#define AOUT_TARGET_FORMAT "a.out-mach3" +#endif +#ifdef TE_DYNIX +#define AOUT_TARGET_FORMAT "a.out-i386-dynix" +#endif +#ifndef AOUT_TARGET_FORMAT +#define AOUT_TARGET_FORMAT "a.out-i386" +#endif + +#ifdef TE_FreeBSD +#define ELF_TARGET_FORMAT "elf32-i386-freebsd" +#define ELF_TARGET_FORMAT64 "elf64-x86-64-freebsd" +#elif defined (TE_VXWORKS) +#define ELF_TARGET_FORMAT "elf32-i386-vxworks" +#elif defined (TE_NACL) +#define ELF_TARGET_FORMAT "elf32-i386-nacl" +#define ELF_TARGET_FORMAT32 "elf32-x86-64-nacl" +#define ELF_TARGET_FORMAT64 "elf64-x86-64-nacl" +#endif + +#ifdef TE_SOLARIS +#define ELF_TARGET_FORMAT "elf32-i386-sol2" +#define ELF_TARGET_FORMAT64 "elf64-x86-64-sol2" +#endif + +#ifndef ELF_TARGET_FORMAT +#define ELF_TARGET_FORMAT "elf32-i386" +#endif + +#ifndef ELF_TARGET_FORMAT64 +#define ELF_TARGET_FORMAT64 "elf64-x86-64" +#endif + +#ifndef ELF_TARGET_FORMAT32 +#define ELF_TARGET_FORMAT32 "elf32-x86-64" +#endif + +#ifndef ELF_TARGET_L1OM_FORMAT +#define ELF_TARGET_L1OM_FORMAT "elf64-l1om" +#endif + +#ifndef ELF_TARGET_K1OM_FORMAT +#define ELF_TARGET_K1OM_FORMAT "elf64-k1om" +#endif + +#if ((defined (OBJ_MAYBE_COFF) && defined (OBJ_MAYBE_AOUT)) \ + || defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \ + || defined (TE_PE) || defined (TE_PEP) || defined (OBJ_MACH_O)) +extern const char *i386_target_format (void); +#define TARGET_FORMAT i386_target_format () +#else +#ifdef TE_GO32 +#define TARGET_FORMAT "coff-go32" +#endif +#ifdef OBJ_AOUT +#define TARGET_FORMAT AOUT_TARGET_FORMAT +#endif +#endif + +#if (defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF)) +#define md_end i386_elf_emit_arch_note +extern void i386_elf_emit_arch_note (void); +#endif + +#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 0 + +/* '$' may be used as immediate prefix. */ +#undef LOCAL_LABELS_DOLLAR +#define LOCAL_LABELS_DOLLAR 0 +#undef LOCAL_LABELS_FB +#define LOCAL_LABELS_FB 1 + +extern const char extra_symbol_chars[]; +#define tc_symbol_chars extra_symbol_chars + +extern const char *i386_comment_chars; +#define tc_comment_chars i386_comment_chars + +/* The name of the global offset table generated by the compiler. Allow + this to be overridden if need be. */ +#ifndef GLOBAL_OFFSET_TABLE_NAME +#define GLOBAL_OFFSET_TABLE_NAME "_GLOBAL_OFFSET_TABLE_" +#endif + +#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) && !defined (LEX_AT) +#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) x86_cons (EXP, NBYTES) +#endif +extern void x86_cons (expressionS *, int); + +#define TC_CONS_FIX_NEW(FRAG,OFF,LEN,EXP) x86_cons_fix_new(FRAG, OFF, LEN, EXP) +extern void x86_cons_fix_new + (fragS *, unsigned int, unsigned int, expressionS *); + +#define TC_ADDRESS_BYTES x86_address_bytes +extern int x86_address_bytes (void); + +#define DIFF_EXPR_OK /* foo-. gets turned into PC relative relocs */ + +#define NO_RELOC BFD_RELOC_NONE + +void i386_validate_fix (struct fix *); +#define TC_VALIDATE_FIX(FIX,SEGTYPE,SKIP) i386_validate_fix(FIX) + +#define tc_fix_adjustable(X) tc_i386_fix_adjustable(X) +extern int tc_i386_fix_adjustable (struct fix *); + +/* Values passed to md_apply_fix don't include the symbol value. */ +#define MD_APPLY_SYM_VALUE(FIX) 0 + +/* ELF wants external syms kept, as does PE COFF. */ +#if defined (TE_PE) && defined (STRICT_PE_FORMAT) +#define EXTERN_FORCE_RELOC \ + (OUTPUT_FLAVOR == bfd_target_elf_flavour \ + || OUTPUT_FLAVOR == bfd_target_coff_flavour) +#else +#define EXTERN_FORCE_RELOC \ + (OUTPUT_FLAVOR == bfd_target_elf_flavour) +#endif + +/* This expression evaluates to true if the relocation is for a local + object for which we still want to do the relocation at runtime. + False if we are willing to perform this relocation while building + the .o file. GOTOFF and GOT32 do not need to be checked here because + they are not pcrel. .*/ + +#define TC_FORCE_RELOCATION_LOCAL(FIX) \ + (!(FIX)->fx_pcrel \ + || (FIX)->fx_r_type == BFD_RELOC_386_PLT32 \ + || (FIX)->fx_r_type == BFD_RELOC_386_GOTPC \ + || (FIX)->fx_r_type == BFD_RELOC_X86_64_GOTPCREL \ + || TC_FORCE_RELOCATION (FIX)) + +extern int i386_parse_name (char *, expressionS *, char *); +#define md_parse_name(s, e, m, c) i386_parse_name (s, e, c) + +extern operatorT i386_operator (const char *name, unsigned int operands, char *); +#define md_operator i386_operator + +extern int i386_need_index_operator (void); +#define md_need_index_operator i386_need_index_operator + +#define md_register_arithmetic 0 + +extern const struct relax_type md_relax_table[]; +#define TC_GENERIC_RELAX_TABLE md_relax_table + +extern int optimize_align_code; + +#define md_do_align(n, fill, len, max, around) \ +if ((n) \ + && !need_pass_2 \ + && optimize_align_code \ + && (!(fill) \ + || ((char)*(fill) == (char)0x90 && (len) == 1)) \ + && subseg_text_p (now_seg)) \ + { \ + frag_align_code ((n), (max)); \ + goto around; \ + } + +#define MAX_MEM_FOR_RS_ALIGN_CODE 31 + +extern void i386_align_code (fragS *, int); + +#define HANDLE_ALIGN(fragP) \ +if (fragP->fr_type == rs_align_code) \ + i386_align_code (fragP, (fragP->fr_next->fr_address \ + - fragP->fr_address \ + - fragP->fr_fix)); + +void i386_print_statistics (FILE *); +#define tc_print_statistics i386_print_statistics + +extern unsigned int i386_frag_max_var (fragS *); +#define md_frag_max_var i386_frag_max_var + +#define md_number_to_chars number_to_chars_littleendian + +enum processor_type +{ + PROCESSOR_UNKNOWN, + PROCESSOR_I386, + PROCESSOR_I486, + PROCESSOR_PENTIUM, + PROCESSOR_PENTIUMPRO, + PROCESSOR_PENTIUM4, + PROCESSOR_NOCONA, + PROCESSOR_CORE, + PROCESSOR_CORE2, + PROCESSOR_COREI7, + PROCESSOR_L1OM, + PROCESSOR_K1OM, + PROCESSOR_K6, + PROCESSOR_ATHLON, + PROCESSOR_K8, + PROCESSOR_GENERIC32, + PROCESSOR_GENERIC64, + PROCESSOR_AMDFAM10, + PROCESSOR_BD, + PROCESSOR_BT +}; + +extern enum processor_type cpu_arch_tune; +extern enum processor_type cpu_arch_isa; +extern i386_cpu_flags cpu_arch_isa_flags; + +struct i386_tc_frag_data +{ + enum processor_type isa; + i386_cpu_flags isa_flags; + enum processor_type tune; +}; + +/* We need to emit the right NOP pattern in .align frags. This is + done after the text-to-bits assembly pass, so we need to mark it with + the isa/tune settings at the time the .align was assembled. */ +#define TC_FRAG_TYPE struct i386_tc_frag_data + +#define TC_FRAG_INIT(FRAGP) \ + do \ + { \ + (FRAGP)->tc_frag_data.isa = cpu_arch_isa; \ + (FRAGP)->tc_frag_data.isa_flags = cpu_arch_isa_flags; \ + (FRAGP)->tc_frag_data.tune = cpu_arch_tune; \ + } \ + while (0) + +#ifdef SCO_ELF +#define tc_init_after_args() sco_id () +extern void sco_id (void); +#endif + +#define WORKING_DOT_WORD 1 + +/* We want .cfi_* pseudo-ops for generating unwind info. */ +#define TARGET_USE_CFIPOP 1 + +extern unsigned int x86_dwarf2_return_column; +#define DWARF2_DEFAULT_RETURN_COLUMN x86_dwarf2_return_column + +extern int x86_cie_data_alignment; +#define DWARF2_CIE_DATA_ALIGNMENT x86_cie_data_alignment + +extern int x86_dwarf2_addr_size (void); +#define DWARF2_ADDR_SIZE(bfd) x86_dwarf2_addr_size () + +#define tc_parse_to_dw2regnum tc_x86_parse_to_dw2regnum +extern void tc_x86_parse_to_dw2regnum (expressionS *); + +#define tc_cfi_frame_initial_instructions tc_x86_frame_initial_instructions +extern void tc_x86_frame_initial_instructions (void); + +#define md_elf_section_type(str,len) i386_elf_section_type (str, len) +extern int i386_elf_section_type (const char *, size_t); + +#ifdef TE_SOLARIS +#define md_fix_up_eh_frame(sec) i386_solaris_fix_up_eh_frame (sec) +extern void i386_solaris_fix_up_eh_frame (segT); +#endif + +/* Support for SHF_X86_64_LARGE */ +extern bfd_vma x86_64_section_word (char *, size_t); +extern bfd_vma x86_64_section_letter (int, char **); +#define md_elf_section_letter(LETTER, PTR_MSG) x86_64_section_letter (LETTER, PTR_MSG) +#define md_elf_section_word(STR, LEN) x86_64_section_word (STR, LEN) + +#ifdef TE_PE + +#define O_secrel O_md1 + +#define TC_DWARF2_EMIT_OFFSET tc_pe_dwarf2_emit_offset +void tc_pe_dwarf2_emit_offset (symbolS *, unsigned int); + +#endif /* TE_PE */ + +/* X_add_symbol:X_op_symbol (Intel mode only) */ +#define O_full_ptr O_md2 + +#ifdef OBJ_MACH_O + +#define TC_FORCE_RELOCATION(FIX) (obj_mach_o_force_reloc (FIX)) + +#define TC_FORCE_RELOCATION_SUB_SAME(FIX,SEG) \ + (obj_mach_o_force_reloc_sub_same (FIX, SEG)) + +#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX,SEG) \ + (obj_mach_o_force_reloc_sub_local (FIX, SEG)) + +#define TC_VALIDATE_FIX_SUB(FIX, SEG) 1 + +#endif /* OBJ_MACH_O */ + +#endif /* TC_I386 */ diff --git a/contrib/toolchain/binutils/gas/config/te-pe.h b/contrib/toolchain/binutils/gas/config/te-pe.h new file mode 100644 index 0000000000..1ac632cb78 --- /dev/null +++ b/contrib/toolchain/binutils/gas/config/te-pe.h @@ -0,0 +1,26 @@ +/* Copyright 2007 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + GAS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + the 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#define TE_PE +#define LEX_AT (LEX_BEGIN_NAME | LEX_NAME) /* Can have @'s inside labels. */ + +/* The PE format supports long section names. */ +#define COFF_LONG_SECTION_NAMES + +#include "obj-format.h" diff --git a/contrib/toolchain/binutils/gas/depend.c b/contrib/toolchain/binutils/gas/depend.c new file mode 100644 index 0000000000..7a3c54c4e1 --- /dev/null +++ b/contrib/toolchain/binutils/gas/depend.c @@ -0,0 +1,208 @@ +/* depend.c - Handle dependency tracking. + Copyright 1997, 1998, 2000, 2001, 2003, 2004, 2005, 2007 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" +#include "filenames.h" + +/* The file to write to, or NULL if no dependencies being kept. */ +static char * dep_file = NULL; + +struct dependency + { + char * file; + struct dependency * next; + }; + +/* All the files we depend on. */ +static struct dependency * dep_chain = NULL; + +/* Current column in output file. */ +static int column = 0; + +static int quote_string_for_make (FILE *, char *); +static void wrap_output (FILE *, char *, int); + +/* Number of columns allowable. */ +#define MAX_COLUMNS 72 + +/* Start saving dependencies, to be written to FILENAME. If this is + never called, then dependency tracking is simply skipped. */ + +void +start_dependencies (char *filename) +{ + dep_file = filename; +} + +/* Noticed a new filename, so try to register it. */ + +void +register_dependency (char *filename) +{ + struct dependency *dep; + + if (dep_file == NULL) + return; + + for (dep = dep_chain; dep != NULL; dep = dep->next) + { + if (!filename_cmp (filename, dep->file)) + return; + } + + dep = (struct dependency *) xmalloc (sizeof (struct dependency)); + dep->file = xstrdup (filename); + dep->next = dep_chain; + dep_chain = dep; +} + +/* Quote a file name the way `make' wants it, and print it to FILE. + If FILE is NULL, do no printing, but return the length of the + quoted string. + + This code is taken from gcc with only minor changes. */ + +static int +quote_string_for_make (FILE *file, char *src) +{ + char *p = src; + int i = 0; + + for (;;) + { + char c = *p++; + + switch (c) + { + case '\0': + case ' ': + case '\t': + { + /* GNU make uses a weird quoting scheme for white space. + A space or tab preceded by 2N+1 backslashes represents + N backslashes followed by space; a space or tab + preceded by 2N backslashes represents N backslashes at + the end of a file name; and backslashes in other + contexts should not be doubled. */ + char *q; + + for (q = p - 1; src < q && q[-1] == '\\'; q--) + { + if (file) + putc ('\\', file); + i++; + } + } + if (!c) + return i; + if (file) + putc ('\\', file); + i++; + goto ordinary_char; + + case '$': + if (file) + putc (c, file); + i++; + /* Fall through. This can mishandle things like "$(" but + there's no easy fix. */ + default: + ordinary_char: + /* This can mishandle characters in the string "\0\n%*?[\\~"; + exactly which chars are mishandled depends on the `make' version. + We know of no portable solution for this; + even GNU make 3.76.1 doesn't solve the problem entirely. + (Also, '\0' is mishandled due to our calling conventions.) */ + if (file) + putc (c, file); + i++; + break; + } + } +} + +/* Append some output to the file, keeping track of columns and doing + wrapping as necessary. */ + +static void +wrap_output (FILE *f, char *string, int spacer) +{ + int len = quote_string_for_make (NULL, string); + + if (len == 0) + return; + + if (column + && (MAX_COLUMNS + - 1 /* spacer */ + - 2 /* ` \' */ + < column + len)) + { + fprintf (f, " \\\n "); + column = 0; + if (spacer == ' ') + spacer = '\0'; + } + + if (spacer == ' ') + { + putc (spacer, f); + ++column; + } + + quote_string_for_make (f, string); + column += len; + + if (spacer == ':') + { + putc (spacer, f); + ++column; + } +} + +/* Print dependency file. */ + +void +print_dependencies (void) +{ + FILE *f; + struct dependency *dep; + + if (dep_file == NULL) + return; + + f = fopen (dep_file, FOPEN_WT); + if (f == NULL) + { + as_warn (_("can't open `%s' for writing"), dep_file); + return; + } + + column = 0; + wrap_output (f, out_file_name, ':'); + for (dep = dep_chain; dep != NULL; dep = dep->next) + wrap_output (f, dep->file, ' '); + + putc ('\n', f); + + if (fclose (f)) + as_warn (_("can't close `%s'"), dep_file); +} diff --git a/contrib/toolchain/binutils/gas/dw2gencfi.c b/contrib/toolchain/binutils/gas/dw2gencfi.c new file mode 100644 index 0000000000..faa8384d88 --- /dev/null +++ b/contrib/toolchain/binutils/gas/dw2gencfi.c @@ -0,0 +1,2044 @@ +/* dw2gencfi.c - Support for generating Dwarf2 CFI information. + Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 + Free Software Foundation, Inc. + Contributed by Michal Ludvig + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" +#include "dw2gencfi.h" +#include "subsegs.h" +#include "dwarf2dbg.h" + +#ifdef TARGET_USE_CFIPOP + +/* By default, use difference expressions if DIFF_EXPR_OK is defined. */ +#ifndef CFI_DIFF_EXPR_OK +# ifdef DIFF_EXPR_OK +# define CFI_DIFF_EXPR_OK 1 +# else +# define CFI_DIFF_EXPR_OK 0 +# endif +#endif + +#ifndef CFI_DIFF_LSDA_OK +#define CFI_DIFF_LSDA_OK CFI_DIFF_EXPR_OK +#endif + +#if CFI_DIFF_EXPR_OK == 1 && CFI_DIFF_LSDA_OK == 0 +# error "CFI_DIFF_EXPR_OK should imply CFI_DIFF_LSDA_OK" +#endif + +/* We re-use DWARF2_LINE_MIN_INSN_LENGTH for the code alignment field + of the CIE. Default to 1 if not otherwise specified. */ +#ifndef DWARF2_LINE_MIN_INSN_LENGTH +#define DWARF2_LINE_MIN_INSN_LENGTH 1 +#endif + +/* By default, use 32-bit relocations from .eh_frame into .text. */ +#ifndef DWARF2_FDE_RELOC_SIZE +#define DWARF2_FDE_RELOC_SIZE 4 +#endif + +/* By default, use a read-only .eh_frame section. */ +#ifndef DWARF2_EH_FRAME_READ_ONLY +#define DWARF2_EH_FRAME_READ_ONLY SEC_READONLY +#endif + +#ifndef EH_FRAME_ALIGNMENT +#define EH_FRAME_ALIGNMENT (bfd_get_arch_size (stdoutput) == 64 ? 3 : 2) +#endif + +#ifndef tc_cfi_frame_initial_instructions +#define tc_cfi_frame_initial_instructions() ((void)0) +#endif + +#ifndef tc_cfi_startproc +# define tc_cfi_startproc() ((void)0) +#endif + +#ifndef tc_cfi_endproc +# define tc_cfi_endproc(fde) ((void) (fde)) +#endif + +#ifndef DWARF2_FORMAT +#define DWARF2_FORMAT(SEC) dwarf2_format_32bit +#endif + +#ifndef DWARF2_ADDR_SIZE +#define DWARF2_ADDR_SIZE(bfd) (bfd_arch_bits_per_address (bfd) / 8) +#endif + +#if SUPPORT_FRAME_LINKONCE +#define CUR_SEG(structp) structp->cur_seg +#define SET_CUR_SEG(structp, seg) structp->cur_seg = seg +#define HANDLED(structp) structp->handled +#define SET_HANDLED(structp, val) structp->handled = val +#else +#define CUR_SEG(structp) NULL +#define SET_CUR_SEG(structp, seg) (void) (0 && seg) +#define HANDLED(structp) 0 +#define SET_HANDLED(structp, val) (void) (0 && val) +#endif + +/* Private segment collection list. */ +struct dwcfi_seg_list +{ + segT seg; + int subseg; + char * seg_name; +}; + +#define FRAME_NAME ".eh_frame" + +static struct hash_control *dwcfi_hash; + +/* Build based on segment the derived .debug_... + segment name containing origin segment's postfix name part. */ + +static char * +get_debugseg_name (segT seg, const char *base_name) +{ + const char *name; + + if (!seg) + name = ""; + else + { + const char * dollar; + const char * dot; + + name = bfd_get_section_name (stdoutput, seg); + + dollar = strchr (name, '$'); + dot = strchr (name + 1, '.'); + + if (!dollar && !dot) + name = ""; + else if (!dollar) + name = dot; + else if (!dot) + name = dollar; + else if (dot < dollar) + name = dot; + else + name = dollar; + } + + return concat (base_name, name, NULL); +} + +/* Allocate a dwcfi_seg_list structure. */ + +static struct dwcfi_seg_list * +alloc_debugseg_item (segT seg, int subseg, char *name) +{ + struct dwcfi_seg_list *r; + + r = (struct dwcfi_seg_list *) + xmalloc (sizeof (struct dwcfi_seg_list) + strlen (name)); + r->seg = seg; + r->subseg = subseg; + r->seg_name = name; + return r; +} + +static segT +is_now_linkonce_segment (void) +{ + if ((bfd_get_section_flags (stdoutput, now_seg) + & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD + | SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE + | SEC_LINK_DUPLICATES_SAME_CONTENTS)) != 0) + return now_seg; + return NULL; +} + +/* Generate debug... segment with same linkonce properties + of based segment. */ + +static segT +make_debug_seg (segT cseg, char *name, int sflags) +{ + segT save_seg = now_seg; + int save_subseg = now_subseg; + segT r; + flagword flags; + + r = subseg_new (name, 0); + + /* Check if code segment is marked as linked once. */ + if (!cseg) + flags = 0; + else + flags = bfd_get_section_flags (stdoutput, cseg) + & (SEC_LINK_ONCE | SEC_LINK_DUPLICATES_DISCARD + | SEC_LINK_DUPLICATES_ONE_ONLY | SEC_LINK_DUPLICATES_SAME_SIZE + | SEC_LINK_DUPLICATES_SAME_CONTENTS); + + /* Add standard section flags. */ + flags |= sflags; + + /* Apply possibly linked once flags to new generated segment, too. */ + if (!bfd_set_section_flags (stdoutput, r, flags)) + as_bad (_("bfd_set_section_flags: %s"), + bfd_errmsg (bfd_get_error ())); + + /* Restore to previous segment. */ + if (save_seg != NULL) + subseg_set (save_seg, save_subseg); + return r; +} + +static void +dwcfi_hash_insert (const char *name, struct dwcfi_seg_list *item) +{ + const char *error_string; + + if ((error_string = hash_jam (dwcfi_hash, name, (char *) item))) + as_fatal (_("Inserting \"%s\" into structure table failed: %s"), + name, error_string); +} + +static struct dwcfi_seg_list * +dwcfi_hash_find (char *name) +{ + return (struct dwcfi_seg_list *) hash_find (dwcfi_hash, name); +} + +static struct dwcfi_seg_list * +dwcfi_hash_find_or_make (segT cseg, const char *base_name, int flags) +{ + struct dwcfi_seg_list *item; + char *name; + + /* Initialize dwcfi_hash once. */ + if (!dwcfi_hash) + dwcfi_hash = hash_new (); + + name = get_debugseg_name (cseg, base_name); + + item = dwcfi_hash_find (name); + if (!item) + { + item = alloc_debugseg_item (make_debug_seg (cseg, name, flags), 0, name); + + dwcfi_hash_insert (item->seg_name, item); + } + else + free (name); + + return item; +} + +/* ??? Share this with dwarf2cfg.c. */ +#ifndef TC_DWARF2_EMIT_OFFSET +#define TC_DWARF2_EMIT_OFFSET generic_dwarf2_emit_offset + +/* Create an offset to .dwarf2_*. */ + +static void +generic_dwarf2_emit_offset (symbolS *symbol, unsigned int size) +{ + expressionS exp; + + exp.X_op = O_symbol; + exp.X_add_symbol = symbol; + exp.X_add_number = 0; + emit_expr (&exp, size); +} +#endif + +struct cfi_escape_data +{ + struct cfi_escape_data *next; + expressionS exp; +}; + +struct cie_entry +{ + struct cie_entry *next; +#if SUPPORT_FRAME_LINKONCE + segT cur_seg; +#endif + symbolS *start_address; + unsigned int return_column; + unsigned int signal_frame; + unsigned char per_encoding; + unsigned char lsda_encoding; + expressionS personality; + struct cfi_insn_data *first, *last; +}; + +/* List of FDE entries. */ + +struct fde_entry *all_fde_data; +static struct fde_entry **last_fde_data = &all_fde_data; + +/* List of CIEs so that they could be reused. */ +static struct cie_entry *cie_root; + +/* Stack of old CFI data, for save/restore. */ +struct cfa_save_data +{ + struct cfa_save_data *next; + offsetT cfa_offset; +}; + +/* Current open FDE entry. */ +struct frch_cfi_data +{ + struct fde_entry *cur_fde_data; + symbolS *last_address; + offsetT cur_cfa_offset; + struct cfa_save_data *cfa_save_stack; +}; + +/* Construct a new FDE structure and add it to the end of the fde list. */ + +static struct fde_entry * +alloc_fde_entry (void) +{ + struct fde_entry *fde = (struct fde_entry *) + xcalloc (1, sizeof (struct fde_entry)); + + frchain_now->frch_cfi_data = (struct frch_cfi_data *) + xcalloc (1, sizeof (struct frch_cfi_data)); + frchain_now->frch_cfi_data->cur_fde_data = fde; + *last_fde_data = fde; + last_fde_data = &fde->next; + SET_CUR_SEG (fde, is_now_linkonce_segment ()); + SET_HANDLED (fde, 0); + fde->last = &fde->data; + fde->return_column = DWARF2_DEFAULT_RETURN_COLUMN; + fde->per_encoding = DW_EH_PE_omit; + fde->lsda_encoding = DW_EH_PE_omit; + + return fde; +} + +/* The following functions are available for a backend to construct its + own unwind information, usually from legacy unwind directives. */ + +/* Construct a new INSN structure and add it to the end of the insn list + for the currently active FDE. */ + +static struct cfi_insn_data * +alloc_cfi_insn_data (void) +{ + struct cfi_insn_data *insn = (struct cfi_insn_data *) + xcalloc (1, sizeof (struct cfi_insn_data)); + struct fde_entry *cur_fde_data = frchain_now->frch_cfi_data->cur_fde_data; + + *cur_fde_data->last = insn; + cur_fde_data->last = &insn->next; + SET_CUR_SEG (insn, is_now_linkonce_segment ()); + return insn; +} + +/* Construct a new FDE structure that begins at LABEL. */ + +void +cfi_new_fde (symbolS *label) +{ + struct fde_entry *fde = alloc_fde_entry (); + fde->start_address = label; + frchain_now->frch_cfi_data->last_address = label; +} + +/* End the currently open FDE. */ + +void +cfi_end_fde (symbolS *label) +{ + frchain_now->frch_cfi_data->cur_fde_data->end_address = label; + free (frchain_now->frch_cfi_data); + frchain_now->frch_cfi_data = NULL; +} + +/* Set the return column for the current FDE. */ + +void +cfi_set_return_column (unsigned regno) +{ + frchain_now->frch_cfi_data->cur_fde_data->return_column = regno; +} + +/* Universal functions to store new instructions. */ + +static void +cfi_add_CFA_insn (int insn) +{ + struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data (); + + insn_ptr->insn = insn; +} + +static void +cfi_add_CFA_insn_reg (int insn, unsigned regno) +{ + struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data (); + + insn_ptr->insn = insn; + insn_ptr->u.r = regno; +} + +static void +cfi_add_CFA_insn_offset (int insn, offsetT offset) +{ + struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data (); + + insn_ptr->insn = insn; + insn_ptr->u.i = offset; +} + +static void +cfi_add_CFA_insn_reg_reg (int insn, unsigned reg1, unsigned reg2) +{ + struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data (); + + insn_ptr->insn = insn; + insn_ptr->u.rr.reg1 = reg1; + insn_ptr->u.rr.reg2 = reg2; +} + +static void +cfi_add_CFA_insn_reg_offset (int insn, unsigned regno, offsetT offset) +{ + struct cfi_insn_data *insn_ptr = alloc_cfi_insn_data (); + + insn_ptr->insn = insn; + insn_ptr->u.ri.reg = regno; + insn_ptr->u.ri.offset = offset; +} + +/* Add a CFI insn to advance the PC from the last address to LABEL. */ + +void +cfi_add_advance_loc (symbolS *label) +{ + struct cfi_insn_data *insn = alloc_cfi_insn_data (); + + insn->insn = DW_CFA_advance_loc; + insn->u.ll.lab1 = frchain_now->frch_cfi_data->last_address; + insn->u.ll.lab2 = label; + + frchain_now->frch_cfi_data->last_address = label; +} + +/* Add a DW_CFA_offset record to the CFI data. */ + +void +cfi_add_CFA_offset (unsigned regno, offsetT offset) +{ + unsigned int abs_data_align; + + gas_assert (DWARF2_CIE_DATA_ALIGNMENT != 0); + cfi_add_CFA_insn_reg_offset (DW_CFA_offset, regno, offset); + + abs_data_align = (DWARF2_CIE_DATA_ALIGNMENT < 0 + ? -DWARF2_CIE_DATA_ALIGNMENT : DWARF2_CIE_DATA_ALIGNMENT); + if (offset % abs_data_align) + as_bad (_("register save offset not a multiple of %u"), abs_data_align); +} + +/* Add a DW_CFA_def_cfa record to the CFI data. */ + +void +cfi_add_CFA_def_cfa (unsigned regno, offsetT offset) +{ + cfi_add_CFA_insn_reg_offset (DW_CFA_def_cfa, regno, offset); + frchain_now->frch_cfi_data->cur_cfa_offset = offset; +} + +/* Add a DW_CFA_register record to the CFI data. */ + +void +cfi_add_CFA_register (unsigned reg1, unsigned reg2) +{ + cfi_add_CFA_insn_reg_reg (DW_CFA_register, reg1, reg2); +} + +/* Add a DW_CFA_def_cfa_register record to the CFI data. */ + +void +cfi_add_CFA_def_cfa_register (unsigned regno) +{ + cfi_add_CFA_insn_reg (DW_CFA_def_cfa_register, regno); +} + +/* Add a DW_CFA_def_cfa_offset record to the CFI data. */ + +void +cfi_add_CFA_def_cfa_offset (offsetT offset) +{ + cfi_add_CFA_insn_offset (DW_CFA_def_cfa_offset, offset); + frchain_now->frch_cfi_data->cur_cfa_offset = offset; +} + +void +cfi_add_CFA_restore (unsigned regno) +{ + cfi_add_CFA_insn_reg (DW_CFA_restore, regno); +} + +void +cfi_add_CFA_undefined (unsigned regno) +{ + cfi_add_CFA_insn_reg (DW_CFA_undefined, regno); +} + +void +cfi_add_CFA_same_value (unsigned regno) +{ + cfi_add_CFA_insn_reg (DW_CFA_same_value, regno); +} + +void +cfi_add_CFA_remember_state (void) +{ + struct cfa_save_data *p; + + cfi_add_CFA_insn (DW_CFA_remember_state); + + p = (struct cfa_save_data *) xmalloc (sizeof (*p)); + p->cfa_offset = frchain_now->frch_cfi_data->cur_cfa_offset; + p->next = frchain_now->frch_cfi_data->cfa_save_stack; + frchain_now->frch_cfi_data->cfa_save_stack = p; +} + +void +cfi_add_CFA_restore_state (void) +{ + struct cfa_save_data *p; + + cfi_add_CFA_insn (DW_CFA_restore_state); + + p = frchain_now->frch_cfi_data->cfa_save_stack; + if (p) + { + frchain_now->frch_cfi_data->cur_cfa_offset = p->cfa_offset; + frchain_now->frch_cfi_data->cfa_save_stack = p->next; + free (p); + } + else + as_bad (_("CFI state restore without previous remember")); +} + + +/* Parse CFI assembler directives. */ + +static void dot_cfi (int); +static void dot_cfi_escape (int); +static void dot_cfi_sections (int); +static void dot_cfi_startproc (int); +static void dot_cfi_endproc (int); +static void dot_cfi_personality (int); +static void dot_cfi_lsda (int); +static void dot_cfi_val_encoded_addr (int); + +const pseudo_typeS cfi_pseudo_table[] = + { + { "cfi_sections", dot_cfi_sections, 0 }, + { "cfi_startproc", dot_cfi_startproc, 0 }, + { "cfi_endproc", dot_cfi_endproc, 0 }, + { "cfi_def_cfa", dot_cfi, DW_CFA_def_cfa }, + { "cfi_def_cfa_register", dot_cfi, DW_CFA_def_cfa_register }, + { "cfi_def_cfa_offset", dot_cfi, DW_CFA_def_cfa_offset }, + { "cfi_adjust_cfa_offset", dot_cfi, CFI_adjust_cfa_offset }, + { "cfi_offset", dot_cfi, DW_CFA_offset }, + { "cfi_rel_offset", dot_cfi, CFI_rel_offset }, + { "cfi_register", dot_cfi, DW_CFA_register }, + { "cfi_return_column", dot_cfi, CFI_return_column }, + { "cfi_restore", dot_cfi, DW_CFA_restore }, + { "cfi_undefined", dot_cfi, DW_CFA_undefined }, + { "cfi_same_value", dot_cfi, DW_CFA_same_value }, + { "cfi_remember_state", dot_cfi, DW_CFA_remember_state }, + { "cfi_restore_state", dot_cfi, DW_CFA_restore_state }, + { "cfi_window_save", dot_cfi, DW_CFA_GNU_window_save }, + { "cfi_escape", dot_cfi_escape, 0 }, + { "cfi_signal_frame", dot_cfi, CFI_signal_frame }, + { "cfi_personality", dot_cfi_personality, 0 }, + { "cfi_lsda", dot_cfi_lsda, 0 }, + { "cfi_val_encoded_addr", dot_cfi_val_encoded_addr, 0 }, + { NULL, NULL, 0 } + }; + +static void +cfi_parse_separator (void) +{ + SKIP_WHITESPACE (); + if (*input_line_pointer == ',') + input_line_pointer++; + else + as_bad (_("missing separator")); +} + +#ifndef tc_parse_to_dw2regnum +static void +tc_parse_to_dw2regnum (expressionS *exp) +{ +# ifdef tc_regname_to_dw2regnum + SKIP_WHITESPACE (); + if (is_name_beginner (*input_line_pointer) + || (*input_line_pointer == '%' + && is_name_beginner (*++input_line_pointer))) + { + char *name, c; + + name = input_line_pointer; + c = get_symbol_end (); + + exp->X_op = O_constant; + exp->X_add_number = tc_regname_to_dw2regnum (name); + + *input_line_pointer = c; + } + else +# endif + expression_and_evaluate (exp); +} +#endif + +static unsigned +cfi_parse_reg (void) +{ + int regno; + expressionS exp; + + tc_parse_to_dw2regnum (&exp); + switch (exp.X_op) + { + case O_register: + case O_constant: + regno = exp.X_add_number; + break; + + default: + regno = -1; + break; + } + + if (regno < 0) + { + as_bad (_("bad register expression")); + regno = 0; + } + + return regno; +} + +static offsetT +cfi_parse_const (void) +{ + return get_absolute_expression (); +} + +static void +dot_cfi (int arg) +{ + offsetT offset; + unsigned reg1, reg2; + + if (frchain_now->frch_cfi_data == NULL) + { + as_bad (_("CFI instruction used without previous .cfi_startproc")); + ignore_rest_of_line (); + return; + } + + /* If the last address was not at the current PC, advance to current. */ + if (symbol_get_frag (frchain_now->frch_cfi_data->last_address) != frag_now + || S_GET_VALUE (frchain_now->frch_cfi_data->last_address) + != frag_now_fix ()) + cfi_add_advance_loc (symbol_temp_new_now ()); + + switch (arg) + { + case DW_CFA_offset: + reg1 = cfi_parse_reg (); + cfi_parse_separator (); + offset = cfi_parse_const (); + cfi_add_CFA_offset (reg1, offset); + break; + + case CFI_rel_offset: + reg1 = cfi_parse_reg (); + cfi_parse_separator (); + offset = cfi_parse_const (); + cfi_add_CFA_offset (reg1, + offset - frchain_now->frch_cfi_data->cur_cfa_offset); + break; + + case DW_CFA_def_cfa: + reg1 = cfi_parse_reg (); + cfi_parse_separator (); + offset = cfi_parse_const (); + cfi_add_CFA_def_cfa (reg1, offset); + break; + + case DW_CFA_register: + reg1 = cfi_parse_reg (); + cfi_parse_separator (); + reg2 = cfi_parse_reg (); + cfi_add_CFA_register (reg1, reg2); + break; + + case DW_CFA_def_cfa_register: + reg1 = cfi_parse_reg (); + cfi_add_CFA_def_cfa_register (reg1); + break; + + case DW_CFA_def_cfa_offset: + offset = cfi_parse_const (); + cfi_add_CFA_def_cfa_offset (offset); + break; + + case CFI_adjust_cfa_offset: + offset = cfi_parse_const (); + cfi_add_CFA_def_cfa_offset (frchain_now->frch_cfi_data->cur_cfa_offset + + offset); + break; + + case DW_CFA_restore: + for (;;) + { + reg1 = cfi_parse_reg (); + cfi_add_CFA_restore (reg1); + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + break; + ++input_line_pointer; + } + break; + + case DW_CFA_undefined: + for (;;) + { + reg1 = cfi_parse_reg (); + cfi_add_CFA_undefined (reg1); + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + break; + ++input_line_pointer; + } + break; + + case DW_CFA_same_value: + reg1 = cfi_parse_reg (); + cfi_add_CFA_same_value (reg1); + break; + + case CFI_return_column: + reg1 = cfi_parse_reg (); + cfi_set_return_column (reg1); + break; + + case DW_CFA_remember_state: + cfi_add_CFA_remember_state (); + break; + + case DW_CFA_restore_state: + cfi_add_CFA_restore_state (); + break; + + case DW_CFA_GNU_window_save: + cfi_add_CFA_insn (DW_CFA_GNU_window_save); + break; + + case CFI_signal_frame: + frchain_now->frch_cfi_data->cur_fde_data->signal_frame = 1; + break; + + default: + abort (); + } + + demand_empty_rest_of_line (); +} + +static void +dot_cfi_escape (int ignored ATTRIBUTE_UNUSED) +{ + struct cfi_escape_data *head, **tail, *e; + struct cfi_insn_data *insn; + + if (frchain_now->frch_cfi_data == NULL) + { + as_bad (_("CFI instruction used without previous .cfi_startproc")); + ignore_rest_of_line (); + return; + } + + /* If the last address was not at the current PC, advance to current. */ + if (symbol_get_frag (frchain_now->frch_cfi_data->last_address) != frag_now + || S_GET_VALUE (frchain_now->frch_cfi_data->last_address) + != frag_now_fix ()) + cfi_add_advance_loc (symbol_temp_new_now ()); + + tail = &head; + do + { + e = (struct cfi_escape_data *) xmalloc (sizeof (*e)); + do_parse_cons_expression (&e->exp, 1); + *tail = e; + tail = &e->next; + } + while (*input_line_pointer++ == ','); + *tail = NULL; + + insn = alloc_cfi_insn_data (); + insn->insn = CFI_escape; + insn->u.esc = head; + + --input_line_pointer; + demand_empty_rest_of_line (); +} + +static void +dot_cfi_personality (int ignored ATTRIBUTE_UNUSED) +{ + struct fde_entry *fde; + offsetT encoding; + + if (frchain_now->frch_cfi_data == NULL) + { + as_bad (_("CFI instruction used without previous .cfi_startproc")); + ignore_rest_of_line (); + return; + } + + fde = frchain_now->frch_cfi_data->cur_fde_data; + encoding = cfi_parse_const (); + if (encoding == DW_EH_PE_omit) + { + demand_empty_rest_of_line (); + fde->per_encoding = encoding; + return; + } + + if ((encoding & 0xff) != encoding + || ((encoding & 0x70) != 0 +#if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr + && (encoding & 0x70) != DW_EH_PE_pcrel +#endif + ) + /* leb128 can be handled, but does something actually need it? */ + || (encoding & 7) == DW_EH_PE_uleb128 + || (encoding & 7) > DW_EH_PE_udata8) + { + as_bad (_("invalid or unsupported encoding in .cfi_personality")); + ignore_rest_of_line (); + return; + } + + if (*input_line_pointer++ != ',') + { + as_bad (_(".cfi_personality requires encoding and symbol arguments")); + ignore_rest_of_line (); + return; + } + + expression_and_evaluate (&fde->personality); + switch (fde->personality.X_op) + { + case O_symbol: + break; + case O_constant: + if ((encoding & 0x70) == DW_EH_PE_pcrel) + encoding = DW_EH_PE_omit; + break; + default: + encoding = DW_EH_PE_omit; + break; + } + + fde->per_encoding = encoding; + + if (encoding == DW_EH_PE_omit) + { + as_bad (_("wrong second argument to .cfi_personality")); + ignore_rest_of_line (); + return; + } + + demand_empty_rest_of_line (); +} + +static void +dot_cfi_lsda (int ignored ATTRIBUTE_UNUSED) +{ + struct fde_entry *fde; + offsetT encoding; + + if (frchain_now->frch_cfi_data == NULL) + { + as_bad (_("CFI instruction used without previous .cfi_startproc")); + ignore_rest_of_line (); + return; + } + + fde = frchain_now->frch_cfi_data->cur_fde_data; + encoding = cfi_parse_const (); + if (encoding == DW_EH_PE_omit) + { + demand_empty_rest_of_line (); + fde->lsda_encoding = encoding; + return; + } + + if ((encoding & 0xff) != encoding + || ((encoding & 0x70) != 0 +#if CFI_DIFF_LSDA_OK || defined tc_cfi_emit_pcrel_expr + && (encoding & 0x70) != DW_EH_PE_pcrel +#endif + ) + /* leb128 can be handled, but does something actually need it? */ + || (encoding & 7) == DW_EH_PE_uleb128 + || (encoding & 7) > DW_EH_PE_udata8) + { + as_bad (_("invalid or unsupported encoding in .cfi_lsda")); + ignore_rest_of_line (); + return; + } + + if (*input_line_pointer++ != ',') + { + as_bad (_(".cfi_lsda requires encoding and symbol arguments")); + ignore_rest_of_line (); + return; + } + + fde->lsda_encoding = encoding; + + expression_and_evaluate (&fde->lsda); + switch (fde->lsda.X_op) + { + case O_symbol: + break; + case O_constant: + if ((encoding & 0x70) == DW_EH_PE_pcrel) + encoding = DW_EH_PE_omit; + break; + default: + encoding = DW_EH_PE_omit; + break; + } + + fde->lsda_encoding = encoding; + + if (encoding == DW_EH_PE_omit) + { + as_bad (_("wrong second argument to .cfi_lsda")); + ignore_rest_of_line (); + return; + } + + demand_empty_rest_of_line (); +} + +static void +dot_cfi_val_encoded_addr (int ignored ATTRIBUTE_UNUSED) +{ + struct cfi_insn_data *insn_ptr; + offsetT encoding; + + if (frchain_now->frch_cfi_data == NULL) + { + as_bad (_("CFI instruction used without previous .cfi_startproc")); + ignore_rest_of_line (); + return; + } + + /* If the last address was not at the current PC, advance to current. */ + if (symbol_get_frag (frchain_now->frch_cfi_data->last_address) != frag_now + || S_GET_VALUE (frchain_now->frch_cfi_data->last_address) + != frag_now_fix ()) + cfi_add_advance_loc (symbol_temp_new_now ()); + + insn_ptr = alloc_cfi_insn_data (); + insn_ptr->insn = CFI_val_encoded_addr; + + insn_ptr->u.ea.reg = cfi_parse_reg (); + + cfi_parse_separator (); + encoding = cfi_parse_const (); + if ((encoding & 0xff) != encoding + || ((encoding & 0x70) != 0 +#if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr + && (encoding & 0x70) != DW_EH_PE_pcrel +#endif + ) + /* leb128 can be handled, but does something actually need it? */ + || (encoding & 7) == DW_EH_PE_uleb128 + || (encoding & 7) > DW_EH_PE_udata8) + { + as_bad (_("invalid or unsupported encoding in .cfi_lsda")); + encoding = DW_EH_PE_omit; + } + + cfi_parse_separator (); + expression_and_evaluate (&insn_ptr->u.ea.exp); + switch (insn_ptr->u.ea.exp.X_op) + { + case O_symbol: + break; + case O_constant: + if ((encoding & 0x70) != DW_EH_PE_pcrel) + break; + default: + encoding = DW_EH_PE_omit; + break; + } + + insn_ptr->u.ea.encoding = encoding; + if (encoding == DW_EH_PE_omit) + { + as_bad (_("wrong third argument to .cfi_val_encoded_addr")); + ignore_rest_of_line (); + return; + } + + demand_empty_rest_of_line (); +} + +/* By default emit .eh_frame only, not .debug_frame. */ +#define CFI_EMIT_eh_frame (1 << 0) +#define CFI_EMIT_debug_frame (1 << 1) +#define CFI_EMIT_target (1 << 2) +static int cfi_sections = CFI_EMIT_eh_frame; + +static void +dot_cfi_sections (int ignored ATTRIBUTE_UNUSED) +{ + int sections = 0; + + SKIP_WHITESPACE (); + if (is_name_beginner (*input_line_pointer)) + while (1) + { + char *name, c; + + name = input_line_pointer; + c = get_symbol_end (); + + if (strncmp (name, ".eh_frame", sizeof ".eh_frame") == 0 + && name[9] != '_') + sections |= CFI_EMIT_eh_frame; + else if (strncmp (name, ".debug_frame", sizeof ".debug_frame") == 0) + sections |= CFI_EMIT_debug_frame; +#ifdef tc_cfi_section_name + else if (strcmp (name, tc_cfi_section_name) == 0) + sections |= CFI_EMIT_target; +#endif + else + { + *input_line_pointer = c; + input_line_pointer = name; + break; + } + + *input_line_pointer = c; + SKIP_WHITESPACE (); + if (*input_line_pointer == ',') + { + name = input_line_pointer++; + SKIP_WHITESPACE (); + if (!is_name_beginner (*input_line_pointer)) + { + input_line_pointer = name; + break; + } + } + else if (is_name_beginner (*input_line_pointer)) + break; + } + + demand_empty_rest_of_line (); + cfi_sections = sections; +} + +static void +dot_cfi_startproc (int ignored ATTRIBUTE_UNUSED) +{ + int simple = 0; + + if (frchain_now->frch_cfi_data != NULL) + { + as_bad (_("previous CFI entry not closed (missing .cfi_endproc)")); + ignore_rest_of_line (); + return; + } + + cfi_new_fde (symbol_temp_new_now ()); + + SKIP_WHITESPACE (); + if (is_name_beginner (*input_line_pointer)) + { + char *name, c; + + name = input_line_pointer; + c = get_symbol_end (); + + if (strcmp (name, "simple") == 0) + { + simple = 1; + *input_line_pointer = c; + } + else + input_line_pointer = name; + } + demand_empty_rest_of_line (); + + frchain_now->frch_cfi_data->cur_cfa_offset = 0; + if (!simple) + tc_cfi_frame_initial_instructions (); + + if ((cfi_sections & CFI_EMIT_target) != 0) + tc_cfi_startproc (); +} + +static void +dot_cfi_endproc (int ignored ATTRIBUTE_UNUSED) +{ + struct fde_entry *fde; + + if (frchain_now->frch_cfi_data == NULL) + { + as_bad (_(".cfi_endproc without corresponding .cfi_startproc")); + ignore_rest_of_line (); + return; + } + + fde = frchain_now->frch_cfi_data->cur_fde_data; + + cfi_end_fde (symbol_temp_new_now ()); + + demand_empty_rest_of_line (); + + if ((cfi_sections & CFI_EMIT_target) != 0) + tc_cfi_endproc (fde); +} + + +/* Emit a single byte into the current segment. */ + +static inline void +out_one (int byte) +{ + FRAG_APPEND_1_CHAR (byte); +} + +/* Emit a two-byte word into the current segment. */ + +static inline void +out_two (int data) +{ + md_number_to_chars (frag_more (2), data, 2); +} + +/* Emit a four byte word into the current segment. */ + +static inline void +out_four (int data) +{ + md_number_to_chars (frag_more (4), data, 4); +} + +/* Emit an unsigned "little-endian base 128" number. */ + +static void +out_uleb128 (addressT value) +{ + output_leb128 (frag_more (sizeof_leb128 (value, 0)), value, 0); +} + +/* Emit an unsigned "little-endian base 128" number. */ + +static void +out_sleb128 (offsetT value) +{ + output_leb128 (frag_more (sizeof_leb128 (value, 1)), value, 1); +} + +static void +output_cfi_insn (struct cfi_insn_data *insn) +{ + offsetT offset; + unsigned int regno; + + switch (insn->insn) + { + case DW_CFA_advance_loc: + { + symbolS *from = insn->u.ll.lab1; + symbolS *to = insn->u.ll.lab2; + + if (symbol_get_frag (to) == symbol_get_frag (from)) + { + addressT delta = S_GET_VALUE (to) - S_GET_VALUE (from); + addressT scaled = delta / DWARF2_LINE_MIN_INSN_LENGTH; + + if (scaled <= 0x3F) + out_one (DW_CFA_advance_loc + scaled); + else if (scaled <= 0xFF) + { + out_one (DW_CFA_advance_loc1); + out_one (scaled); + } + else if (scaled <= 0xFFFF) + { + out_one (DW_CFA_advance_loc2); + out_two (scaled); + } + else + { + out_one (DW_CFA_advance_loc4); + out_four (scaled); + } + } + else + { + expressionS exp; + + exp.X_op = O_subtract; + exp.X_add_symbol = to; + exp.X_op_symbol = from; + exp.X_add_number = 0; + + /* The code in ehopt.c expects that one byte of the encoding + is already allocated to the frag. This comes from the way + that it scans the .eh_frame section looking first for the + .byte DW_CFA_advance_loc4. */ + *frag_more (1) = DW_CFA_advance_loc4; + + frag_var (rs_cfa, 4, 0, DWARF2_LINE_MIN_INSN_LENGTH << 3, + make_expr_symbol (&exp), frag_now_fix () - 1, + (char *) frag_now); + } + } + break; + + case DW_CFA_def_cfa: + offset = insn->u.ri.offset; + if (offset < 0) + { + out_one (DW_CFA_def_cfa_sf); + out_uleb128 (insn->u.ri.reg); + out_sleb128 (offset / DWARF2_CIE_DATA_ALIGNMENT); + } + else + { + out_one (DW_CFA_def_cfa); + out_uleb128 (insn->u.ri.reg); + out_uleb128 (offset); + } + break; + + case DW_CFA_def_cfa_register: + case DW_CFA_undefined: + case DW_CFA_same_value: + out_one (insn->insn); + out_uleb128 (insn->u.r); + break; + + case DW_CFA_def_cfa_offset: + offset = insn->u.i; + if (offset < 0) + { + out_one (DW_CFA_def_cfa_offset_sf); + out_sleb128 (offset / DWARF2_CIE_DATA_ALIGNMENT); + } + else + { + out_one (DW_CFA_def_cfa_offset); + out_uleb128 (offset); + } + break; + + case DW_CFA_restore: + regno = insn->u.r; + if (regno <= 0x3F) + { + out_one (DW_CFA_restore + regno); + } + else + { + out_one (DW_CFA_restore_extended); + out_uleb128 (regno); + } + break; + + case DW_CFA_offset: + regno = insn->u.ri.reg; + offset = insn->u.ri.offset / DWARF2_CIE_DATA_ALIGNMENT; + if (offset < 0) + { + out_one (DW_CFA_offset_extended_sf); + out_uleb128 (regno); + out_sleb128 (offset); + } + else if (regno <= 0x3F) + { + out_one (DW_CFA_offset + regno); + out_uleb128 (offset); + } + else + { + out_one (DW_CFA_offset_extended); + out_uleb128 (regno); + out_uleb128 (offset); + } + break; + + case DW_CFA_register: + out_one (DW_CFA_register); + out_uleb128 (insn->u.rr.reg1); + out_uleb128 (insn->u.rr.reg2); + break; + + case DW_CFA_remember_state: + case DW_CFA_restore_state: + out_one (insn->insn); + break; + + case DW_CFA_GNU_window_save: + out_one (DW_CFA_GNU_window_save); + break; + + case CFI_escape: + { + struct cfi_escape_data *e; + for (e = insn->u.esc; e ; e = e->next) + emit_expr (&e->exp, 1); + break; + } + + case CFI_val_encoded_addr: + { + unsigned encoding = insn->u.ea.encoding; + offsetT encoding_size; + + if (encoding == DW_EH_PE_omit) + break; + out_one (DW_CFA_val_expression); + out_uleb128 (insn->u.ea.reg); + + switch (encoding & 0x7) + { + case DW_EH_PE_absptr: + encoding_size = DWARF2_ADDR_SIZE (stdoutput); + break; + case DW_EH_PE_udata2: + encoding_size = 2; + break; + case DW_EH_PE_udata4: + encoding_size = 4; + break; + case DW_EH_PE_udata8: + encoding_size = 8; + break; + default: + abort (); + } + + /* If the user has requested absolute encoding, + then use the smaller DW_OP_addr encoding. */ + if (insn->u.ea.encoding == DW_EH_PE_absptr) + { + out_uleb128 (1 + encoding_size); + out_one (DW_OP_addr); + } + else + { + out_uleb128 (1 + 1 + encoding_size); + out_one (DW_OP_GNU_encoded_addr); + out_one (encoding); + + if ((encoding & 0x70) == DW_EH_PE_pcrel) + { +#if CFI_DIFF_EXPR_OK + insn->u.ea.exp.X_op = O_subtract; + insn->u.ea.exp.X_op_symbol = symbol_temp_new_now (); +#elif defined (tc_cfi_emit_pcrel_expr) + tc_cfi_emit_pcrel_expr (&insn->u.ea.exp, encoding_size); + break; +#else + abort (); +#endif + } + } + emit_expr (&insn->u.ea.exp, encoding_size); + } + break; + + default: + abort (); + } +} + +static offsetT +encoding_size (unsigned char encoding) +{ + if (encoding == DW_EH_PE_omit) + return 0; + switch (encoding & 0x7) + { + case 0: + return bfd_get_arch_size (stdoutput) == 64 ? 8 : 4; + case DW_EH_PE_udata2: + return 2; + case DW_EH_PE_udata4: + return 4; + case DW_EH_PE_udata8: + return 8; + default: + abort (); + } +} + +static void +output_cie (struct cie_entry *cie, bfd_boolean eh_frame, int align) +{ + symbolS *after_size_address, *end_address; + expressionS exp; + struct cfi_insn_data *i; + offsetT augmentation_size; + int enc; + enum dwarf2_format fmt = DWARF2_FORMAT (now_seg); + + cie->start_address = symbol_temp_new_now (); + after_size_address = symbol_temp_make (); + end_address = symbol_temp_make (); + + exp.X_op = O_subtract; + exp.X_add_symbol = end_address; + exp.X_op_symbol = after_size_address; + exp.X_add_number = 0; + + if (eh_frame || fmt == dwarf2_format_32bit) + emit_expr (&exp, 4); /* Length. */ + else + { + if (fmt == dwarf2_format_64bit) + out_four (-1); + emit_expr (&exp, 8); /* Length. */ + } + symbol_set_value_now (after_size_address); + if (eh_frame) + out_four (0); /* CIE id. */ + else + { + out_four (-1); /* CIE id. */ + if (fmt != dwarf2_format_32bit) + out_four (-1); + } + out_one (DW_CIE_VERSION); /* Version. */ + if (eh_frame) + { + out_one ('z'); /* Augmentation. */ + if (cie->per_encoding != DW_EH_PE_omit) + out_one ('P'); + if (cie->lsda_encoding != DW_EH_PE_omit) + out_one ('L'); + out_one ('R'); + } + if (cie->signal_frame) + out_one ('S'); + out_one (0); + out_uleb128 (DWARF2_LINE_MIN_INSN_LENGTH); /* Code alignment. */ + out_sleb128 (DWARF2_CIE_DATA_ALIGNMENT); /* Data alignment. */ + if (DW_CIE_VERSION == 1) /* Return column. */ + out_one (cie->return_column); + else + out_uleb128 (cie->return_column); + if (eh_frame) + { + augmentation_size = 1 + (cie->lsda_encoding != DW_EH_PE_omit); + if (cie->per_encoding != DW_EH_PE_omit) + augmentation_size += 1 + encoding_size (cie->per_encoding); + out_uleb128 (augmentation_size); /* Augmentation size. */ + + if (cie->per_encoding != DW_EH_PE_omit) + { + offsetT size = encoding_size (cie->per_encoding); + out_one (cie->per_encoding); + exp = cie->personality; + if ((cie->per_encoding & 0x70) == DW_EH_PE_pcrel) + { +#if CFI_DIFF_EXPR_OK + exp.X_op = O_subtract; + exp.X_op_symbol = symbol_temp_new_now (); + emit_expr (&exp, size); +#elif defined (tc_cfi_emit_pcrel_expr) + tc_cfi_emit_pcrel_expr (&exp, size); +#else + abort (); +#endif + } + else + emit_expr (&exp, size); + } + + if (cie->lsda_encoding != DW_EH_PE_omit) + out_one (cie->lsda_encoding); + } + + switch (DWARF2_FDE_RELOC_SIZE) + { + case 2: + enc = DW_EH_PE_sdata2; + break; + case 4: + enc = DW_EH_PE_sdata4; + break; + case 8: + enc = DW_EH_PE_sdata8; + break; + default: + abort (); + } +#if CFI_DIFF_EXPR_OK || defined tc_cfi_emit_pcrel_expr + enc |= DW_EH_PE_pcrel; +#endif + if (eh_frame) + out_one (enc); + + if (cie->first) + { + for (i = cie->first; i != cie->last; i = i->next) + { + if (CUR_SEG (i) != CUR_SEG (cie)) + continue; + output_cfi_insn (i); + } + } + + frag_align (align, DW_CFA_nop, 0); + symbol_set_value_now (end_address); +} + +static void +output_fde (struct fde_entry *fde, struct cie_entry *cie, + bfd_boolean eh_frame, struct cfi_insn_data *first, + int align) +{ + symbolS *after_size_address, *end_address; + expressionS exp; + offsetT augmentation_size; + enum dwarf2_format fmt = DWARF2_FORMAT (now_seg); + int offset_size; + int addr_size; + + after_size_address = symbol_temp_make (); + end_address = symbol_temp_make (); + + exp.X_op = O_subtract; + exp.X_add_symbol = end_address; + exp.X_op_symbol = after_size_address; + exp.X_add_number = 0; + if (eh_frame || fmt == dwarf2_format_32bit) + offset_size = 4; + else + { + if (fmt == dwarf2_format_64bit) + out_four (-1); + offset_size = 8; + } + emit_expr (&exp, offset_size); /* Length. */ + symbol_set_value_now (after_size_address); + + if (eh_frame) + { + exp.X_op = O_subtract; + exp.X_add_symbol = after_size_address; + exp.X_op_symbol = cie->start_address; + exp.X_add_number = 0; + emit_expr (&exp, offset_size); /* CIE offset. */ + } + else + { + TC_DWARF2_EMIT_OFFSET (cie->start_address, offset_size); + } + + if (eh_frame) + { + exp.X_op = O_subtract; + exp.X_add_number = 0; +#if CFI_DIFF_EXPR_OK + exp.X_add_symbol = fde->start_address; + exp.X_op_symbol = symbol_temp_new_now (); + emit_expr (&exp, DWARF2_FDE_RELOC_SIZE); /* Code offset. */ +#else + exp.X_op = O_symbol; + exp.X_add_symbol = fde->start_address; +#ifdef tc_cfi_emit_pcrel_expr + tc_cfi_emit_pcrel_expr (&exp, DWARF2_FDE_RELOC_SIZE); /* Code offset. */ +#else + emit_expr (&exp, DWARF2_FDE_RELOC_SIZE); /* Code offset. */ +#endif +#endif + addr_size = DWARF2_FDE_RELOC_SIZE; + } + else + { + exp.X_op = O_symbol; + exp.X_add_symbol = fde->start_address; + exp.X_add_number = 0; + addr_size = DWARF2_ADDR_SIZE (stdoutput); + emit_expr (&exp, addr_size); + } + + exp.X_op = O_subtract; + exp.X_add_symbol = fde->end_address; + exp.X_op_symbol = fde->start_address; /* Code length. */ + exp.X_add_number = 0; + emit_expr (&exp, addr_size); + + augmentation_size = encoding_size (fde->lsda_encoding); + if (eh_frame) + out_uleb128 (augmentation_size); /* Augmentation size. */ + + if (fde->lsda_encoding != DW_EH_PE_omit) + { + exp = fde->lsda; + if ((fde->lsda_encoding & 0x70) == DW_EH_PE_pcrel) + { +#if CFI_DIFF_LSDA_OK + exp.X_op = O_subtract; + exp.X_op_symbol = symbol_temp_new_now (); + emit_expr (&exp, augmentation_size); +#elif defined (tc_cfi_emit_pcrel_expr) + tc_cfi_emit_pcrel_expr (&exp, augmentation_size); +#else + abort (); +#endif + } + else + emit_expr (&exp, augmentation_size); + } + + for (; first; first = first->next) + if (CUR_SEG (first) == CUR_SEG (fde)) + output_cfi_insn (first); + + frag_align (align, DW_CFA_nop, 0); + symbol_set_value_now (end_address); +} + +static struct cie_entry * +select_cie_for_fde (struct fde_entry *fde, bfd_boolean eh_frame, + struct cfi_insn_data **pfirst, int align) +{ + struct cfi_insn_data *i, *j; + struct cie_entry *cie; + + for (cie = cie_root; cie; cie = cie->next) + { + if (CUR_SEG (cie) != CUR_SEG (fde)) + continue; + if (cie->return_column != fde->return_column + || cie->signal_frame != fde->signal_frame + || cie->per_encoding != fde->per_encoding + || cie->lsda_encoding != fde->lsda_encoding) + continue; + if (cie->per_encoding != DW_EH_PE_omit) + { + if (cie->personality.X_op != fde->personality.X_op + || cie->personality.X_add_number + != fde->personality.X_add_number) + continue; + switch (cie->personality.X_op) + { + case O_constant: + if (cie->personality.X_unsigned != fde->personality.X_unsigned) + continue; + break; + case O_symbol: + if (cie->personality.X_add_symbol + != fde->personality.X_add_symbol) + continue; + break; + default: + abort (); + } + } + for (i = cie->first, j = fde->data; + i != cie->last && j != NULL; + i = i->next, j = j->next) + { + if (i->insn != j->insn) + goto fail; + switch (i->insn) + { + case DW_CFA_advance_loc: + case DW_CFA_remember_state: + /* We reached the first advance/remember in the FDE, + but did not reach the end of the CIE list. */ + goto fail; + + case DW_CFA_offset: + case DW_CFA_def_cfa: + if (i->u.ri.reg != j->u.ri.reg) + goto fail; + if (i->u.ri.offset != j->u.ri.offset) + goto fail; + break; + + case DW_CFA_register: + if (i->u.rr.reg1 != j->u.rr.reg1) + goto fail; + if (i->u.rr.reg2 != j->u.rr.reg2) + goto fail; + break; + + case DW_CFA_def_cfa_register: + case DW_CFA_restore: + case DW_CFA_undefined: + case DW_CFA_same_value: + if (i->u.r != j->u.r) + goto fail; + break; + + case DW_CFA_def_cfa_offset: + if (i->u.i != j->u.i) + goto fail; + break; + + case CFI_escape: + case CFI_val_encoded_addr: + /* Don't bother matching these for now. */ + goto fail; + + default: + abort (); + } + } + + /* Success if we reached the end of the CIE list, and we've either + run out of FDE entries or we've encountered an advance, + remember, or escape. */ + if (i == cie->last + && (!j + || j->insn == DW_CFA_advance_loc + || j->insn == DW_CFA_remember_state + || j->insn == CFI_escape + || j->insn == CFI_val_encoded_addr)) + { + *pfirst = j; + return cie; + } + + fail:; + } + + cie = (struct cie_entry *) xmalloc (sizeof (struct cie_entry)); + cie->next = cie_root; + cie_root = cie; + SET_CUR_SEG (cie, CUR_SEG (fde)); + cie->return_column = fde->return_column; + cie->signal_frame = fde->signal_frame; + cie->per_encoding = fde->per_encoding; + cie->lsda_encoding = fde->lsda_encoding; + cie->personality = fde->personality; + cie->first = fde->data; + + for (i = cie->first; i ; i = i->next) + if (i->insn == DW_CFA_advance_loc + || i->insn == DW_CFA_remember_state + || i->insn == CFI_escape + || i->insn == CFI_val_encoded_addr) + break; + + cie->last = i; + *pfirst = i; + + output_cie (cie, eh_frame, align); + + return cie; +} + +#ifdef md_reg_eh_frame_to_debug_frame +static void +cfi_change_reg_numbers (struct cfi_insn_data *insn, segT ccseg) +{ + for (; insn; insn = insn->next) + { + if (CUR_SEG (insn) != ccseg) + continue; + switch (insn->insn) + { + case DW_CFA_advance_loc: + case DW_CFA_def_cfa_offset: + case DW_CFA_remember_state: + case DW_CFA_restore_state: + case DW_CFA_GNU_window_save: + case CFI_escape: + break; + + case DW_CFA_def_cfa: + case DW_CFA_offset: + insn->u.ri.reg = md_reg_eh_frame_to_debug_frame (insn->u.ri.reg); + break; + + case DW_CFA_def_cfa_register: + case DW_CFA_undefined: + case DW_CFA_same_value: + case DW_CFA_restore: + insn->u.r = md_reg_eh_frame_to_debug_frame (insn->u.r); + break; + + case DW_CFA_register: + insn->u.rr.reg1 = md_reg_eh_frame_to_debug_frame (insn->u.rr.reg1); + insn->u.rr.reg2 = md_reg_eh_frame_to_debug_frame (insn->u.rr.reg2); + break; + + case CFI_val_encoded_addr: + insn->u.ea.reg = md_reg_eh_frame_to_debug_frame (insn->u.ea.reg); + break; + + default: + abort (); + } + } +} +#else +#define cfi_change_reg_numbers(insn, cseg) do { } while (0) +#endif + +static segT +get_cfi_seg (segT cseg, const char *base, flagword flags, int align) +{ + if (SUPPORT_FRAME_LINKONCE) + { + struct dwcfi_seg_list *l; + + l = dwcfi_hash_find_or_make (cseg, base, flags); + + cseg = l->seg; + subseg_set (cseg, l->subseg); + } + else + { + cseg = subseg_new (base, 0); + bfd_set_section_flags (stdoutput, cseg, flags); + } + record_alignment (cseg, align); + return cseg; +} + +void +cfi_finish (void) +{ + struct cie_entry *cie, *cie_next; + segT cfi_seg, ccseg; + struct fde_entry *fde; + struct cfi_insn_data *first; + int save_flag_traditional_format, seek_next_seg; + + if (all_fde_data == 0) + return; + + if ((cfi_sections & CFI_EMIT_eh_frame) != 0) + { + /* Make sure check_eh_frame doesn't do anything with our output. */ + save_flag_traditional_format = flag_traditional_format; + flag_traditional_format = 1; + + if (!SUPPORT_FRAME_LINKONCE) + { + /* Open .eh_frame section. */ + cfi_seg = get_cfi_seg (NULL, ".eh_frame", + (SEC_ALLOC | SEC_LOAD | SEC_DATA + | DWARF2_EH_FRAME_READ_ONLY), + EH_FRAME_ALIGNMENT); +#ifdef md_fix_up_eh_frame + md_fix_up_eh_frame (cfi_seg); +#else + (void) cfi_seg; +#endif + } + + do + { + ccseg = NULL; + seek_next_seg = 0; + + for (cie = cie_root; cie; cie = cie_next) + { + cie_next = cie->next; + free ((void *) cie); + } + cie_root = NULL; + + for (fde = all_fde_data; fde ; fde = fde->next) + { + if (SUPPORT_FRAME_LINKONCE) + { + if (HANDLED (fde)) + continue; + if (seek_next_seg && CUR_SEG (fde) != ccseg) + { + seek_next_seg = 2; + continue; + } + if (!seek_next_seg) + { + ccseg = CUR_SEG (fde); + /* Open .eh_frame section. */ + cfi_seg = get_cfi_seg (ccseg, ".eh_frame", + (SEC_ALLOC | SEC_LOAD | SEC_DATA + | DWARF2_EH_FRAME_READ_ONLY), + EH_FRAME_ALIGNMENT); +#ifdef md_fix_up_eh_frame + md_fix_up_eh_frame (cfi_seg); +#else + (void) cfi_seg; +#endif + seek_next_seg = 1; + } + SET_HANDLED (fde, 1); + } + + if (fde->end_address == NULL) + { + as_bad (_("open CFI at the end of file; missing .cfi_endproc directive")); + fde->end_address = fde->start_address; + } + + cie = select_cie_for_fde (fde, TRUE, &first, 2); + output_fde (fde, cie, TRUE, first, + fde->next == NULL ? EH_FRAME_ALIGNMENT : 2); + } + } + while (SUPPORT_FRAME_LINKONCE && seek_next_seg == 2); + + if (SUPPORT_FRAME_LINKONCE) + for (fde = all_fde_data; fde ; fde = fde->next) + SET_HANDLED (fde, 0); + + flag_traditional_format = save_flag_traditional_format; + } + + if ((cfi_sections & CFI_EMIT_debug_frame) != 0) + { + int alignment = ffs (DWARF2_ADDR_SIZE (stdoutput)) - 1; + + if (!SUPPORT_FRAME_LINKONCE) + get_cfi_seg (NULL, ".debug_frame", + SEC_READONLY | SEC_DEBUGGING, + alignment); + + do + { + ccseg = NULL; + seek_next_seg = 0; + + for (cie = cie_root; cie; cie = cie_next) + { + cie_next = cie->next; + free ((void *) cie); + } + cie_root = NULL; + + for (fde = all_fde_data; fde ; fde = fde->next) + { + if (SUPPORT_FRAME_LINKONCE) + { + if (HANDLED (fde)) + continue; + if (seek_next_seg && CUR_SEG (fde) != ccseg) + { + seek_next_seg = 2; + continue; + } + if (!seek_next_seg) + { + ccseg = CUR_SEG (fde); + /* Open .debug_frame section. */ + get_cfi_seg (ccseg, ".debug_frame", + SEC_READONLY | SEC_DEBUGGING, + alignment); + seek_next_seg = 1; + } + SET_HANDLED (fde, 1); + } + if (fde->end_address == NULL) + { + as_bad (_("open CFI at the end of file; missing .cfi_endproc directive")); + fde->end_address = fde->start_address; + } + + fde->per_encoding = DW_EH_PE_omit; + fde->lsda_encoding = DW_EH_PE_omit; + cfi_change_reg_numbers (fde->data, ccseg); + cie = select_cie_for_fde (fde, FALSE, &first, alignment); + output_fde (fde, cie, FALSE, first, alignment); + } + } + while (SUPPORT_FRAME_LINKONCE && seek_next_seg == 2); + + if (SUPPORT_FRAME_LINKONCE) + for (fde = all_fde_data; fde ; fde = fde->next) + SET_HANDLED (fde, 0); + } +} + +#else /* TARGET_USE_CFIPOP */ + +/* Emit an intelligible error message for missing support. */ + +static void +dot_cfi_dummy (int ignored ATTRIBUTE_UNUSED) +{ + as_bad (_("CFI is not supported for this target")); + ignore_rest_of_line (); +} + +const pseudo_typeS cfi_pseudo_table[] = + { + { "cfi_sections", dot_cfi_dummy, 0 }, + { "cfi_startproc", dot_cfi_dummy, 0 }, + { "cfi_endproc", dot_cfi_dummy, 0 }, + { "cfi_def_cfa", dot_cfi_dummy, 0 }, + { "cfi_def_cfa_register", dot_cfi_dummy, 0 }, + { "cfi_def_cfa_offset", dot_cfi_dummy, 0 }, + { "cfi_adjust_cfa_offset", dot_cfi_dummy, 0 }, + { "cfi_offset", dot_cfi_dummy, 0 }, + { "cfi_rel_offset", dot_cfi_dummy, 0 }, + { "cfi_register", dot_cfi_dummy, 0 }, + { "cfi_return_column", dot_cfi_dummy, 0 }, + { "cfi_restore", dot_cfi_dummy, 0 }, + { "cfi_undefined", dot_cfi_dummy, 0 }, + { "cfi_same_value", dot_cfi_dummy, 0 }, + { "cfi_remember_state", dot_cfi_dummy, 0 }, + { "cfi_restore_state", dot_cfi_dummy, 0 }, + { "cfi_window_save", dot_cfi_dummy, 0 }, + { "cfi_escape", dot_cfi_dummy, 0 }, + { "cfi_signal_frame", dot_cfi_dummy, 0 }, + { "cfi_personality", dot_cfi_dummy, 0 }, + { "cfi_lsda", dot_cfi_dummy, 0 }, + { "cfi_val_encoded_addr", dot_cfi_dummy, 0 }, + { NULL, NULL, 0 } + }; + +void +cfi_finish (void) +{ +} +#endif /* TARGET_USE_CFIPOP */ diff --git a/contrib/toolchain/binutils/gas/dw2gencfi.h b/contrib/toolchain/binutils/gas/dw2gencfi.h new file mode 100644 index 0000000000..7f834966b9 --- /dev/null +++ b/contrib/toolchain/binutils/gas/dw2gencfi.h @@ -0,0 +1,132 @@ +/* dw2gencfi.h - Support for generating Dwarf2 CFI information. + Copyright 2003, 2004, 2005, 2007, 2009 Free Software Foundation, Inc. + Contributed by Michal Ludvig + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef DW2GENCFI_H +#define DW2GENCFI_H + +#include "dwarf2.h" + +struct symbol; + +extern const pseudo_typeS cfi_pseudo_table[]; + +/* cfi_finish() is called at the end of file. It will complain if + the last CFI wasn't properly closed by .cfi_endproc. */ +extern void cfi_finish (void); + +/* Entry points for backends to add unwind information. */ +extern void cfi_new_fde (struct symbol *); +extern void cfi_end_fde (struct symbol *); +extern void cfi_set_return_column (unsigned); +extern void cfi_add_advance_loc (struct symbol *); + +extern void cfi_add_CFA_offset (unsigned, offsetT); +extern void cfi_add_CFA_def_cfa (unsigned, offsetT); +extern void cfi_add_CFA_register (unsigned, unsigned); +extern void cfi_add_CFA_def_cfa_register (unsigned); +extern void cfi_add_CFA_def_cfa_offset (offsetT); +extern void cfi_add_CFA_restore (unsigned); +extern void cfi_add_CFA_undefined (unsigned); +extern void cfi_add_CFA_same_value (unsigned); +extern void cfi_add_CFA_remember_state (void); +extern void cfi_add_CFA_restore_state (void); + +/* Structures for md_cfi_end. */ + +#if defined (TE_PE) || defined (TE_PEP) +#define SUPPORT_FRAME_LINKONCE 1 +#else +#define SUPPORT_FRAME_LINKONCE 0 +#endif + +struct cfi_insn_data +{ + struct cfi_insn_data *next; +#if SUPPORT_FRAME_LINKONCE + segT cur_seg; +#endif + int insn; + union + { + struct + { + unsigned reg; + offsetT offset; + } ri; + + struct + { + unsigned reg1; + unsigned reg2; + } rr; + + unsigned r; + offsetT i; + + struct + { + symbolS *lab1; + symbolS *lab2; + } ll; + + struct cfi_escape_data *esc; + + struct + { + unsigned reg, encoding; + expressionS exp; + } ea; + } u; +}; + +struct fde_entry +{ + struct fde_entry *next; +#if SUPPORT_FRAME_LINKONCE + segT cur_seg; +#endif + symbolS *start_address; + symbolS *end_address; + struct cfi_insn_data *data; + struct cfi_insn_data **last; + unsigned char per_encoding; + unsigned char lsda_encoding; + expressionS personality; + expressionS lsda; + unsigned int return_column; + unsigned int signal_frame; +#if SUPPORT_FRAME_LINKONCE + int handled; +#endif +}; + +/* The list of all FDEs that have been collected. */ +extern struct fde_entry *all_fde_data; + +/* Fake CFI type; outside the byte range of any real CFI insn. */ +#define CFI_adjust_cfa_offset 0x100 +#define CFI_return_column 0x101 +#define CFI_rel_offset 0x102 +#define CFI_escape 0x103 +#define CFI_signal_frame 0x104 +#define CFI_val_encoded_addr 0x105 + +#endif /* DW2GENCFI_H */ diff --git a/contrib/toolchain/binutils/gas/dwarf2dbg.c b/contrib/toolchain/binutils/gas/dwarf2dbg.c new file mode 100644 index 0000000000..6d6ee2dd19 --- /dev/null +++ b/contrib/toolchain/binutils/gas/dwarf2dbg.c @@ -0,0 +1,1944 @@ +/* dwarf2dbg.c - DWARF2 debug support + Copyright 1999-2013 Free Software Foundation, Inc. + Contributed by David Mosberger-Tang + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* Logical line numbers can be controlled by the compiler via the + following directives: + + .file FILENO "file.c" + .loc FILENO LINENO [COLUMN] [basic_block] [prologue_end] \ + [epilogue_begin] [is_stmt VALUE] [isa VALUE] \ + [discriminator VALUE] +*/ + +#include "as.h" +#include "safe-ctype.h" + +#ifdef HAVE_LIMITS_H +#include +#else +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#ifndef INT_MAX +#define INT_MAX (int) (((unsigned) (-1)) >> 1) +#endif +#endif + +#include "dwarf2dbg.h" +#include + +#ifdef HAVE_DOS_BASED_FILE_SYSTEM +/* We need to decide which character to use as a directory separator. + Just because HAVE_DOS_BASED_FILE_SYSTEM is defined, it does not + necessarily mean that the backslash character is the one to use. + Some environments, eg Cygwin, can support both naming conventions. + So we use the heuristic that we only need to use the backslash if + the path is an absolute path starting with a DOS style drive + selector. eg C: or D: */ +# define INSERT_DIR_SEPARATOR(string, offset) \ + do \ + { \ + if (offset > 1 \ + && string[0] != 0 \ + && string[1] == ':') \ + string [offset] = '\\'; \ + else \ + string [offset] = '/'; \ + } \ + while (0) +#else +# define INSERT_DIR_SEPARATOR(string, offset) string[offset] = '/' +#endif + +#ifndef DWARF2_FORMAT +# define DWARF2_FORMAT(SEC) dwarf2_format_32bit +#endif + +#ifndef DWARF2_ADDR_SIZE +# define DWARF2_ADDR_SIZE(bfd) (bfd_arch_bits_per_address (bfd) / 8) +#endif + +#ifndef DWARF2_FILE_NAME +#define DWARF2_FILE_NAME(FILENAME, DIRNAME) FILENAME +#endif + +#ifndef DWARF2_FILE_TIME_NAME +#define DWARF2_FILE_TIME_NAME(FILENAME,DIRNAME) 0 +#endif + +#ifndef DWARF2_FILE_SIZE_NAME +#define DWARF2_FILE_SIZE_NAME(FILENAME,DIRNAME) 0 +#endif + +#ifndef DWARF2_VERSION +#define DWARF2_VERSION 2 +#endif + +/* The .debug_aranges version has been 2 in DWARF version 2, 3 and 4. */ +#ifndef DWARF2_ARANGES_VERSION +#define DWARF2_ARANGES_VERSION 2 +#endif + +/* This implementation output version 2 .debug_line information. */ +#ifndef DWARF2_LINE_VERSION +#define DWARF2_LINE_VERSION 2 +#endif + +#include "subsegs.h" + +#include "dwarf2.h" + +/* Since we can't generate the prolog until the body is complete, we + use three different subsegments for .debug_line: one holding the + prolog, one for the directory and filename info, and one for the + body ("statement program"). */ +#define DL_PROLOG 0 +#define DL_FILES 1 +#define DL_BODY 2 + +/* If linker relaxation might change offsets in the code, the DWARF special + opcodes and variable-length operands cannot be used. If this macro is + nonzero, use the DW_LNS_fixed_advance_pc opcode instead. */ +#ifndef DWARF2_USE_FIXED_ADVANCE_PC +# define DWARF2_USE_FIXED_ADVANCE_PC linkrelax +#endif + +/* First special line opcde - leave room for the standard opcodes. + Note: If you want to change this, you'll have to update the + "standard_opcode_lengths" table that is emitted below in + out_debug_line(). */ +#define DWARF2_LINE_OPCODE_BASE 13 + +#ifndef DWARF2_LINE_BASE + /* Minimum line offset in a special line info. opcode. This value + was chosen to give a reasonable range of values. */ +# define DWARF2_LINE_BASE -5 +#endif + +/* Range of line offsets in a special line info. opcode. */ +#ifndef DWARF2_LINE_RANGE +# define DWARF2_LINE_RANGE 14 +#endif + +#ifndef DWARF2_LINE_MIN_INSN_LENGTH + /* Define the architecture-dependent minimum instruction length (in + bytes). This value should be rather too small than too big. */ +# define DWARF2_LINE_MIN_INSN_LENGTH 1 +#endif + +/* Flag that indicates the initial value of the is_stmt_start flag. */ +#define DWARF2_LINE_DEFAULT_IS_STMT 1 + +/* Given a special op, return the line skip amount. */ +#define SPECIAL_LINE(op) \ + (((op) - DWARF2_LINE_OPCODE_BASE)%DWARF2_LINE_RANGE + DWARF2_LINE_BASE) + +/* Given a special op, return the address skip amount (in units of + DWARF2_LINE_MIN_INSN_LENGTH. */ +#define SPECIAL_ADDR(op) (((op) - DWARF2_LINE_OPCODE_BASE)/DWARF2_LINE_RANGE) + +/* The maximum address skip amount that can be encoded with a special op. */ +#define MAX_SPECIAL_ADDR_DELTA SPECIAL_ADDR(255) + +struct line_entry { + struct line_entry *next; + symbolS *label; + struct dwarf2_line_info loc; +}; + +struct line_subseg { + struct line_subseg *next; + subsegT subseg; + struct line_entry *head; + struct line_entry **ptail; + struct line_entry **pmove_tail; +}; + +struct line_seg { + struct line_seg *next; + segT seg; + struct line_subseg *head; + symbolS *text_start; + symbolS *text_end; +}; + +/* Collects data for all line table entries during assembly. */ +static struct line_seg *all_segs; +/* Hash used to quickly lookup a segment by name, avoiding the need to search + through the all_segs list. */ +static struct hash_control *all_segs_hash; +static struct line_seg **last_seg_ptr; + +struct file_entry { + const char *filename; + unsigned int dir; +}; + +/* Table of files used by .debug_line. */ +static struct file_entry *files; +static unsigned int files_in_use; +static unsigned int files_allocated; + +/* Table of directories used by .debug_line. */ +static char **dirs; +static unsigned int dirs_in_use; +static unsigned int dirs_allocated; + +/* TRUE when we've seen a .loc directive recently. Used to avoid + doing work when there's nothing to do. */ +bfd_boolean dwarf2_loc_directive_seen; + +/* TRUE when we're supposed to set the basic block mark whenever a + label is seen. */ +bfd_boolean dwarf2_loc_mark_labels; + +/* Current location as indicated by the most recent .loc directive. */ +static struct dwarf2_line_info current = { + 1, 1, 0, 0, + DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0, + 0 +}; + +/* The size of an address on the target. */ +static unsigned int sizeof_address; + +static unsigned int get_filenum (const char *, unsigned int); + +#ifndef TC_DWARF2_EMIT_OFFSET +#define TC_DWARF2_EMIT_OFFSET generic_dwarf2_emit_offset + +/* Create an offset to .dwarf2_*. */ + +static void +generic_dwarf2_emit_offset (symbolS *symbol, unsigned int size) +{ + expressionS exp; + + exp.X_op = O_symbol; + exp.X_add_symbol = symbol; + exp.X_add_number = 0; + emit_expr (&exp, size); +} +#endif + +/* Find or create (if CREATE_P) an entry for SEG+SUBSEG in ALL_SEGS. */ + +static struct line_subseg * +get_line_subseg (segT seg, subsegT subseg, bfd_boolean create_p) +{ + static segT last_seg; + static subsegT last_subseg; + static struct line_subseg *last_line_subseg; + + struct line_seg *s; + struct line_subseg **pss, *lss; + + if (seg == last_seg && subseg == last_subseg) + return last_line_subseg; + + s = (struct line_seg *) hash_find (all_segs_hash, seg->name); + if (s == NULL) + { + if (!create_p) + return NULL; + + s = (struct line_seg *) xmalloc (sizeof (*s)); + s->next = NULL; + s->seg = seg; + s->head = NULL; + *last_seg_ptr = s; + last_seg_ptr = &s->next; + hash_insert (all_segs_hash, seg->name, s); + } + gas_assert (seg == s->seg); + + for (pss = &s->head; (lss = *pss) != NULL ; pss = &lss->next) + { + if (lss->subseg == subseg) + goto found_subseg; + if (lss->subseg > subseg) + break; + } + + lss = (struct line_subseg *) xmalloc (sizeof (*lss)); + lss->next = *pss; + lss->subseg = subseg; + lss->head = NULL; + lss->ptail = &lss->head; + lss->pmove_tail = &lss->head; + *pss = lss; + + found_subseg: + last_seg = seg; + last_subseg = subseg; + last_line_subseg = lss; + + return lss; +} + +/* Record an entry for LOC occurring at LABEL. */ + +static void +dwarf2_gen_line_info_1 (symbolS *label, struct dwarf2_line_info *loc) +{ + struct line_subseg *lss; + struct line_entry *e; + + e = (struct line_entry *) xmalloc (sizeof (*e)); + e->next = NULL; + e->label = label; + e->loc = *loc; + + lss = get_line_subseg (now_seg, now_subseg, TRUE); + *lss->ptail = e; + lss->ptail = &e->next; +} + +/* Record an entry for LOC occurring at OFS within the current fragment. */ + +void +dwarf2_gen_line_info (addressT ofs, struct dwarf2_line_info *loc) +{ + static unsigned int line = -1; + static unsigned int filenum = -1; + + symbolS *sym; + + /* Early out for as-yet incomplete location information. */ + if (loc->filenum == 0 || loc->line == 0) + return; + + /* Don't emit sequences of line symbols for the same line when the + symbols apply to assembler code. It is necessary to emit + duplicate line symbols when a compiler asks for them, because GDB + uses them to determine the end of the prologue. */ + if (debug_type == DEBUG_DWARF2 + && line == loc->line && filenum == loc->filenum) + return; + + line = loc->line; + filenum = loc->filenum; + + if (linkrelax) + { + char name[120]; + + /* Use a non-fake name for the line number location, + so that it can be referred to by relocations. */ + sprintf (name, ".Loc.%u.%u", line, filenum); + sym = symbol_new (name, now_seg, ofs, frag_now); + } + else + sym = symbol_temp_new (now_seg, ofs, frag_now); + dwarf2_gen_line_info_1 (sym, loc); +} + +/* Returns the current source information. If .file directives have + been encountered, the info for the corresponding source file is + returned. Otherwise, the info for the assembly source file is + returned. */ + +void +dwarf2_where (struct dwarf2_line_info *line) +{ + if (debug_type == DEBUG_DWARF2) + { + char *filename; + as_where (&filename, &line->line); + line->filenum = get_filenum (filename, 0); + line->column = 0; + line->flags = DWARF2_FLAG_IS_STMT; + line->isa = current.isa; + line->discriminator = current.discriminator; + } + else + *line = current; +} + +/* A hook to allow the target backend to inform the line number state + machine of isa changes when assembler debug info is enabled. */ + +void +dwarf2_set_isa (unsigned int isa) +{ + current.isa = isa; +} + +/* Called for each machine instruction, or relatively atomic group of + machine instructions (ie built-in macro). The instruction or group + is SIZE bytes in length. If dwarf2 line number generation is called + for, emit a line statement appropriately. */ + +void +dwarf2_emit_insn (int size) +{ + struct dwarf2_line_info loc; + + if (!dwarf2_loc_directive_seen && debug_type != DEBUG_DWARF2) + return; + + dwarf2_where (&loc); + + dwarf2_gen_line_info (frag_now_fix () - size, &loc); + dwarf2_consume_line_info (); +} + +/* Move all previously-emitted line entries for the current position by + DELTA bytes. This function cannot be used to move the same entries + twice. */ + +void +dwarf2_move_insn (int delta) +{ + struct line_subseg *lss; + struct line_entry *e; + valueT now; + + if (delta == 0) + return; + + lss = get_line_subseg (now_seg, now_subseg, FALSE); + if (!lss) + return; + + now = frag_now_fix (); + while ((e = *lss->pmove_tail)) + { + if (S_GET_VALUE (e->label) == now) + S_SET_VALUE (e->label, now + delta); + lss->pmove_tail = &e->next; + } +} + +/* Called after the current line information has been either used with + dwarf2_gen_line_info or saved with a machine instruction for later use. + This resets the state of the line number information to reflect that + it has been used. */ + +void +dwarf2_consume_line_info (void) +{ + /* Unless we generate DWARF2 debugging information for each + assembler line, we only emit one line symbol for one LOC. */ + dwarf2_loc_directive_seen = FALSE; + + current.flags &= ~(DWARF2_FLAG_BASIC_BLOCK + | DWARF2_FLAG_PROLOGUE_END + | DWARF2_FLAG_EPILOGUE_BEGIN); + current.discriminator = 0; +} + +/* Called for each (preferably code) label. If dwarf2_loc_mark_labels + is enabled, emit a basic block marker. */ + +void +dwarf2_emit_label (symbolS *label) +{ + struct dwarf2_line_info loc; + + if (!dwarf2_loc_mark_labels) + return; + if (S_GET_SEGMENT (label) != now_seg) + return; + if (!(bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE)) + return; + if (files_in_use == 0 && debug_type != DEBUG_DWARF2) + return; + + dwarf2_where (&loc); + + loc.flags |= DWARF2_FLAG_BASIC_BLOCK; + + dwarf2_gen_line_info_1 (label, &loc); + dwarf2_consume_line_info (); +} + +/* Get a .debug_line file number for FILENAME. If NUM is nonzero, + allocate it on that file table slot, otherwise return the first + empty one. */ + +static unsigned int +get_filenum (const char *filename, unsigned int num) +{ + static unsigned int last_used, last_used_dir_len; + const char *file; + size_t dir_len; + unsigned int i, dir; + + if (num == 0 && last_used) + { + if (! files[last_used].dir + && filename_cmp (filename, files[last_used].filename) == 0) + return last_used; + if (files[last_used].dir + && filename_ncmp (filename, dirs[files[last_used].dir], + last_used_dir_len) == 0 + && IS_DIR_SEPARATOR (filename [last_used_dir_len]) + && filename_cmp (filename + last_used_dir_len + 1, + files[last_used].filename) == 0) + return last_used; + } + + file = lbasename (filename); + /* Don't make empty string from / or A: from A:/ . */ +#ifdef HAVE_DOS_BASED_FILE_SYSTEM + if (file <= filename + 3) + file = filename; +#else + if (file == filename + 1) + file = filename; +#endif + dir_len = file - filename; + + dir = 0; + if (dir_len) + { +#ifndef DWARF2_DIR_SHOULD_END_WITH_SEPARATOR + --dir_len; +#endif + for (dir = 1; dir < dirs_in_use; ++dir) + if (filename_ncmp (filename, dirs[dir], dir_len) == 0 + && dirs[dir][dir_len] == '\0') + break; + + if (dir >= dirs_in_use) + { + if (dir >= dirs_allocated) + { + dirs_allocated = dir + 32; + dirs = (char **) + xrealloc (dirs, (dir + 32) * sizeof (const char *)); + } + + dirs[dir] = (char *) xmalloc (dir_len + 1); + memcpy (dirs[dir], filename, dir_len); + dirs[dir][dir_len] = '\0'; + dirs_in_use = dir + 1; + } + } + + if (num == 0) + { + for (i = 1; i < files_in_use; ++i) + if (files[i].dir == dir + && files[i].filename + && filename_cmp (file, files[i].filename) == 0) + { + last_used = i; + last_used_dir_len = dir_len; + return i; + } + } + else + i = num; + + if (i >= files_allocated) + { + unsigned int old = files_allocated; + + files_allocated = i + 32; + files = (struct file_entry *) + xrealloc (files, (i + 32) * sizeof (struct file_entry)); + + memset (files + old, 0, (i + 32 - old) * sizeof (struct file_entry)); + } + + files[i].filename = num ? file : xstrdup (file); + files[i].dir = dir; + if (files_in_use < i + 1) + files_in_use = i + 1; + last_used = i; + last_used_dir_len = dir_len; + + return i; +} + +/* Handle two forms of .file directive: + - Pass .file "source.c" to s_app_file + - Handle .file 1 "source.c" by adding an entry to the DWARF-2 file table + + If an entry is added to the file table, return a pointer to the filename. */ + +char * +dwarf2_directive_file (int dummy ATTRIBUTE_UNUSED) +{ + offsetT num; + char *filename; + int filename_len; + + /* Continue to accept a bare string and pass it off. */ + SKIP_WHITESPACE (); + if (*input_line_pointer == '"') + { + s_app_file (0); + return NULL; + } + + num = get_absolute_expression (); + filename = demand_copy_C_string (&filename_len); + if (filename == NULL) + return NULL; + demand_empty_rest_of_line (); + + if (num < 1) + { + as_bad (_("file number less than one")); + return NULL; + } + + /* A .file directive implies compiler generated debug information is + being supplied. Turn off gas generated debug info. */ + debug_type = DEBUG_NONE; + + if (num < (int) files_in_use && files[num].filename != 0) + { + as_bad (_("file number %ld already allocated"), (long) num); + return NULL; + } + + get_filenum (filename, num); + + return filename; +} + +void +dwarf2_directive_loc (int dummy ATTRIBUTE_UNUSED) +{ + offsetT filenum, line; + + /* If we see two .loc directives in a row, force the first one to be + output now. */ + if (dwarf2_loc_directive_seen) + dwarf2_emit_insn (0); + + filenum = get_absolute_expression (); + SKIP_WHITESPACE (); + line = get_absolute_expression (); + + if (filenum < 1) + { + as_bad (_("file number less than one")); + return; + } + if (filenum >= (int) files_in_use || files[filenum].filename == 0) + { + as_bad (_("unassigned file number %ld"), (long) filenum); + return; + } + + current.filenum = filenum; + current.line = line; + current.discriminator = 0; + +#ifndef NO_LISTING + if (listing) + { + if (files[filenum].dir) + { + size_t dir_len = strlen (dirs[files[filenum].dir]); + size_t file_len = strlen (files[filenum].filename); + char *cp = (char *) alloca (dir_len + 1 + file_len + 1); + + memcpy (cp, dirs[files[filenum].dir], dir_len); + INSERT_DIR_SEPARATOR (cp, dir_len); + memcpy (cp + dir_len + 1, files[filenum].filename, file_len); + cp[dir_len + file_len + 1] = '\0'; + listing_source_file (cp); + } + else + listing_source_file (files[filenum].filename); + listing_source_line (line); + } +#endif + + SKIP_WHITESPACE (); + if (ISDIGIT (*input_line_pointer)) + { + current.column = get_absolute_expression (); + SKIP_WHITESPACE (); + } + + while (ISALPHA (*input_line_pointer)) + { + char *p, c; + offsetT value; + + p = input_line_pointer; + c = get_symbol_end (); + + if (strcmp (p, "basic_block") == 0) + { + current.flags |= DWARF2_FLAG_BASIC_BLOCK; + *input_line_pointer = c; + } + else if (strcmp (p, "prologue_end") == 0) + { + current.flags |= DWARF2_FLAG_PROLOGUE_END; + *input_line_pointer = c; + } + else if (strcmp (p, "epilogue_begin") == 0) + { + current.flags |= DWARF2_FLAG_EPILOGUE_BEGIN; + *input_line_pointer = c; + } + else if (strcmp (p, "is_stmt") == 0) + { + *input_line_pointer = c; + value = get_absolute_expression (); + if (value == 0) + current.flags &= ~DWARF2_FLAG_IS_STMT; + else if (value == 1) + current.flags |= DWARF2_FLAG_IS_STMT; + else + { + as_bad (_("is_stmt value not 0 or 1")); + return; + } + } + else if (strcmp (p, "isa") == 0) + { + *input_line_pointer = c; + value = get_absolute_expression (); + if (value >= 0) + current.isa = value; + else + { + as_bad (_("isa number less than zero")); + return; + } + } + else if (strcmp (p, "discriminator") == 0) + { + *input_line_pointer = c; + value = get_absolute_expression (); + if (value >= 0) + current.discriminator = value; + else + { + as_bad (_("discriminator less than zero")); + return; + } + } + else + { + as_bad (_("unknown .loc sub-directive `%s'"), p); + *input_line_pointer = c; + return; + } + + SKIP_WHITESPACE (); + } + + demand_empty_rest_of_line (); + dwarf2_loc_directive_seen = TRUE; + debug_type = DEBUG_NONE; +} + +void +dwarf2_directive_loc_mark_labels (int dummy ATTRIBUTE_UNUSED) +{ + offsetT value = get_absolute_expression (); + + if (value != 0 && value != 1) + { + as_bad (_("expected 0 or 1")); + ignore_rest_of_line (); + } + else + { + dwarf2_loc_mark_labels = value != 0; + demand_empty_rest_of_line (); + } +} + +static struct frag * +first_frag_for_seg (segT seg) +{ + return seg_info (seg)->frchainP->frch_root; +} + +static struct frag * +last_frag_for_seg (segT seg) +{ + frchainS *f = seg_info (seg)->frchainP; + + while (f->frch_next != NULL) + f = f->frch_next; + + return f->frch_last; +} + +/* Emit a single byte into the current segment. */ + +static inline void +out_byte (int byte) +{ + FRAG_APPEND_1_CHAR (byte); +} + +/* Emit a statement program opcode into the current segment. */ + +static inline void +out_opcode (int opc) +{ + out_byte (opc); +} + +/* Emit a two-byte word into the current segment. */ + +static inline void +out_two (int data) +{ + md_number_to_chars (frag_more (2), data, 2); +} + +/* Emit a four byte word into the current segment. */ + +static inline void +out_four (int data) +{ + md_number_to_chars (frag_more (4), data, 4); +} + +/* Emit an unsigned "little-endian base 128" number. */ + +static void +out_uleb128 (addressT value) +{ + output_leb128 (frag_more (sizeof_leb128 (value, 0)), value, 0); +} + +/* Emit a signed "little-endian base 128" number. */ + +static void +out_leb128 (addressT value) +{ + output_leb128 (frag_more (sizeof_leb128 (value, 1)), value, 1); +} + +/* Emit a tuple for .debug_abbrev. */ + +static inline void +out_abbrev (int name, int form) +{ + out_uleb128 (name); + out_uleb128 (form); +} + +/* Get the size of a fragment. */ + +static offsetT +get_frag_fix (fragS *frag, segT seg) +{ + frchainS *fr; + + if (frag->fr_next) + return frag->fr_fix; + + /* If a fragment is the last in the chain, special measures must be + taken to find its size before relaxation, since it may be pending + on some subsegment chain. */ + for (fr = seg_info (seg)->frchainP; fr; fr = fr->frch_next) + if (fr->frch_last == frag) + return (char *) obstack_next_free (&fr->frch_obstack) - frag->fr_literal; + + abort (); +} + +/* Set an absolute address (may result in a relocation entry). */ + +static void +out_set_addr (symbolS *sym) +{ + expressionS exp; + + out_opcode (DW_LNS_extended_op); + out_uleb128 (sizeof_address + 1); + + out_opcode (DW_LNE_set_address); + exp.X_op = O_symbol; + exp.X_add_symbol = sym; + exp.X_add_number = 0; + emit_expr (&exp, sizeof_address); +} + +static void scale_addr_delta (addressT *); + +static void +scale_addr_delta (addressT *addr_delta) +{ + static int printed_this = 0; + if (DWARF2_LINE_MIN_INSN_LENGTH > 1) + { + if (*addr_delta % DWARF2_LINE_MIN_INSN_LENGTH != 0 && !printed_this) + { + as_bad("unaligned opcodes detected in executable segment"); + printed_this = 1; + } + *addr_delta /= DWARF2_LINE_MIN_INSN_LENGTH; + } +} + +/* Encode a pair of line and address skips as efficiently as possible. + Note that the line skip is signed, whereas the address skip is unsigned. + + The following two routines *must* be kept in sync. This is + enforced by making emit_inc_line_addr abort if we do not emit + exactly the expected number of bytes. */ + +static int +size_inc_line_addr (int line_delta, addressT addr_delta) +{ + unsigned int tmp, opcode; + int len = 0; + + /* Scale the address delta by the minimum instruction length. */ + scale_addr_delta (&addr_delta); + + /* INT_MAX is a signal that this is actually a DW_LNE_end_sequence. + We cannot use special opcodes here, since we want the end_sequence + to emit the matrix entry. */ + if (line_delta == INT_MAX) + { + if (addr_delta == MAX_SPECIAL_ADDR_DELTA) + len = 1; + else + len = 1 + sizeof_leb128 (addr_delta, 0); + return len + 3; + } + + /* Bias the line delta by the base. */ + tmp = line_delta - DWARF2_LINE_BASE; + + /* If the line increment is out of range of a special opcode, we + must encode it with DW_LNS_advance_line. */ + if (tmp >= DWARF2_LINE_RANGE) + { + len = 1 + sizeof_leb128 (line_delta, 1); + line_delta = 0; + tmp = 0 - DWARF2_LINE_BASE; + } + + /* Bias the opcode by the special opcode base. */ + tmp += DWARF2_LINE_OPCODE_BASE; + + /* Avoid overflow when addr_delta is large. */ + if (addr_delta < 256 + MAX_SPECIAL_ADDR_DELTA) + { + /* Try using a special opcode. */ + opcode = tmp + addr_delta * DWARF2_LINE_RANGE; + if (opcode <= 255) + return len + 1; + + /* Try using DW_LNS_const_add_pc followed by special op. */ + opcode = tmp + (addr_delta - MAX_SPECIAL_ADDR_DELTA) * DWARF2_LINE_RANGE; + if (opcode <= 255) + return len + 2; + } + + /* Otherwise use DW_LNS_advance_pc. */ + len += 1 + sizeof_leb128 (addr_delta, 0); + + /* DW_LNS_copy or special opcode. */ + len += 1; + + return len; +} + +static void +emit_inc_line_addr (int line_delta, addressT addr_delta, char *p, int len) +{ + unsigned int tmp, opcode; + int need_copy = 0; + char *end = p + len; + + /* Line number sequences cannot go backward in addresses. This means + we've incorrectly ordered the statements in the sequence. */ + gas_assert ((offsetT) addr_delta >= 0); + + /* Scale the address delta by the minimum instruction length. */ + scale_addr_delta (&addr_delta); + + /* INT_MAX is a signal that this is actually a DW_LNE_end_sequence. + We cannot use special opcodes here, since we want the end_sequence + to emit the matrix entry. */ + if (line_delta == INT_MAX) + { + if (addr_delta == MAX_SPECIAL_ADDR_DELTA) + *p++ = DW_LNS_const_add_pc; + else + { + *p++ = DW_LNS_advance_pc; + p += output_leb128 (p, addr_delta, 0); + } + + *p++ = DW_LNS_extended_op; + *p++ = 1; + *p++ = DW_LNE_end_sequence; + goto done; + } + + /* Bias the line delta by the base. */ + tmp = line_delta - DWARF2_LINE_BASE; + + /* If the line increment is out of range of a special opcode, we + must encode it with DW_LNS_advance_line. */ + if (tmp >= DWARF2_LINE_RANGE) + { + *p++ = DW_LNS_advance_line; + p += output_leb128 (p, line_delta, 1); + + line_delta = 0; + tmp = 0 - DWARF2_LINE_BASE; + need_copy = 1; + } + + /* Prettier, I think, to use DW_LNS_copy instead of a "line +0, addr +0" + special opcode. */ + if (line_delta == 0 && addr_delta == 0) + { + *p++ = DW_LNS_copy; + goto done; + } + + /* Bias the opcode by the special opcode base. */ + tmp += DWARF2_LINE_OPCODE_BASE; + + /* Avoid overflow when addr_delta is large. */ + if (addr_delta < 256 + MAX_SPECIAL_ADDR_DELTA) + { + /* Try using a special opcode. */ + opcode = tmp + addr_delta * DWARF2_LINE_RANGE; + if (opcode <= 255) + { + *p++ = opcode; + goto done; + } + + /* Try using DW_LNS_const_add_pc followed by special op. */ + opcode = tmp + (addr_delta - MAX_SPECIAL_ADDR_DELTA) * DWARF2_LINE_RANGE; + if (opcode <= 255) + { + *p++ = DW_LNS_const_add_pc; + *p++ = opcode; + goto done; + } + } + + /* Otherwise use DW_LNS_advance_pc. */ + *p++ = DW_LNS_advance_pc; + p += output_leb128 (p, addr_delta, 0); + + if (need_copy) + *p++ = DW_LNS_copy; + else + *p++ = tmp; + + done: + gas_assert (p == end); +} + +/* Handy routine to combine calls to the above two routines. */ + +static void +out_inc_line_addr (int line_delta, addressT addr_delta) +{ + int len = size_inc_line_addr (line_delta, addr_delta); + emit_inc_line_addr (line_delta, addr_delta, frag_more (len), len); +} + +/* Write out an alternative form of line and address skips using + DW_LNS_fixed_advance_pc opcodes. This uses more space than the default + line and address information, but it is required if linker relaxation + could change the code offsets. The following two routines *must* be + kept in sync. */ +#define ADDR_DELTA_LIMIT 50000 + +static int +size_fixed_inc_line_addr (int line_delta, addressT addr_delta) +{ + int len = 0; + + /* INT_MAX is a signal that this is actually a DW_LNE_end_sequence. */ + if (line_delta != INT_MAX) + len = 1 + sizeof_leb128 (line_delta, 1); + + if (addr_delta > ADDR_DELTA_LIMIT) + { + /* DW_LNS_extended_op */ + len += 1 + sizeof_leb128 (sizeof_address + 1, 0); + /* DW_LNE_set_address */ + len += 1 + sizeof_address; + } + else + /* DW_LNS_fixed_advance_pc */ + len += 3; + + if (line_delta == INT_MAX) + /* DW_LNS_extended_op + DW_LNE_end_sequence */ + len += 3; + else + /* DW_LNS_copy */ + len += 1; + + return len; +} + +static void +emit_fixed_inc_line_addr (int line_delta, addressT addr_delta, fragS *frag, + char *p, int len) +{ + expressionS *pexp; + char *end = p + len; + + /* Line number sequences cannot go backward in addresses. This means + we've incorrectly ordered the statements in the sequence. */ + gas_assert ((offsetT) addr_delta >= 0); + + /* Verify that we have kept in sync with size_fixed_inc_line_addr. */ + gas_assert (len == size_fixed_inc_line_addr (line_delta, addr_delta)); + + /* INT_MAX is a signal that this is actually a DW_LNE_end_sequence. */ + if (line_delta != INT_MAX) + { + *p++ = DW_LNS_advance_line; + p += output_leb128 (p, line_delta, 1); + } + + pexp = symbol_get_value_expression (frag->fr_symbol); + + /* The DW_LNS_fixed_advance_pc opcode has a 2-byte operand so it can + advance the address by at most 64K. Linker relaxation (without + which this function would not be used) could change the operand by + an unknown amount. If the address increment is getting close to + the limit, just reset the address. */ + if (addr_delta > ADDR_DELTA_LIMIT) + { + symbolS *to_sym; + expressionS exp; + + gas_assert (pexp->X_op == O_subtract); + to_sym = pexp->X_add_symbol; + + *p++ = DW_LNS_extended_op; + p += output_leb128 (p, sizeof_address + 1, 0); + *p++ = DW_LNE_set_address; + exp.X_op = O_symbol; + exp.X_add_symbol = to_sym; + exp.X_add_number = 0; + emit_expr_fix (&exp, sizeof_address, frag, p); + p += sizeof_address; + } + else + { + *p++ = DW_LNS_fixed_advance_pc; + emit_expr_fix (pexp, 2, frag, p); + p += 2; + } + + if (line_delta == INT_MAX) + { + *p++ = DW_LNS_extended_op; + *p++ = 1; + *p++ = DW_LNE_end_sequence; + } + else + *p++ = DW_LNS_copy; + + gas_assert (p == end); +} + +/* Generate a variant frag that we can use to relax address/line + increments between fragments of the target segment. */ + +static void +relax_inc_line_addr (int line_delta, symbolS *to_sym, symbolS *from_sym) +{ + expressionS exp; + int max_chars; + + exp.X_op = O_subtract; + exp.X_add_symbol = to_sym; + exp.X_op_symbol = from_sym; + exp.X_add_number = 0; + + /* The maximum size of the frag is the line delta with a maximum + sized address delta. */ + if (DWARF2_USE_FIXED_ADVANCE_PC) + max_chars = size_fixed_inc_line_addr (line_delta, + -DWARF2_LINE_MIN_INSN_LENGTH); + else + max_chars = size_inc_line_addr (line_delta, -DWARF2_LINE_MIN_INSN_LENGTH); + + frag_var (rs_dwarf2dbg, max_chars, max_chars, 1, + make_expr_symbol (&exp), line_delta, NULL); +} + +/* The function estimates the size of a rs_dwarf2dbg variant frag + based on the current values of the symbols. It is called before + the relaxation loop. We set fr_subtype to the expected length. */ + +int +dwarf2dbg_estimate_size_before_relax (fragS *frag) +{ + offsetT addr_delta; + int size; + + addr_delta = resolve_symbol_value (frag->fr_symbol); + if (DWARF2_USE_FIXED_ADVANCE_PC) + size = size_fixed_inc_line_addr (frag->fr_offset, addr_delta); + else + size = size_inc_line_addr (frag->fr_offset, addr_delta); + + frag->fr_subtype = size; + + return size; +} + +/* This function relaxes a rs_dwarf2dbg variant frag based on the + current values of the symbols. fr_subtype is the current length + of the frag. This returns the change in frag length. */ + +int +dwarf2dbg_relax_frag (fragS *frag) +{ + int old_size, new_size; + + old_size = frag->fr_subtype; + new_size = dwarf2dbg_estimate_size_before_relax (frag); + + return new_size - old_size; +} + +/* This function converts a rs_dwarf2dbg variant frag into a normal + fill frag. This is called after all relaxation has been done. + fr_subtype will be the desired length of the frag. */ + +void +dwarf2dbg_convert_frag (fragS *frag) +{ + offsetT addr_diff; + + if (DWARF2_USE_FIXED_ADVANCE_PC) + { + /* If linker relaxation is enabled then the distance bewteen the two + symbols in the frag->fr_symbol expression might change. Hence we + cannot rely upon the value computed by resolve_symbol_value. + Instead we leave the expression unfinalized and allow + emit_fixed_inc_line_addr to create a fixup (which later becomes a + relocation) that will allow the linker to correctly compute the + actual address difference. We have to use a fixed line advance for + this as we cannot (easily) relocate leb128 encoded values. */ + int saved_finalize_syms = finalize_syms; + + finalize_syms = 0; + addr_diff = resolve_symbol_value (frag->fr_symbol); + finalize_syms = saved_finalize_syms; + } + else + addr_diff = resolve_symbol_value (frag->fr_symbol); + + /* fr_var carries the max_chars that we created the fragment with. + fr_subtype carries the current expected length. We must, of + course, have allocated enough memory earlier. */ + gas_assert (frag->fr_var >= (int) frag->fr_subtype); + + if (DWARF2_USE_FIXED_ADVANCE_PC) + emit_fixed_inc_line_addr (frag->fr_offset, addr_diff, frag, + frag->fr_literal + frag->fr_fix, + frag->fr_subtype); + else + emit_inc_line_addr (frag->fr_offset, addr_diff, + frag->fr_literal + frag->fr_fix, frag->fr_subtype); + + frag->fr_fix += frag->fr_subtype; + frag->fr_type = rs_fill; + frag->fr_var = 0; + frag->fr_offset = 0; +} + +/* Generate .debug_line content for the chain of line number entries + beginning at E, for segment SEG. */ + +static void +process_entries (segT seg, struct line_entry *e) +{ + unsigned filenum = 1; + unsigned line = 1; + unsigned column = 0; + unsigned isa = 0; + unsigned flags = DWARF2_LINE_DEFAULT_IS_STMT ? DWARF2_FLAG_IS_STMT : 0; + fragS *last_frag = NULL, *frag; + addressT last_frag_ofs = 0, frag_ofs; + symbolS *last_lab = NULL, *lab; + struct line_entry *next; + + if (flag_dwarf_sections) + { + char * name; + const char * sec_name; + + /* Switch to the relevent sub-section before we start to emit + the line number table. + + FIXME: These sub-sections do not have a normal Line Number + Program Header, thus strictly speaking they are not valid + DWARF sections. Unfortunately the DWARF standard assumes + a one-to-one relationship between compilation units and + line number tables. Thus we have to have a .debug_line + section, as well as our sub-sections, and we have to ensure + that all of the sub-sections are merged into a proper + .debug_line section before a debugger sees them. */ + + sec_name = bfd_get_section_name (stdoutput, seg); + if (strcmp (sec_name, ".text") != 0) + { + unsigned int len; + + len = strlen (sec_name); + name = xmalloc (len + 11 + 2); + sprintf (name, ".debug_line%s", sec_name); + subseg_set (subseg_get (name, FALSE), 0); + } + else + /* Don't create a .debug_line.text section - + that is redundant. Instead just switch back to the + normal .debug_line section. */ + subseg_set (subseg_get (".debug_line", FALSE), 0); + } + + do + { + int line_delta; + + if (filenum != e->loc.filenum) + { + filenum = e->loc.filenum; + out_opcode (DW_LNS_set_file); + out_uleb128 (filenum); + } + + if (column != e->loc.column) + { + column = e->loc.column; + out_opcode (DW_LNS_set_column); + out_uleb128 (column); + } + + if (e->loc.discriminator != 0) + { + out_opcode (DW_LNS_extended_op); + out_leb128 (1 + sizeof_leb128 (e->loc.discriminator, 0)); + out_opcode (DW_LNE_set_discriminator); + out_uleb128 (e->loc.discriminator); + } + + if (isa != e->loc.isa) + { + isa = e->loc.isa; + out_opcode (DW_LNS_set_isa); + out_uleb128 (isa); + } + + if ((e->loc.flags ^ flags) & DWARF2_FLAG_IS_STMT) + { + flags = e->loc.flags; + out_opcode (DW_LNS_negate_stmt); + } + + if (e->loc.flags & DWARF2_FLAG_BASIC_BLOCK) + out_opcode (DW_LNS_set_basic_block); + + if (e->loc.flags & DWARF2_FLAG_PROLOGUE_END) + out_opcode (DW_LNS_set_prologue_end); + + if (e->loc.flags & DWARF2_FLAG_EPILOGUE_BEGIN) + out_opcode (DW_LNS_set_epilogue_begin); + + /* Don't try to optimize away redundant entries; gdb wants two + entries for a function where the code starts on the same line as + the {, and there's no way to identify that case here. Trust gcc + to optimize appropriately. */ + line_delta = e->loc.line - line; + lab = e->label; + frag = symbol_get_frag (lab); + frag_ofs = S_GET_VALUE (lab); + + if (last_frag == NULL) + { + out_set_addr (lab); + out_inc_line_addr (line_delta, 0); + } + else if (frag == last_frag && ! DWARF2_USE_FIXED_ADVANCE_PC) + out_inc_line_addr (line_delta, frag_ofs - last_frag_ofs); + else + relax_inc_line_addr (line_delta, lab, last_lab); + + line = e->loc.line; + last_lab = lab; + last_frag = frag; + last_frag_ofs = frag_ofs; + + next = e->next; + free (e); + e = next; + } + while (e); + + /* Emit a DW_LNE_end_sequence for the end of the section. */ + frag = last_frag_for_seg (seg); + frag_ofs = get_frag_fix (frag, seg); + if (frag == last_frag && ! DWARF2_USE_FIXED_ADVANCE_PC) + out_inc_line_addr (INT_MAX, frag_ofs - last_frag_ofs); + else + { + lab = symbol_temp_new (seg, frag_ofs, frag); + relax_inc_line_addr (INT_MAX, lab, last_lab); + } +} + +/* Emit the directory and file tables for .debug_line. */ + +static void +out_file_list (void) +{ + size_t size; + const char *dir; + char *cp; + unsigned int i; + + /* Emit directory list. */ + for (i = 1; i < dirs_in_use; ++i) + { + dir = remap_debug_filename (dirs[i]); + size = strlen (dir) + 1; + cp = frag_more (size); + memcpy (cp, dir, size); + } + /* Terminate it. */ + out_byte ('\0'); + + for (i = 1; i < files_in_use; ++i) + { + const char *fullfilename; + + if (files[i].filename == NULL) + { + as_bad (_("unassigned file number %ld"), (long) i); + /* Prevent a crash later, particularly for file 1. */ + files[i].filename = ""; + continue; + } + + fullfilename = DWARF2_FILE_NAME (files[i].filename, + files[i].dir ? dirs [files [i].dir] : ""); + size = strlen (fullfilename) + 1; + cp = frag_more (size); + memcpy (cp, fullfilename, size); + + out_uleb128 (files[i].dir); /* directory number */ + /* Output the last modification timestamp. */ + out_uleb128 (DWARF2_FILE_TIME_NAME (files[i].filename, + files[i].dir ? dirs [files [i].dir] : "")); + /* Output the filesize. */ + out_uleb128 (DWARF2_FILE_SIZE_NAME (files[i].filename, + files[i].dir ? dirs [files [i].dir] : "")); + } + + /* Terminate filename list. */ + out_byte (0); +} + +/* Switch to SEC and output a header length field. Return the size of + offsets used in SEC. The caller must set EXPR->X_add_symbol value + to the end of the section. */ + +static int +out_header (asection *sec, expressionS *exp) +{ + symbolS *start_sym; + symbolS *end_sym; + + subseg_set (sec, 0); + start_sym = symbol_temp_new_now (); + end_sym = symbol_temp_make (); + + /* Total length of the information. */ + exp->X_op = O_subtract; + exp->X_add_symbol = end_sym; + exp->X_op_symbol = start_sym; + + switch (DWARF2_FORMAT (sec)) + { + case dwarf2_format_32bit: + exp->X_add_number = -4; + emit_expr (exp, 4); + return 4; + + case dwarf2_format_64bit: + exp->X_add_number = -12; + out_four (-1); + emit_expr (exp, 8); + return 8; + + case dwarf2_format_64bit_irix: + exp->X_add_number = -8; + emit_expr (exp, 8); + return 8; + } + + as_fatal (_("internal error: unknown dwarf2 format")); + return 0; +} + +/* Emit the collected .debug_line data. */ + +static void +out_debug_line (segT line_seg) +{ + expressionS exp; + symbolS *prologue_end; + symbolS *line_end; + struct line_seg *s; + int sizeof_offset; + + sizeof_offset = out_header (line_seg, &exp); + line_end = exp.X_add_symbol; + + /* Version. */ + out_two (DWARF2_LINE_VERSION); + + /* Length of the prologue following this length. */ + prologue_end = symbol_temp_make (); + exp.X_add_symbol = prologue_end; + exp.X_add_number = - (4 + 2 + 4); + emit_expr (&exp, sizeof_offset); + + /* Parameters of the state machine. */ + out_byte (DWARF2_LINE_MIN_INSN_LENGTH); + out_byte (DWARF2_LINE_DEFAULT_IS_STMT); + out_byte (DWARF2_LINE_BASE); + out_byte (DWARF2_LINE_RANGE); + out_byte (DWARF2_LINE_OPCODE_BASE); + + /* Standard opcode lengths. */ + out_byte (0); /* DW_LNS_copy */ + out_byte (1); /* DW_LNS_advance_pc */ + out_byte (1); /* DW_LNS_advance_line */ + out_byte (1); /* DW_LNS_set_file */ + out_byte (1); /* DW_LNS_set_column */ + out_byte (0); /* DW_LNS_negate_stmt */ + out_byte (0); /* DW_LNS_set_basic_block */ + out_byte (0); /* DW_LNS_const_add_pc */ + out_byte (1); /* DW_LNS_fixed_advance_pc */ + out_byte (0); /* DW_LNS_set_prologue_end */ + out_byte (0); /* DW_LNS_set_epilogue_begin */ + out_byte (1); /* DW_LNS_set_isa */ + + out_file_list (); + + symbol_set_value_now (prologue_end); + + /* For each section, emit a statement program. */ + for (s = all_segs; s; s = s->next) + if (SEG_NORMAL (s->seg)) + process_entries (s->seg, s->head->head); + else + as_warn ("dwarf line number information for %s ignored", + segment_name (s->seg)); + + if (flag_dwarf_sections) + /* We have to switch to the special .debug_line_end section + before emitting the end-of-debug_line symbol. The linker + script arranges for this section to be placed after all the + (potentially garbage collected) .debug_line. sections. + This section contains the line_end symbol which is used to + compute the size of the linked .debug_line section, as seen + in the DWARF Line Number header. */ + subseg_set (subseg_get (".debug_line_end", FALSE), 0); + + symbol_set_value_now (line_end); +} + +static void +out_debug_ranges (segT ranges_seg) +{ + unsigned int addr_size = sizeof_address; + struct line_seg *s; + expressionS exp; + unsigned int i; + + subseg_set (ranges_seg, 0); + + /* Base Address Entry. */ + for (i = 0; i < addr_size; i++) + out_byte (0xff); + for (i = 0; i < addr_size; i++) + out_byte (0); + + /* Range List Entry. */ + for (s = all_segs; s; s = s->next) + { + fragS *frag; + symbolS *beg, *end; + + frag = first_frag_for_seg (s->seg); + beg = symbol_temp_new (s->seg, 0, frag); + s->text_start = beg; + + frag = last_frag_for_seg (s->seg); + end = symbol_temp_new (s->seg, get_frag_fix (frag, s->seg), frag); + s->text_end = end; + + exp.X_op = O_symbol; + exp.X_add_symbol = beg; + exp.X_add_number = 0; + emit_expr (&exp, addr_size); + + exp.X_op = O_symbol; + exp.X_add_symbol = end; + exp.X_add_number = 0; + emit_expr (&exp, addr_size); + } + + /* End of Range Entry. */ + for (i = 0; i < addr_size; i++) + out_byte (0); + for (i = 0; i < addr_size; i++) + out_byte (0); +} + +/* Emit data for .debug_aranges. */ + +static void +out_debug_aranges (segT aranges_seg, segT info_seg) +{ + unsigned int addr_size = sizeof_address; + struct line_seg *s; + expressionS exp; + symbolS *aranges_end; + char *p; + int sizeof_offset; + + sizeof_offset = out_header (aranges_seg, &exp); + aranges_end = exp.X_add_symbol; + + /* Version. */ + out_two (DWARF2_ARANGES_VERSION); + + /* Offset to .debug_info. */ + TC_DWARF2_EMIT_OFFSET (section_symbol (info_seg), sizeof_offset); + + /* Size of an address (offset portion). */ + out_byte (addr_size); + + /* Size of a segment descriptor. */ + out_byte (0); + + /* Align the header. */ + frag_align (ffs (2 * addr_size) - 1, 0, 0); + + for (s = all_segs; s; s = s->next) + { + fragS *frag; + symbolS *beg, *end; + + frag = first_frag_for_seg (s->seg); + beg = symbol_temp_new (s->seg, 0, frag); + s->text_start = beg; + + frag = last_frag_for_seg (s->seg); + end = symbol_temp_new (s->seg, get_frag_fix (frag, s->seg), frag); + s->text_end = end; + + exp.X_op = O_symbol; + exp.X_add_symbol = beg; + exp.X_add_number = 0; + emit_expr (&exp, addr_size); + + exp.X_op = O_subtract; + exp.X_add_symbol = end; + exp.X_op_symbol = beg; + exp.X_add_number = 0; + emit_expr (&exp, addr_size); + } + + p = frag_more (2 * addr_size); + md_number_to_chars (p, 0, addr_size); + md_number_to_chars (p + addr_size, 0, addr_size); + + symbol_set_value_now (aranges_end); +} + +/* Emit data for .debug_abbrev. Note that this must be kept in + sync with out_debug_info below. */ + +static void +out_debug_abbrev (segT abbrev_seg, + segT info_seg ATTRIBUTE_UNUSED, + segT line_seg ATTRIBUTE_UNUSED) +{ + subseg_set (abbrev_seg, 0); + + out_uleb128 (1); + out_uleb128 (DW_TAG_compile_unit); + out_byte (DW_CHILDREN_no); + if (DWARF2_FORMAT (line_seg) == dwarf2_format_32bit) + out_abbrev (DW_AT_stmt_list, DW_FORM_data4); + else + out_abbrev (DW_AT_stmt_list, DW_FORM_data8); + if (all_segs->next == NULL) + { + out_abbrev (DW_AT_low_pc, DW_FORM_addr); + if (DWARF2_VERSION < 4) + out_abbrev (DW_AT_high_pc, DW_FORM_addr); + else + out_abbrev (DW_AT_high_pc, (sizeof_address == 4 + ? DW_FORM_data4 : DW_FORM_data8)); + } + else + { + if (DWARF2_FORMAT (info_seg) == dwarf2_format_32bit) + out_abbrev (DW_AT_ranges, DW_FORM_data4); + else + out_abbrev (DW_AT_ranges, DW_FORM_data8); + } + out_abbrev (DW_AT_name, DW_FORM_string); + out_abbrev (DW_AT_comp_dir, DW_FORM_string); + out_abbrev (DW_AT_producer, DW_FORM_string); + out_abbrev (DW_AT_language, DW_FORM_data2); + out_abbrev (0, 0); + + /* Terminate the abbreviations for this compilation unit. */ + out_byte (0); +} + +/* Emit a description of this compilation unit for .debug_info. */ + +static void +out_debug_info (segT info_seg, segT abbrev_seg, segT line_seg, segT ranges_seg) +{ + char producer[128]; + const char *comp_dir; + const char *dirname; + expressionS exp; + symbolS *info_end; + char *p; + int len; + int sizeof_offset; + + sizeof_offset = out_header (info_seg, &exp); + info_end = exp.X_add_symbol; + + /* DWARF version. */ + out_two (DWARF2_VERSION); + + /* .debug_abbrev offset */ + TC_DWARF2_EMIT_OFFSET (section_symbol (abbrev_seg), sizeof_offset); + + /* Target address size. */ + out_byte (sizeof_address); + + /* DW_TAG_compile_unit DIE abbrev */ + out_uleb128 (1); + + /* DW_AT_stmt_list */ + TC_DWARF2_EMIT_OFFSET (section_symbol (line_seg), + (DWARF2_FORMAT (line_seg) == dwarf2_format_32bit + ? 4 : 8)); + + /* These two attributes are emitted if all of the code is contiguous. */ + if (all_segs->next == NULL) + { + /* DW_AT_low_pc */ + exp.X_op = O_symbol; + exp.X_add_symbol = all_segs->text_start; + exp.X_add_number = 0; + emit_expr (&exp, sizeof_address); + + /* DW_AT_high_pc */ + if (DWARF2_VERSION < 4) + exp.X_op = O_symbol; + else + { + exp.X_op = O_subtract; + exp.X_op_symbol = all_segs->text_start; + } + exp.X_add_symbol = all_segs->text_end; + exp.X_add_number = 0; + emit_expr (&exp, sizeof_address); + } + else + { + /* This attribute is emitted if the code is disjoint. */ + /* DW_AT_ranges. */ + TC_DWARF2_EMIT_OFFSET (section_symbol (ranges_seg), sizeof_offset); + } + + /* DW_AT_name. We don't have the actual file name that was present + on the command line, so assume files[1] is the main input file. + We're not supposed to get called unless at least one line number + entry was emitted, so this should always be defined. */ + if (files_in_use == 0) + abort (); + if (files[1].dir) + { + dirname = remap_debug_filename (dirs[files[1].dir]); + len = strlen (dirname); +#ifdef TE_VMS + /* Already has trailing slash. */ + p = frag_more (len); + memcpy (p, dirname, len); +#else + p = frag_more (len + 1); + memcpy (p, dirname, len); + INSERT_DIR_SEPARATOR (p, len); +#endif + } + len = strlen (files[1].filename) + 1; + p = frag_more (len); + memcpy (p, files[1].filename, len); + + /* DW_AT_comp_dir */ + comp_dir = remap_debug_filename (getpwd ()); + len = strlen (comp_dir) + 1; + p = frag_more (len); + memcpy (p, comp_dir, len); + + /* DW_AT_producer */ + sprintf (producer, "GNU AS %s", VERSION); + len = strlen (producer) + 1; + p = frag_more (len); + memcpy (p, producer, len); + + /* DW_AT_language. Yes, this is probably not really MIPS, but the + dwarf2 draft has no standard code for assembler. */ + out_two (DW_LANG_Mips_Assembler); + + symbol_set_value_now (info_end); +} + +void +dwarf2_init (void) +{ + all_segs_hash = hash_new (); + last_seg_ptr = &all_segs; +} + + +/* Finish the dwarf2 debug sections. We emit .debug.line if there + were any .file/.loc directives, or --gdwarf2 was given, or if the + file has a non-empty .debug_info section and an empty .debug_line + section. If we emit .debug_line, and the .debug_info section is + empty, we also emit .debug_info, .debug_aranges and .debug_abbrev. + ALL_SEGS will be non-null if there were any .file/.loc directives, + or --gdwarf2 was given and there were any located instructions + emitted. */ + +void +dwarf2_finish (void) +{ + segT line_seg; + struct line_seg *s; + segT info_seg; + int emit_other_sections = 0; + int empty_debug_line = 0; + + info_seg = bfd_get_section_by_name (stdoutput, ".debug_info"); + emit_other_sections = info_seg == NULL || !seg_not_empty_p (info_seg); + + line_seg = bfd_get_section_by_name (stdoutput, ".debug_line"); + empty_debug_line = line_seg == NULL || !seg_not_empty_p (line_seg); + + /* We can't construct a new debug_line section if we already have one. + Give an error. */ + if (all_segs && !empty_debug_line) + as_fatal ("duplicate .debug_line sections"); + + if ((!all_segs && emit_other_sections) + || (!emit_other_sections && !empty_debug_line)) + /* If there is no line information and no non-empty .debug_info + section, or if there is both a non-empty .debug_info and a non-empty + .debug_line, then we do nothing. */ + return; + + /* Calculate the size of an address for the target machine. */ + sizeof_address = DWARF2_ADDR_SIZE (stdoutput); + + /* Create and switch to the line number section. */ + line_seg = subseg_new (".debug_line", 0); + bfd_set_section_flags (stdoutput, line_seg, SEC_READONLY | SEC_DEBUGGING); + + /* For each subsection, chain the debug entries together. */ + for (s = all_segs; s; s = s->next) + { + struct line_subseg *lss = s->head; + struct line_entry **ptail = lss->ptail; + + while ((lss = lss->next) != NULL) + { + *ptail = lss->head; + ptail = lss->ptail; + } + } + + out_debug_line (line_seg); + + /* If this is assembler generated line info, and there is no + debug_info already, we need .debug_info and .debug_abbrev + sections as well. */ + if (emit_other_sections) + { + segT abbrev_seg; + segT aranges_seg; + segT ranges_seg; + + gas_assert (all_segs); + + info_seg = subseg_new (".debug_info", 0); + abbrev_seg = subseg_new (".debug_abbrev", 0); + aranges_seg = subseg_new (".debug_aranges", 0); + + bfd_set_section_flags (stdoutput, info_seg, + SEC_READONLY | SEC_DEBUGGING); + bfd_set_section_flags (stdoutput, abbrev_seg, + SEC_READONLY | SEC_DEBUGGING); + bfd_set_section_flags (stdoutput, aranges_seg, + SEC_READONLY | SEC_DEBUGGING); + + record_alignment (aranges_seg, ffs (2 * sizeof_address) - 1); + + if (all_segs->next == NULL) + ranges_seg = NULL; + else + { + ranges_seg = subseg_new (".debug_ranges", 0); + bfd_set_section_flags (stdoutput, ranges_seg, + SEC_READONLY | SEC_DEBUGGING); + record_alignment (ranges_seg, ffs (2 * sizeof_address) - 1); + out_debug_ranges (ranges_seg); + } + + out_debug_aranges (aranges_seg, info_seg); + out_debug_abbrev (abbrev_seg, info_seg, line_seg); + out_debug_info (info_seg, abbrev_seg, line_seg, ranges_seg); + } +} diff --git a/contrib/toolchain/binutils/gas/dwarf2dbg.h b/contrib/toolchain/binutils/gas/dwarf2dbg.h new file mode 100644 index 0000000000..84ef8f6d52 --- /dev/null +++ b/contrib/toolchain/binutils/gas/dwarf2dbg.h @@ -0,0 +1,116 @@ +/* dwarf2dbg.h - DWARF2 debug support + Copyright 1999, 2000, 2002, 2003, 2005, 2006, 2007, 2009 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef AS_DWARF2DBG_H +#define AS_DWARF2DBG_H + +#include "as.h" + +#define DWARF2_FLAG_IS_STMT (1 << 0) +#define DWARF2_FLAG_BASIC_BLOCK (1 << 1) +#define DWARF2_FLAG_PROLOGUE_END (1 << 2) +#define DWARF2_FLAG_EPILOGUE_BEGIN (1 << 3) + +struct dwarf2_line_info { + unsigned int filenum; + unsigned int line; + unsigned int column; + unsigned int isa; + unsigned int flags; + unsigned int discriminator; +}; + +/* Implements the .file FILENO "FILENAME" directive. FILENO can be 0 + to indicate that no file number has been assigned. All real file + number must be >0. */ +extern char *dwarf2_directive_file (int dummy); + +/* Implements the .loc FILENO LINENO [COLUMN] directive. FILENO is + the file number, LINENO the line number and the (optional) COLUMN + the column of the source code that the following instruction + corresponds to. FILENO can be 0 to indicate that the filename + specified by the textually most recent .file directive should be + used. */ +extern void dwarf2_directive_loc (int dummy); + +/* Implements the .loc_mark_labels {0,1} directive. */ +extern void dwarf2_directive_loc_mark_labels (int dummy); + +/* Returns the current source information. If .file directives have + been encountered, the info for the corresponding source file is + returned. Otherwise, the info for the assembly source file is + returned. */ +extern void dwarf2_where (struct dwarf2_line_info *l); + +/* A hook to allow the target backend to inform the line number state + machine of isa changes when assembler debug info is enabled. */ +extern void dwarf2_set_isa (unsigned int isa); + +/* This function generates .debug_line info based on the address and + source information passed in the arguments. ADDR should be the + frag-relative offset of the instruction the information is for and + L is the source information that should be associated with that + address. */ +extern void dwarf2_gen_line_info (addressT addr, struct dwarf2_line_info *l); + +/* Must be called for each generated instruction. */ +extern void dwarf2_emit_insn (int); + +void dwarf2_move_insn (int); + +/* Reset the state of the line number information to reflect that + it has been used. */ +extern void dwarf2_consume_line_info (void); + +/* Should be called for each code label. */ +extern void dwarf2_emit_label (symbolS *); + +/* True when we've seen a .loc directive recently. Used to avoid + doing work when there's nothing to do. */ +extern bfd_boolean dwarf2_loc_directive_seen; + +/* True when we're supposed to set the basic block mark whenever a label + is seen. Unless the target is doing Something Weird, just call + dwarf2_emit_label. */ +extern bfd_boolean dwarf2_loc_mark_labels; + +extern void dwarf2_init (void); + +extern void dwarf2_finish (void); + +extern int dwarf2dbg_estimate_size_before_relax (fragS *); +extern int dwarf2dbg_relax_frag (fragS *); +extern void dwarf2dbg_convert_frag (fragS *); + +/* An enumeration which describes the sizes of offsets (to DWARF sections) + and the mechanism by which the size is indicated. */ +enum dwarf2_format { + /* 32-bit format: the initial length field is 4 bytes long. */ + dwarf2_format_32bit, + /* DWARF3 64-bit format: the representation of the initial length + (of a DWARF section) is 0xffffffff (4 bytes) followed by eight + bytes indicating the actual length. */ + dwarf2_format_64bit, + /* SGI extension to DWARF2: The initial length is eight bytes. */ + dwarf2_format_64bit_irix +}; + +#endif /* AS_DWARF2DBG_H */ diff --git a/contrib/toolchain/binutils/gas/ecoff.c b/contrib/toolchain/binutils/gas/ecoff.c new file mode 100644 index 0000000000..821b02ce7d --- /dev/null +++ b/contrib/toolchain/binutils/gas/ecoff.c @@ -0,0 +1,5244 @@ +/* ECOFF debugging support. + Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, + 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Free Software Foundation, Inc. + Contributed by Cygnus Support. + This file was put together by Ian Lance Taylor . A + good deal of it comes directly from mips-tfile.c, by Michael + Meissner . + + This file is part of GAS. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" + +/* This file is compiled conditionally for those targets which use + ECOFF debugging information (e.g., MIPS ELF, Alpha ECOFF). */ + +#include "ecoff.h" + +#ifdef ECOFF_DEBUGGING + +#include "coff/internal.h" +#include "coff/symconst.h" +#include "aout/stab_gnu.h" +#include "filenames.h" +#include "safe-ctype.h" + +/* Why isn't this in coff/sym.h? */ +#define ST_RFDESCAPE 0xfff + +/* This file constructs the information used by the ECOFF debugging + format. It just builds a large block of data. + + We support both ECOFF style debugging and stabs debugging (the + stabs symbols are encapsulated in ECOFF symbols). This should let + us handle anything the compiler might throw at us. */ + +/* Here is a brief description of the MIPS ECOFF symbol table, by + Michael Meissner. The MIPS symbol table has the following pieces: + + Symbolic Header + | + +-- Auxiliary Symbols + | + +-- Dense number table + | + +-- Optimizer Symbols + | + +-- External Strings + | + +-- External Symbols + | + +-- Relative file descriptors + | + +-- File table + | + +-- Procedure table + | + +-- Line number table + | + +-- Local Strings + | + +-- Local Symbols + + The symbolic header points to each of the other tables, and also + contains the number of entries. It also contains a magic number + and MIPS compiler version number, such as 2.0. + + The auxiliary table is a series of 32 bit integers, that are + referenced as needed from the local symbol table. Unlike standard + COFF, the aux. information does not follow the symbol that uses + it, but rather is a separate table. In theory, this would allow + the MIPS compilers to collapse duplicate aux. entries, but I've not + noticed this happening with the 1.31 compiler suite. The different + types of aux. entries are: + + 1) dnLow: Low bound on array dimension. + + 2) dnHigh: High bound on array dimension. + + 3) isym: Index to the local symbol which is the start of the + function for the end of function first aux. entry. + + 4) width: Width of structures and bitfields. + + 5) count: Count of ranges for variant part. + + 6) rndx: A relative index into the symbol table. The relative + index field has two parts: rfd which is a pointer into the + relative file index table or ST_RFDESCAPE which says the next + aux. entry is the file number, and index: which is the pointer + into the local symbol within a given file table. This is for + things like references to types defined in another file. + + 7) Type information: This is like the COFF type bits, except it + is 32 bits instead of 16; they still have room to add new + basic types; and they can handle more than 6 levels of array, + pointer, function, etc. Each type information field contains + the following structure members: + + a) fBitfield: a bit that says this is a bitfield, and the + size in bits follows as the next aux. entry. + + b) continued: a bit that says the next aux. entry is a + continuation of the current type information (in case + there are more than 6 levels of array/ptr/function). + + c) bt: an integer containing the base type before adding + array, pointer, function, etc. qualifiers. The + current base types that I have documentation for are: + + btNil -- undefined + btAdr -- address - integer same size as ptr + btChar -- character + btUChar -- unsigned character + btShort -- short + btUShort -- unsigned short + btInt -- int + btUInt -- unsigned int + btLong -- long + btULong -- unsigned long + btFloat -- float (real) + btDouble -- Double (real) + btStruct -- Structure (Record) + btUnion -- Union (variant) + btEnum -- Enumerated + btTypedef -- defined via a typedef isymRef + btRange -- subrange of int + btSet -- pascal sets + btComplex -- fortran complex + btDComplex -- fortran double complex + btIndirect -- forward or unnamed typedef + btFixedDec -- Fixed Decimal + btFloatDec -- Float Decimal + btString -- Varying Length Character String + btBit -- Aligned Bit String + btPicture -- Picture + btVoid -- Void (MIPS cc revision >= 2.00) + + d) tq0 - tq5: type qualifier fields as needed. The + current type qualifier fields I have documentation for + are: + + tqNil -- no more qualifiers + tqPtr -- pointer + tqProc -- procedure + tqArray -- array + tqFar -- 8086 far pointers + tqVol -- volatile + + The dense number table is used in the front ends, and disappears by + the time the .o is created. + + With the 1.31 compiler suite, the optimization symbols don't seem + to be used as far as I can tell. + + The linker is the first entity that creates the relative file + descriptor table, and I believe it is used so that the individual + file table pointers don't have to be rewritten when the objects are + merged together into the program file. + + Unlike COFF, the basic symbol & string tables are split into + external and local symbols/strings. The relocation information + only goes off of the external symbol table, and the debug + information only goes off of the internal symbol table. The + external symbols can have links to an appropriate file index and + symbol within the file to give it the appropriate type information. + Because of this, the external symbols are actually larger than the + internal symbols (to contain the link information), and contain the + local symbol structure as a member, though this member is not the + first member of the external symbol structure (!). I suspect this + split is to make strip easier to deal with. + + Each file table has offsets for where the line numbers, local + strings, local symbols, and procedure table starts from within the + global tables, and the indexs are reset to 0 for each of those + tables for the file. + + The procedure table contains the binary equivalents of the .ent + (start of the function address), .frame (what register is the + virtual frame pointer, constant offset from the register to obtain + the VFP, and what register holds the return address), .mask/.fmask + (bitmask of saved registers, and where the first register is stored + relative to the VFP) assembler directives. It also contains the + low and high bounds of the line numbers if debugging is turned on. + + The line number table is a compressed form of the normal COFF line + table. Each line number entry is either 1 or 3 bytes long, and + contains a signed delta from the previous line, and an unsigned + count of the number of instructions this statement takes. + + The local symbol table contains the following fields: + + 1) iss: index to the local string table giving the name of the + symbol. + + 2) value: value of the symbol (address, register number, etc.). + + 3) st: symbol type. The current symbol types are: + + stNil -- Nuthin' special + stGlobal -- external symbol + stStatic -- static + stParam -- procedure argument + stLocal -- local variable + stLabel -- label + stProc -- External Procedure + stBlock -- beginning of block + stEnd -- end (of anything) + stMember -- member (of anything) + stTypedef -- type definition + stFile -- file name + stRegReloc -- register relocation + stForward -- forwarding address + stStaticProc -- Static procedure + stConstant -- const + + 4) sc: storage class. The current storage classes are: + + scText -- text symbol + scData -- initialized data symbol + scBss -- un-initialized data symbol + scRegister -- value of symbol is register number + scAbs -- value of symbol is absolute + scUndefined -- who knows? + scCdbLocal -- variable's value is IN se->va.?? + scBits -- this is a bit field + scCdbSystem -- value is IN debugger's address space + scRegImage -- register value saved on stack + scInfo -- symbol contains debugger information + scUserStruct -- addr in struct user for current process + scSData -- load time only small data + scSBss -- load time only small common + scRData -- load time only read only data + scVar -- Var parameter (fortranpascal) + scCommon -- common variable + scSCommon -- small common + scVarRegister -- Var parameter in a register + scVariant -- Variant record + scSUndefined -- small undefined(external) data + scInit -- .init section symbol + + 5) index: pointer to a local symbol or aux. entry. + + For the following program: + + #include + + main(){ + printf("Hello World!\n"); + return 0; + } + + Mips-tdump produces the following information: + + Global file header: + magic number 0x162 + # sections 2 + timestamp 645311799, Wed Jun 13 17:16:39 1990 + symbolic header offset 284 + symbolic header size 96 + optional header 56 + flags 0x0 + + Symbolic header, magic number = 0x7009, vstamp = 1.31: + + Info Offset Number Bytes + ==== ====== ====== ===== + + Line numbers 380 4 4 [13] + Dense numbers 0 0 0 + Procedures Tables 384 1 52 + Local Symbols 436 16 192 + Optimization Symbols 0 0 0 + Auxiliary Symbols 628 39 156 + Local Strings 784 80 80 + External Strings 864 144 144 + File Tables 1008 2 144 + Relative Files 0 0 0 + External Symbols 1152 20 320 + + File #0, "hello2.c" + + Name index = 1 Readin = No + Merge = No Endian = LITTLE + Debug level = G2 Language = C + Adr = 0x00000000 + + Info Start Number Size Offset + ==== ===== ====== ==== ====== + Local strings 0 15 15 784 + Local symbols 0 6 72 436 + Line numbers 0 13 13 380 + Optimization symbols 0 0 0 0 + Procedures 0 1 52 384 + Auxiliary symbols 0 14 56 628 + Relative Files 0 0 0 0 + + There are 6 local symbols, starting at 436 + + Symbol# 0: "hello2.c" + End+1 symbol = 6 + String index = 1 + Storage class = Text Index = 6 + Symbol type = File Value = 0 + + Symbol# 1: "main" + End+1 symbol = 5 + Type = int + String index = 10 + Storage class = Text Index = 12 + Symbol type = Proc Value = 0 + + Symbol# 2: "" + End+1 symbol = 4 + String index = 0 + Storage class = Text Index = 4 + Symbol type = Block Value = 8 + + Symbol# 3: "" + First symbol = 2 + String index = 0 + Storage class = Text Index = 2 + Symbol type = End Value = 28 + + Symbol# 4: "main" + First symbol = 1 + String index = 10 + Storage class = Text Index = 1 + Symbol type = End Value = 52 + + Symbol# 5: "hello2.c" + First symbol = 0 + String index = 1 + Storage class = Text Index = 0 + Symbol type = End Value = 0 + + There are 14 auxiliary table entries, starting at 628. + + * #0 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + * #1 24, [ 24/ 0], [ 6 0:0 0:0:0:0:0:0] + * #2 8, [ 8/ 0], [ 2 0:0 0:0:0:0:0:0] + * #3 16, [ 16/ 0], [ 4 0:0 0:0:0:0:0:0] + * #4 24, [ 24/ 0], [ 6 0:0 0:0:0:0:0:0] + * #5 32, [ 32/ 0], [ 8 0:0 0:0:0:0:0:0] + * #6 40, [ 40/ 0], [10 0:0 0:0:0:0:0:0] + * #7 44, [ 44/ 0], [11 0:0 0:0:0:0:0:0] + * #8 12, [ 12/ 0], [ 3 0:0 0:0:0:0:0:0] + * #9 20, [ 20/ 0], [ 5 0:0 0:0:0:0:0:0] + * #10 28, [ 28/ 0], [ 7 0:0 0:0:0:0:0:0] + * #11 36, [ 36/ 0], [ 9 0:0 0:0:0:0:0:0] + #12 5, [ 5/ 0], [ 1 1:0 0:0:0:0:0:0] + #13 24, [ 24/ 0], [ 6 0:0 0:0:0:0:0:0] + + There are 1 procedure descriptor entries, starting at 0. + + Procedure descriptor 0: + Name index = 10 Name = "main" + .mask 0x80000000,-4 .fmask 0x00000000,0 + .frame $29,24,$31 + Opt. start = -1 Symbols start = 1 + First line # = 3 Last line # = 6 + Line Offset = 0 Address = 0x00000000 + + There are 4 bytes holding line numbers, starting at 380. + Line 3, delta 0, count 2 + Line 4, delta 1, count 3 + Line 5, delta 1, count 2 + Line 6, delta 1, count 6 + + File #1, "/usr/include/stdio.h" + + Name index = 1 Readin = No + Merge = Yes Endian = LITTLE + Debug level = G2 Language = C + Adr = 0x00000000 + + Info Start Number Size Offset + ==== ===== ====== ==== ====== + Local strings 15 65 65 799 + Local symbols 6 10 120 508 + Line numbers 0 0 0 380 + Optimization symbols 0 0 0 0 + Procedures 1 0 0 436 + Auxiliary symbols 14 25 100 684 + Relative Files 0 0 0 0 + + There are 10 local symbols, starting at 442 + + Symbol# 0: "/usr/include/stdio.h" + End+1 symbol = 10 + String index = 1 + Storage class = Text Index = 10 + Symbol type = File Value = 0 + + Symbol# 1: "_iobuf" + End+1 symbol = 9 + String index = 22 + Storage class = Info Index = 9 + Symbol type = Block Value = 20 + + Symbol# 2: "_cnt" + Type = int + String index = 29 + Storage class = Info Index = 4 + Symbol type = Member Value = 0 + + Symbol# 3: "_ptr" + Type = ptr to char + String index = 34 + Storage class = Info Index = 15 + Symbol type = Member Value = 32 + + Symbol# 4: "_base" + Type = ptr to char + String index = 39 + Storage class = Info Index = 16 + Symbol type = Member Value = 64 + + Symbol# 5: "_bufsiz" + Type = int + String index = 45 + Storage class = Info Index = 4 + Symbol type = Member Value = 96 + + Symbol# 6: "_flag" + Type = short + String index = 53 + Storage class = Info Index = 3 + Symbol type = Member Value = 128 + + Symbol# 7: "_file" + Type = char + String index = 59 + Storage class = Info Index = 2 + Symbol type = Member Value = 144 + + Symbol# 8: "" + First symbol = 1 + String index = 0 + Storage class = Info Index = 1 + Symbol type = End Value = 0 + + Symbol# 9: "/usr/include/stdio.h" + First symbol = 0 + String index = 1 + Storage class = Text Index = 0 + Symbol type = End Value = 0 + + There are 25 auxiliary table entries, starting at 642. + + * #14 -1, [4095/1048575], [63 1:1 f:f:f:f:f:f] + #15 65544, [ 8/ 16], [ 2 0:0 1:0:0:0:0:0] + #16 65544, [ 8/ 16], [ 2 0:0 1:0:0:0:0:0] + * #17 196656, [ 48/ 48], [12 0:0 3:0:0:0:0:0] + * #18 8191, [4095/ 1], [63 1:1 0:0:0:0:f:1] + * #19 1, [ 1/ 0], [ 0 1:0 0:0:0:0:0:0] + * #20 20479, [4095/ 4], [63 1:1 0:0:0:0:f:4] + * #21 1, [ 1/ 0], [ 0 1:0 0:0:0:0:0:0] + * #22 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + * #23 2, [ 2/ 0], [ 0 0:1 0:0:0:0:0:0] + * #24 160, [ 160/ 0], [40 0:0 0:0:0:0:0:0] + * #25 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + * #26 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + * #27 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + * #28 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + * #29 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + * #30 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + * #31 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + * #32 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + * #33 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + * #34 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + * #35 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + * #36 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + * #37 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + * #38 0, [ 0/ 0], [ 0 0:0 0:0:0:0:0:0] + + There are 0 procedure descriptor entries, starting at 1. + + There are 20 external symbols, starting at 1152 + + Symbol# 0: "_iob" + Type = array [3 {160}] of struct _iobuf { ifd = 1, index = 1 } + String index = 0 Ifd = 1 + Storage class = Nil Index = 17 + Symbol type = Global Value = 60 + + Symbol# 1: "fopen" + String index = 5 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 2: "fdopen" + String index = 11 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 3: "freopen" + String index = 18 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 4: "popen" + String index = 26 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 5: "tmpfile" + String index = 32 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 6: "ftell" + String index = 40 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 7: "rewind" + String index = 46 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 8: "setbuf" + String index = 53 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 9: "setbuffer" + String index = 60 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 10: "setlinebuf" + String index = 70 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 11: "fgets" + String index = 81 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 12: "gets" + String index = 87 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 13: "ctermid" + String index = 92 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 14: "cuserid" + String index = 100 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 15: "tempnam" + String index = 108 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 16: "tmpnam" + String index = 116 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 17: "sprintf" + String index = 123 Ifd = 1 + Storage class = Nil Index = 1048575 + Symbol type = Proc Value = 0 + + Symbol# 18: "main" + Type = int + String index = 131 Ifd = 0 + Storage class = Text Index = 1 + Symbol type = Proc Value = 0 + + Symbol# 19: "printf" + String index = 136 Ifd = 0 + Storage class = Undefined Index = 1048575 + Symbol type = Proc Value = 0 + + The following auxiliary table entries were unused: + + #0 0 0x00000000 void + #2 8 0x00000008 char + #3 16 0x00000010 short + #4 24 0x00000018 int + #5 32 0x00000020 long + #6 40 0x00000028 float + #7 44 0x0000002c double + #8 12 0x0000000c unsigned char + #9 20 0x00000014 unsigned short + #10 28 0x0000001c unsigned int + #11 36 0x00000024 unsigned long + #14 0 0x00000000 void + #15 24 0x00000018 int + #19 32 0x00000020 long + #20 40 0x00000028 float + #21 44 0x0000002c double + #22 12 0x0000000c unsigned char + #23 20 0x00000014 unsigned short + #24 28 0x0000001c unsigned int + #25 36 0x00000024 unsigned long + #26 48 0x00000030 struct no name { ifd = -1, index = 1048575 } +*/ + +/* Redefinition of of storage classes as an enumeration for better + debugging. */ + +typedef enum sc { + sc_Nil = scNil, /* no storage class */ + sc_Text = scText, /* text symbol */ + sc_Data = scData, /* initialized data symbol */ + sc_Bss = scBss, /* un-initialized data symbol */ + sc_Register = scRegister, /* value of symbol is register number */ + sc_Abs = scAbs, /* value of symbol is absolute */ + sc_Undefined = scUndefined, /* who knows? */ + sc_CdbLocal = scCdbLocal, /* variable's value is IN se->va.?? */ + sc_Bits = scBits, /* this is a bit field */ + sc_CdbSystem = scCdbSystem, /* value is IN CDB's address space */ + sc_RegImage = scRegImage, /* register value saved on stack */ + sc_Info = scInfo, /* symbol contains debugger information */ + sc_UserStruct = scUserStruct, /* addr in struct user for current process */ + sc_SData = scSData, /* load time only small data */ + sc_SBss = scSBss, /* load time only small common */ + sc_RData = scRData, /* load time only read only data */ + sc_Var = scVar, /* Var parameter (fortran,pascal) */ + sc_Common = scCommon, /* common variable */ + sc_SCommon = scSCommon, /* small common */ + sc_VarRegister = scVarRegister, /* Var parameter in a register */ + sc_Variant = scVariant, /* Variant record */ + sc_SUndefined = scSUndefined, /* small undefined(external) data */ + sc_Init = scInit, /* .init section symbol */ + sc_Max = scMax /* Max storage class+1 */ +} sc_t; + +/* Redefinition of symbol type. */ + +typedef enum st { + st_Nil = stNil, /* Nuthin' special */ + st_Global = stGlobal, /* external symbol */ + st_Static = stStatic, /* static */ + st_Param = stParam, /* procedure argument */ + st_Local = stLocal, /* local variable */ + st_Label = stLabel, /* label */ + st_Proc = stProc, /* " " Procedure */ + st_Block = stBlock, /* beginning of block */ + st_End = stEnd, /* end (of anything) */ + st_Member = stMember, /* member (of anything - struct/union/enum */ + st_Typedef = stTypedef, /* type definition */ + st_File = stFile, /* file name */ + st_RegReloc = stRegReloc, /* register relocation */ + st_Forward = stForward, /* forwarding address */ + st_StaticProc = stStaticProc, /* load time only static procs */ + st_Constant = stConstant, /* const */ + st_Str = stStr, /* string */ + st_Number = stNumber, /* pure number (ie. 4 NOR 2+2) */ + st_Expr = stExpr, /* 2+2 vs. 4 */ + st_Type = stType, /* post-coercion SER */ + st_Max = stMax /* max type+1 */ +} st_t; + +/* Redefinition of type qualifiers. */ + +typedef enum tq { + tq_Nil = tqNil, /* bt is what you see */ + tq_Ptr = tqPtr, /* pointer */ + tq_Proc = tqProc, /* procedure */ + tq_Array = tqArray, /* duh */ + tq_Far = tqFar, /* longer addressing - 8086/8 land */ + tq_Vol = tqVol, /* volatile */ + tq_Max = tqMax /* Max type qualifier+1 */ +} tq_t; + +/* Redefinition of basic types. */ + +typedef enum bt { + bt_Nil = btNil, /* undefined */ + bt_Adr = btAdr, /* address - integer same size as pointer */ + bt_Char = btChar, /* character */ + bt_UChar = btUChar, /* unsigned character */ + bt_Short = btShort, /* short */ + bt_UShort = btUShort, /* unsigned short */ + bt_Int = btInt, /* int */ + bt_UInt = btUInt, /* unsigned int */ + bt_Long = btLong, /* long */ + bt_ULong = btULong, /* unsigned long */ + bt_Float = btFloat, /* float (real) */ + bt_Double = btDouble, /* Double (real) */ + bt_Struct = btStruct, /* Structure (Record) */ + bt_Union = btUnion, /* Union (variant) */ + bt_Enum = btEnum, /* Enumerated */ + bt_Typedef = btTypedef, /* defined via a typedef, isymRef points */ + bt_Range = btRange, /* subrange of int */ + bt_Set = btSet, /* pascal sets */ + bt_Complex = btComplex, /* fortran complex */ + bt_DComplex = btDComplex, /* fortran double complex */ + bt_Indirect = btIndirect, /* forward or unnamed typedef */ + bt_FixedDec = btFixedDec, /* Fixed Decimal */ + bt_FloatDec = btFloatDec, /* Float Decimal */ + bt_String = btString, /* Varying Length Character String */ + bt_Bit = btBit, /* Aligned Bit String */ + bt_Picture = btPicture, /* Picture */ + bt_Void = btVoid, /* Void */ + bt_Max = btMax /* Max basic type+1 */ +} bt_t; + +#define N_TQ itqMax + +/* States for whether to hash type or not. */ +typedef enum hash_state { + hash_no = 0, /* Don't hash type */ + hash_yes = 1, /* OK to hash type, or use previous hash */ + hash_record = 2 /* OK to record hash, but don't use prev. */ +} hash_state_t; + +/* Types of different sized allocation requests. */ +enum alloc_type { + alloc_type_none, /* dummy value */ + alloc_type_scope, /* nested scopes linked list */ + alloc_type_vlinks, /* glue linking pages in varray */ + alloc_type_shash, /* string hash element */ + alloc_type_thash, /* type hash element */ + alloc_type_tag, /* struct/union/tag element */ + alloc_type_forward, /* element to hold unknown tag */ + alloc_type_thead, /* head of type hash list */ + alloc_type_varray, /* general varray allocation */ + alloc_type_lineno, /* line number list */ + alloc_type_last /* last+1 element for array bounds */ +}; + +/* Types of auxiliary type information. */ +enum aux_type { + aux_tir, /* TIR type information */ + aux_rndx, /* relative index into symbol table */ + aux_dnLow, /* low dimension */ + aux_dnHigh, /* high dimension */ + aux_isym, /* symbol table index (end of proc) */ + aux_iss, /* index into string space (not used) */ + aux_width, /* width for non-default sized struc fields */ + aux_count /* count of ranges for variant arm */ +}; + +/* Structures to provide n-number of virtual arrays, each of which can + grow linearly, and which are written in the object file as + sequential pages. On systems with a BSD malloc, the + MAX_CLUSTER_PAGES should be 1 less than a power of two, since + malloc adds it's overhead, and rounds up to the next power of 2. + Pages are linked together via a linked list. + + If PAGE_SIZE is > 4096, the string length in the shash_t structure + can't be represented (assuming there are strings > 4096 bytes). */ + +/* FIXME: Yes, there can be such strings while emitting C++ class debug + info. Templates are the offender here, the test case in question + having a mangled class name of + + t7rb_tree4Z4xkeyZt4pair2ZC4xkeyZt7xsocket1Z4UserZt9select1st2Zt4pair\ + 2ZC4xkeyZt7xsocket1Z4UserZ4xkeyZt4less1Z4xkey + + Repeat that a couple dozen times while listing the class members and + you've got strings over 4k. Hack around this for now by increasing + the page size. A proper solution would abandon this structure scheme + certainly for very large strings, and possibly entirely. */ + +#ifndef PAGE_SIZE +#define PAGE_SIZE (8*1024) /* size of varray pages */ +#endif + +#define PAGE_USIZE ((unsigned long) PAGE_SIZE) + +#ifndef MAX_CLUSTER_PAGES /* # pages to get from system */ +#define MAX_CLUSTER_PAGES 63 +#endif + +/* Linked list connecting separate page allocations. */ +typedef struct vlinks { + struct vlinks *prev; /* previous set of pages */ + struct vlinks *next; /* next set of pages */ + union page *datum; /* start of page */ + unsigned long start_index; /* starting index # of page */ +} vlinks_t; + +/* Virtual array header. */ +typedef struct varray { + vlinks_t *first; /* first page link */ + vlinks_t *last; /* last page link */ + unsigned long num_allocated; /* # objects allocated */ + unsigned short object_size; /* size in bytes of each object */ + unsigned short objects_per_page; /* # objects that can fit on a page */ + unsigned short objects_last_page; /* # objects allocated on last page */ +} varray_t; + +#ifndef MALLOC_CHECK +#define OBJECTS_PER_PAGE(type) (PAGE_SIZE / sizeof (type)) +#else +#define OBJECTS_PER_PAGE(type) ((sizeof (type) > 1) ? 1 : PAGE_SIZE) +#endif + +#define INIT_VARRAY(type) { /* macro to initialize a varray */ \ + (vlinks_t *)0, /* first */ \ + (vlinks_t *)0, /* last */ \ + 0, /* num_allocated */ \ + sizeof (type), /* object_size */ \ + OBJECTS_PER_PAGE (type), /* objects_per_page */ \ + OBJECTS_PER_PAGE (type), /* objects_last_page */ \ +} + +/* Master type for indexes within the symbol table. */ +typedef unsigned long symint_t; + +/* Linked list support for nested scopes (file, block, structure, etc.). */ +typedef struct scope { + struct scope *prev; /* previous scope level */ + struct scope *free; /* free list pointer */ + struct localsym *lsym; /* pointer to local symbol node */ + st_t type; /* type of the node */ +} scope_t; + +/* For a local symbol we store a gas symbol as well as the debugging + information we generate. The gas symbol will be NULL if this is + only a debugging symbol. */ +typedef struct localsym { + const char *name; /* symbol name */ + symbolS *as_sym; /* symbol as seen by gas */ + bfd_vma addend; /* addend to as_sym value */ + struct efdr *file_ptr; /* file pointer */ + struct ecoff_proc *proc_ptr; /* proc pointer */ + struct localsym *begin_ptr; /* symbol at start of block */ + struct ecoff_aux *index_ptr; /* index value to be filled in */ + struct forward *forward_ref; /* forward references to this symbol */ + long sym_index; /* final symbol index */ + EXTR ecoff_sym; /* ECOFF debugging symbol */ +} localsym_t; + +/* For aux information we keep the type and the data. */ +typedef struct ecoff_aux { + enum aux_type type; /* aux type */ + AUXU data; /* aux data */ +} aux_t; + +/* For a procedure we store the gas symbol as well as the PDR + debugging information. */ +typedef struct ecoff_proc { + localsym_t *sym; /* associated symbol */ + PDR pdr; /* ECOFF debugging info */ +} proc_t; + +/* Number of proc_t structures allocated. */ +static unsigned long proc_cnt; + +/* Forward reference list for tags referenced, but not yet defined. */ +typedef struct forward { + struct forward *next; /* next forward reference */ + struct forward *free; /* free list pointer */ + aux_t *ifd_ptr; /* pointer to store file index */ + aux_t *index_ptr; /* pointer to store symbol index */ +} forward_t; + +/* Linked list support for tags. The first tag in the list is always + the current tag for that block. */ +typedef struct tag { + struct tag *free; /* free list pointer */ + struct shash *hash_ptr; /* pointer to the hash table head */ + struct tag *same_name; /* tag with same name in outer scope */ + struct tag *same_block; /* next tag defined in the same block. */ + struct forward *forward_ref; /* list of forward references */ + bt_t basic_type; /* bt_Struct, bt_Union, or bt_Enum */ + symint_t ifd; /* file # tag defined in */ + localsym_t *sym; /* file's local symbols */ +} tag_t; + +/* Head of a block's linked list of tags. */ +typedef struct thead { + struct thead *prev; /* previous block */ + struct thead *free; /* free list pointer */ + struct tag *first_tag; /* first tag in block defined */ +} thead_t; + +/* Union containing pointers to each the small structures which are freed up. */ +typedef union small_free { + scope_t *f_scope; /* scope structure */ + thead_t *f_thead; /* tag head structure */ + tag_t *f_tag; /* tag element structure */ + forward_t *f_forward; /* forward tag reference */ +} small_free_t; + +/* String hash table entry. */ + +typedef struct shash { + char *string; /* string we are hashing */ + symint_t indx; /* index within string table */ + EXTR *esym_ptr; /* global symbol pointer */ + localsym_t *sym_ptr; /* local symbol pointer */ + localsym_t *end_ptr; /* symbol pointer to end block */ + tag_t *tag_ptr; /* tag pointer */ + proc_t *proc_ptr; /* procedure descriptor pointer */ +} shash_t; + +/* Type hash table support. The size of the hash table must fit + within a page with the other extended file descriptor information. + Because unique types which are hashed are fewer in number than + strings, we use a smaller hash value. */ + +#define HASHBITS 30 + +#ifndef THASH_SIZE +#define THASH_SIZE 113 +#endif + +typedef struct thash { + struct thash *next; /* next hash value */ + AUXU type; /* type we are hashing */ + symint_t indx; /* index within string table */ +} thash_t; + +/* Extended file descriptor that contains all of the support necessary + to add things to each file separately. */ +typedef struct efdr { + FDR fdr; /* File header to be written out */ + FDR *orig_fdr; /* original file header */ + char *name; /* filename */ + int fake; /* whether this is faked .file */ + symint_t void_type; /* aux. pointer to 'void' type */ + symint_t int_type; /* aux. pointer to 'int' type */ + scope_t *cur_scope; /* current nested scopes */ + symint_t file_index; /* current file number */ + int nested_scopes; /* # nested scopes */ + varray_t strings; /* local strings */ + varray_t symbols; /* local symbols */ + varray_t procs; /* procedures */ + varray_t aux_syms; /* auxiliary symbols */ + struct efdr *next_file; /* next file descriptor */ + /* string/type hash tables */ + struct hash_control *str_hash; /* string hash table */ + thash_t *thash_head[THASH_SIZE]; +} efdr_t; + +/* Pre-initialized extended file structure. */ +static const efdr_t init_file = { + { /* FDR structure */ + 0, /* adr: memory address of beginning of file */ + 0, /* rss: file name (of source, if known) */ + 0, /* issBase: file's string space */ + 0, /* cbSs: number of bytes in the ss */ + 0, /* isymBase: beginning of symbols */ + 0, /* csym: count file's of symbols */ + 0, /* ilineBase: file's line symbols */ + 0, /* cline: count of file's line symbols */ + 0, /* ioptBase: file's optimization entries */ + 0, /* copt: count of file's optimization entries */ + 0, /* ipdFirst: start of procedures for this file */ + 0, /* cpd: count of procedures for this file */ + 0, /* iauxBase: file's auxiliary entries */ + 0, /* caux: count of file's auxiliary entries */ + 0, /* rfdBase: index into the file indirect table */ + 0, /* crfd: count file indirect entries */ + langC, /* lang: language for this file */ + 1, /* fMerge: whether this file can be merged */ + 0, /* fReadin: true if read in (not just created) */ + TARGET_BYTES_BIG_ENDIAN, /* fBigendian: if 1, compiled on big endian machine */ + GLEVEL_2, /* glevel: level this file was compiled with */ + 0, /* reserved: reserved for future use */ + 0, /* cbLineOffset: byte offset from header for this file ln's */ + 0, /* cbLine: size of lines for this file */ + }, + + (FDR *)0, /* orig_fdr: original file header pointer */ + (char *)0, /* name: pointer to filename */ + 0, /* fake: whether this is a faked .file */ + 0, /* void_type: ptr to aux node for void type */ + 0, /* int_type: ptr to aux node for int type */ + (scope_t *)0, /* cur_scope: current scope being processed */ + 0, /* file_index: current file # */ + 0, /* nested_scopes: # nested scopes */ + INIT_VARRAY (char), /* strings: local string varray */ + INIT_VARRAY (localsym_t), /* symbols: local symbols varray */ + INIT_VARRAY (proc_t), /* procs: procedure varray */ + INIT_VARRAY (aux_t), /* aux_syms: auxiliary symbols varray */ + + (struct efdr *)0, /* next_file: next file structure */ + + (struct hash_control *)0, /* str_hash: string hash table */ + { 0 }, /* thash_head: type hash table */ +}; + +static efdr_t *first_file; /* first file descriptor */ +static efdr_t **last_file_ptr = &first_file; /* file descriptor tail */ + +/* Line number information is kept in a list until the assembly is + finished. */ +typedef struct lineno_list { + struct lineno_list *next; /* next element in list */ + efdr_t *file; /* file this line is in */ + proc_t *proc; /* procedure this line is in */ + fragS *frag; /* fragment this line number is in */ + unsigned long paddr; /* offset within fragment */ + long lineno; /* actual line number */ +} lineno_list_t; + +static lineno_list_t *first_lineno; +static lineno_list_t *last_lineno; +static lineno_list_t **last_lineno_ptr = &first_lineno; + +/* Sometimes there will be some .loc statements before a .ent. We + keep them in this list so that we can fill in the procedure pointer + after we see the .ent. */ +static lineno_list_t *noproc_lineno; + +/* Union of various things that are held in pages. */ +typedef union page { + char byte [ PAGE_SIZE ]; + unsigned char ubyte [ PAGE_SIZE ]; + efdr_t file [ PAGE_SIZE / sizeof (efdr_t) ]; + FDR ofile [ PAGE_SIZE / sizeof (FDR) ]; + proc_t proc [ PAGE_SIZE / sizeof (proc_t) ]; + localsym_t sym [ PAGE_SIZE / sizeof (localsym_t) ]; + aux_t aux [ PAGE_SIZE / sizeof (aux_t) ]; + DNR dense [ PAGE_SIZE / sizeof (DNR) ]; + scope_t scope [ PAGE_SIZE / sizeof (scope_t) ]; + vlinks_t vlinks [ PAGE_SIZE / sizeof (vlinks_t) ]; + shash_t shash [ PAGE_SIZE / sizeof (shash_t) ]; + thash_t thash [ PAGE_SIZE / sizeof (thash_t) ]; + tag_t tag [ PAGE_SIZE / sizeof (tag_t) ]; + forward_t forward [ PAGE_SIZE / sizeof (forward_t) ]; + thead_t thead [ PAGE_SIZE / sizeof (thead_t) ]; + lineno_list_t lineno [ PAGE_SIZE / sizeof (lineno_list_t) ]; +} page_type; + +/* Structure holding allocation information for small sized structures. */ +typedef struct alloc_info { + char *alloc_name; /* name of this allocation type (must be first) */ + page_type *cur_page; /* current page being allocated from */ + small_free_t free_list; /* current free list if any */ + int unallocated; /* number of elements unallocated on page */ + int total_alloc; /* total number of allocations */ + int total_free; /* total number of frees */ + int total_pages; /* total number of pages allocated */ +} alloc_info_t; + +/* Type information collected together. */ +typedef struct type_info { + bt_t basic_type; /* basic type */ + int orig_type; /* original COFF-based type */ + int num_tq; /* # type qualifiers */ + int num_dims; /* # dimensions */ + int num_sizes; /* # sizes */ + int extra_sizes; /* # extra sizes not tied with dims */ + tag_t * tag_ptr; /* tag pointer */ + int bitfield; /* symbol is a bitfield */ + tq_t type_qualifiers[N_TQ]; /* type qualifiers (ptr, func, array)*/ + symint_t dimensions [N_TQ]; /* dimensions for each array */ + symint_t sizes [N_TQ+2]; /* sizes of each array slice + size of + struct/union/enum + bitfield size */ +} type_info_t; + +/* Pre-initialized type_info struct. */ +static const type_info_t type_info_init = { + bt_Nil, /* basic type */ + T_NULL, /* original COFF-based type */ + 0, /* # type qualifiers */ + 0, /* # dimensions */ + 0, /* # sizes */ + 0, /* sizes not tied with dims */ + NULL, /* ptr to tag */ + 0, /* bitfield */ + { /* type qualifiers */ + tq_Nil, + tq_Nil, + tq_Nil, + tq_Nil, + tq_Nil, + tq_Nil, + }, + { /* dimensions */ + 0, + 0, + 0, + 0, + 0, + 0 + }, + { /* sizes */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }, +}; + +/* Global hash table for the tags table and global table for file + descriptors. */ + +static varray_t file_desc = INIT_VARRAY (efdr_t); + +static struct hash_control *tag_hash; + +/* Static types for int and void. Also, remember the last function's + type (which is set up when we encounter the declaration for the + function, and used when the end block for the function is emitted. */ + +static type_info_t int_type_info; +static type_info_t void_type_info; +static type_info_t last_func_type_info; +static symbolS *last_func_sym_value; + +/* Convert COFF basic type to ECOFF basic type. The T_NULL type + really should use bt_Void, but this causes the current ecoff GDB to + issue unsupported type messages, and the Ultrix 4.00 dbx (aka MIPS + 2.0) doesn't understand it, even though the compiler generates it. + Maybe this will be fixed in 2.10 or 2.20 of the MIPS compiler + suite, but for now go with what works. + + It would make sense for the .type and .scl directives to use the + ECOFF numbers directly, rather than using the COFF numbers and + mapping them. Unfortunately, this is historically what mips-tfile + expects, and changing gcc now would be a considerable pain (the + native compiler generates debugging information internally, rather + than via the assembler, so it will never use .type or .scl). */ + +static const bt_t map_coff_types[] = { + bt_Nil, /* T_NULL */ + bt_Nil, /* T_ARG */ + bt_Char, /* T_CHAR */ + bt_Short, /* T_SHORT */ + bt_Int, /* T_INT */ + bt_Long, /* T_LONG */ + bt_Float, /* T_FLOAT */ + bt_Double, /* T_DOUBLE */ + bt_Struct, /* T_STRUCT */ + bt_Union, /* T_UNION */ + bt_Enum, /* T_ENUM */ + bt_Enum, /* T_MOE */ + bt_UChar, /* T_UCHAR */ + bt_UShort, /* T_USHORT */ + bt_UInt, /* T_UINT */ + bt_ULong /* T_ULONG */ +}; + +/* Convert COFF storage class to ECOFF storage class. */ +static const sc_t map_coff_storage[] = { + sc_Nil, /* 0: C_NULL */ + sc_Abs, /* 1: C_AUTO auto var */ + sc_Undefined, /* 2: C_EXT external */ + sc_Data, /* 3: C_STAT static */ + sc_Register, /* 4: C_REG register */ + sc_Undefined, /* 5: C_EXTDEF ??? */ + sc_Text, /* 6: C_LABEL label */ + sc_Text, /* 7: C_ULABEL user label */ + sc_Info, /* 8: C_MOS member of struct */ + sc_Abs, /* 9: C_ARG argument */ + sc_Info, /* 10: C_STRTAG struct tag */ + sc_Info, /* 11: C_MOU member of union */ + sc_Info, /* 12: C_UNTAG union tag */ + sc_Info, /* 13: C_TPDEF typedef */ + sc_Data, /* 14: C_USTATIC ??? */ + sc_Info, /* 15: C_ENTAG enum tag */ + sc_Info, /* 16: C_MOE member of enum */ + sc_Register, /* 17: C_REGPARM register parameter */ + sc_Bits, /* 18; C_FIELD bitfield */ + sc_Nil, /* 19 */ + sc_Nil, /* 20 */ + sc_Nil, /* 21 */ + sc_Nil, /* 22 */ + sc_Nil, /* 23 */ + sc_Nil, /* 24 */ + sc_Nil, /* 25 */ + sc_Nil, /* 26 */ + sc_Nil, /* 27 */ + sc_Nil, /* 28 */ + sc_Nil, /* 29 */ + sc_Nil, /* 30 */ + sc_Nil, /* 31 */ + sc_Nil, /* 32 */ + sc_Nil, /* 33 */ + sc_Nil, /* 34 */ + sc_Nil, /* 35 */ + sc_Nil, /* 36 */ + sc_Nil, /* 37 */ + sc_Nil, /* 38 */ + sc_Nil, /* 39 */ + sc_Nil, /* 40 */ + sc_Nil, /* 41 */ + sc_Nil, /* 42 */ + sc_Nil, /* 43 */ + sc_Nil, /* 44 */ + sc_Nil, /* 45 */ + sc_Nil, /* 46 */ + sc_Nil, /* 47 */ + sc_Nil, /* 48 */ + sc_Nil, /* 49 */ + sc_Nil, /* 50 */ + sc_Nil, /* 51 */ + sc_Nil, /* 52 */ + sc_Nil, /* 53 */ + sc_Nil, /* 54 */ + sc_Nil, /* 55 */ + sc_Nil, /* 56 */ + sc_Nil, /* 57 */ + sc_Nil, /* 58 */ + sc_Nil, /* 59 */ + sc_Nil, /* 60 */ + sc_Nil, /* 61 */ + sc_Nil, /* 62 */ + sc_Nil, /* 63 */ + sc_Nil, /* 64 */ + sc_Nil, /* 65 */ + sc_Nil, /* 66 */ + sc_Nil, /* 67 */ + sc_Nil, /* 68 */ + sc_Nil, /* 69 */ + sc_Nil, /* 70 */ + sc_Nil, /* 71 */ + sc_Nil, /* 72 */ + sc_Nil, /* 73 */ + sc_Nil, /* 74 */ + sc_Nil, /* 75 */ + sc_Nil, /* 76 */ + sc_Nil, /* 77 */ + sc_Nil, /* 78 */ + sc_Nil, /* 79 */ + sc_Nil, /* 80 */ + sc_Nil, /* 81 */ + sc_Nil, /* 82 */ + sc_Nil, /* 83 */ + sc_Nil, /* 84 */ + sc_Nil, /* 85 */ + sc_Nil, /* 86 */ + sc_Nil, /* 87 */ + sc_Nil, /* 88 */ + sc_Nil, /* 89 */ + sc_Nil, /* 90 */ + sc_Nil, /* 91 */ + sc_Nil, /* 92 */ + sc_Nil, /* 93 */ + sc_Nil, /* 94 */ + sc_Nil, /* 95 */ + sc_Nil, /* 96 */ + sc_Nil, /* 97 */ + sc_Nil, /* 98 */ + sc_Nil, /* 99 */ + sc_Text, /* 100: C_BLOCK block start/end */ + sc_Text, /* 101: C_FCN function start/end */ + sc_Info, /* 102: C_EOS end of struct/union/enum */ + sc_Nil, /* 103: C_FILE file start */ + sc_Nil, /* 104: C_LINE line number */ + sc_Nil, /* 105: C_ALIAS combined type info */ + sc_Nil, /* 106: C_HIDDEN ??? */ +}; + +/* Convert COFF storage class to ECOFF symbol type. */ +static const st_t map_coff_sym_type[] = { + st_Nil, /* 0: C_NULL */ + st_Local, /* 1: C_AUTO auto var */ + st_Global, /* 2: C_EXT external */ + st_Static, /* 3: C_STAT static */ + st_Local, /* 4: C_REG register */ + st_Global, /* 5: C_EXTDEF ??? */ + st_Label, /* 6: C_LABEL label */ + st_Label, /* 7: C_ULABEL user label */ + st_Member, /* 8: C_MOS member of struct */ + st_Param, /* 9: C_ARG argument */ + st_Block, /* 10: C_STRTAG struct tag */ + st_Member, /* 11: C_MOU member of union */ + st_Block, /* 12: C_UNTAG union tag */ + st_Typedef, /* 13: C_TPDEF typedef */ + st_Static, /* 14: C_USTATIC ??? */ + st_Block, /* 15: C_ENTAG enum tag */ + st_Member, /* 16: C_MOE member of enum */ + st_Param, /* 17: C_REGPARM register parameter */ + st_Member, /* 18; C_FIELD bitfield */ + st_Nil, /* 19 */ + st_Nil, /* 20 */ + st_Nil, /* 21 */ + st_Nil, /* 22 */ + st_Nil, /* 23 */ + st_Nil, /* 24 */ + st_Nil, /* 25 */ + st_Nil, /* 26 */ + st_Nil, /* 27 */ + st_Nil, /* 28 */ + st_Nil, /* 29 */ + st_Nil, /* 30 */ + st_Nil, /* 31 */ + st_Nil, /* 32 */ + st_Nil, /* 33 */ + st_Nil, /* 34 */ + st_Nil, /* 35 */ + st_Nil, /* 36 */ + st_Nil, /* 37 */ + st_Nil, /* 38 */ + st_Nil, /* 39 */ + st_Nil, /* 40 */ + st_Nil, /* 41 */ + st_Nil, /* 42 */ + st_Nil, /* 43 */ + st_Nil, /* 44 */ + st_Nil, /* 45 */ + st_Nil, /* 46 */ + st_Nil, /* 47 */ + st_Nil, /* 48 */ + st_Nil, /* 49 */ + st_Nil, /* 50 */ + st_Nil, /* 51 */ + st_Nil, /* 52 */ + st_Nil, /* 53 */ + st_Nil, /* 54 */ + st_Nil, /* 55 */ + st_Nil, /* 56 */ + st_Nil, /* 57 */ + st_Nil, /* 58 */ + st_Nil, /* 59 */ + st_Nil, /* 60 */ + st_Nil, /* 61 */ + st_Nil, /* 62 */ + st_Nil, /* 63 */ + st_Nil, /* 64 */ + st_Nil, /* 65 */ + st_Nil, /* 66 */ + st_Nil, /* 67 */ + st_Nil, /* 68 */ + st_Nil, /* 69 */ + st_Nil, /* 70 */ + st_Nil, /* 71 */ + st_Nil, /* 72 */ + st_Nil, /* 73 */ + st_Nil, /* 74 */ + st_Nil, /* 75 */ + st_Nil, /* 76 */ + st_Nil, /* 77 */ + st_Nil, /* 78 */ + st_Nil, /* 79 */ + st_Nil, /* 80 */ + st_Nil, /* 81 */ + st_Nil, /* 82 */ + st_Nil, /* 83 */ + st_Nil, /* 84 */ + st_Nil, /* 85 */ + st_Nil, /* 86 */ + st_Nil, /* 87 */ + st_Nil, /* 88 */ + st_Nil, /* 89 */ + st_Nil, /* 90 */ + st_Nil, /* 91 */ + st_Nil, /* 92 */ + st_Nil, /* 93 */ + st_Nil, /* 94 */ + st_Nil, /* 95 */ + st_Nil, /* 96 */ + st_Nil, /* 97 */ + st_Nil, /* 98 */ + st_Nil, /* 99 */ + st_Block, /* 100: C_BLOCK block start/end */ + st_Proc, /* 101: C_FCN function start/end */ + st_End, /* 102: C_EOS end of struct/union/enum */ + st_File, /* 103: C_FILE file start */ + st_Nil, /* 104: C_LINE line number */ + st_Nil, /* 105: C_ALIAS combined type info */ + st_Nil, /* 106: C_HIDDEN ??? */ +}; + +/* Keep track of different sized allocation requests. */ +static alloc_info_t alloc_counts[(int) alloc_type_last]; + +/* Record whether we have seen any debugging information. */ +int ecoff_debugging_seen = 0; + +/* Various statics. */ +static efdr_t *cur_file_ptr = (efdr_t *) 0; /* current file desc. header */ +static proc_t *cur_proc_ptr = (proc_t *) 0; /* current procedure header */ +static proc_t *first_proc_ptr = (proc_t *) 0; /* first procedure header */ +static thead_t *top_tag_head = (thead_t *) 0; /* top level tag head */ +static thead_t *cur_tag_head = (thead_t *) 0; /* current tag head */ +#ifdef ECOFF_DEBUG +static int debug = 0; /* trace functions */ +#endif +static int stabs_seen = 0; /* != 0 if stabs have been seen */ + +static int current_file_idx; +static const char *current_stabs_filename; + +/* Pseudo symbol to use when putting stabs into the symbol table. */ +#ifndef STABS_SYMBOL +#define STABS_SYMBOL "@stabs" +#endif + +static char stabs_symbol[] = STABS_SYMBOL; + +/* Prototypes for functions defined in this file. */ + +static void add_varray_page (varray_t *vp); +static symint_t add_string (varray_t *vp, + struct hash_control *hash_tbl, + const char *str, + shash_t **ret_hash); +static localsym_t *add_ecoff_symbol (const char *str, st_t type, + sc_t storage, symbolS *sym, + bfd_vma addend, symint_t value, + symint_t indx); +static symint_t add_aux_sym_symint (symint_t aux_word); +static symint_t add_aux_sym_rndx (int file_index, symint_t sym_index); +static symint_t add_aux_sym_tir (type_info_t *t, + hash_state_t state, + thash_t **hash_tbl); +static tag_t *get_tag (const char *tag, localsym_t *sym, bt_t basic_type); +static void add_unknown_tag (tag_t *ptag); +static void add_procedure (char *func); +static void add_file (const char *file_name, int indx, int fake); +#ifdef ECOFF_DEBUG +static char *sc_to_string (sc_t storage_class); +static char *st_to_string (st_t symbol_type); +#endif +static void mark_stabs (int); +static char *ecoff_add_bytes (char **buf, char **bufend, + char *bufptr, unsigned long need); +static unsigned long ecoff_padding_adjust + (const struct ecoff_debug_swap *backend, char **buf, char **bufend, + unsigned long offset, char **bufptrptr); +static unsigned long ecoff_build_lineno + (const struct ecoff_debug_swap *backend, char **buf, char **bufend, + unsigned long offset, long *linecntptr); +static unsigned long ecoff_build_symbols + (const struct ecoff_debug_swap *backend, char **buf, char **bufend, + unsigned long offset); +static unsigned long ecoff_build_procs + (const struct ecoff_debug_swap *backend, char **buf, char **bufend, + unsigned long offset); +static unsigned long ecoff_build_aux + (const struct ecoff_debug_swap *backend, char **buf, char **bufend, + unsigned long offset); +static unsigned long ecoff_build_strings (char **buf, char **bufend, + unsigned long offset, + varray_t *vp); +static unsigned long ecoff_build_ss + (const struct ecoff_debug_swap *backend, char **buf, char **bufend, + unsigned long offset); +static unsigned long ecoff_build_fdr + (const struct ecoff_debug_swap *backend, char **buf, char **bufend, + unsigned long offset); +static void ecoff_setup_ext (void); +static page_type *allocate_cluster (unsigned long npages); +static page_type *allocate_page (void); +static scope_t *allocate_scope (void); +static void free_scope (scope_t *ptr); +static vlinks_t *allocate_vlinks (void); +static shash_t *allocate_shash (void); +static thash_t *allocate_thash (void); +static tag_t *allocate_tag (void); +static void free_tag (tag_t *ptr); +static forward_t *allocate_forward (void); +static thead_t *allocate_thead (void); +static void free_thead (thead_t *ptr); +static lineno_list_t *allocate_lineno_list (void); + +/* This function should be called when the assembler starts up. */ + +void +ecoff_read_begin_hook (void) +{ + tag_hash = hash_new (); + top_tag_head = allocate_thead (); + top_tag_head->first_tag = (tag_t *) NULL; + top_tag_head->free = (thead_t *) NULL; + top_tag_head->prev = cur_tag_head; + cur_tag_head = top_tag_head; +} + +/* This function should be called when a symbol is created. */ + +void +ecoff_symbol_new_hook (symbolS *symbolP) +{ + OBJ_SYMFIELD_TYPE *obj; + + /* Make sure that we have a file pointer, but only if we have seen a + file. If we haven't seen a file, then this is a probably special + symbol created by md_begin which may required special handling at + some point. Creating a dummy file with a dummy name is certainly + wrong. */ + if (cur_file_ptr == (efdr_t *) NULL + && seen_at_least_1_file ()) + add_file ((const char *) NULL, 0, 1); + obj = symbol_get_obj (symbolP); + obj->ecoff_file = cur_file_ptr; + obj->ecoff_symbol = NULL; + obj->ecoff_extern_size = 0; +} + +void +ecoff_symbol_clone_hook (symbolS *newsymP, symbolS *orgsymP) +{ + OBJ_SYMFIELD_TYPE *n, *o; + + n = symbol_get_obj (newsymP); + o = symbol_get_obj (orgsymP); + memcpy (n, o, sizeof *n); +} + +/* Add a page to a varray object. */ + +static void +add_varray_page (varray_t *vp /* varray to add page to */) +{ + vlinks_t *new_links = allocate_vlinks (); + +#ifdef MALLOC_CHECK + if (vp->object_size > 1) + new_links->datum = (page_type *) xcalloc (1, vp->object_size); + else +#endif + new_links->datum = allocate_page (); + + alloc_counts[(int) alloc_type_varray].total_alloc++; + alloc_counts[(int) alloc_type_varray].total_pages++; + + new_links->start_index = vp->num_allocated; + vp->objects_last_page = 0; + + if (vp->first == (vlinks_t *) NULL) /* first allocation? */ + vp->first = vp->last = new_links; + else + { /* 2nd or greater allocation */ + new_links->prev = vp->last; + vp->last->next = new_links; + vp->last = new_links; + } +} + +/* Add a string (and null pad) to one of the string tables. */ + +static symint_t +add_string (varray_t *vp, /* string obstack */ + struct hash_control *hash_tbl, /* ptr to hash table */ + const char *str, /* string */ + shash_t **ret_hash /* return hash pointer */) +{ + register unsigned long len = strlen (str); + register shash_t *hash_ptr; + + if (len >= PAGE_USIZE) + as_fatal (_("string too big (%lu bytes)"), len); + + hash_ptr = (shash_t *) hash_find (hash_tbl, str); + if (hash_ptr == (shash_t *) NULL) + { + register const char *err; + + if (vp->objects_last_page + len >= PAGE_USIZE) + { + vp->num_allocated = + ((vp->num_allocated + PAGE_USIZE - 1) / PAGE_USIZE) * PAGE_USIZE; + add_varray_page (vp); + } + + hash_ptr = allocate_shash (); + hash_ptr->indx = vp->num_allocated; + + hash_ptr->string = &vp->last->datum->byte[vp->objects_last_page]; + + vp->objects_last_page += len + 1; + vp->num_allocated += len + 1; + + strcpy (hash_ptr->string, str); + + err = hash_insert (hash_tbl, str, (char *) hash_ptr); + if (err) + as_fatal (_("inserting \"%s\" into string hash table: %s"), + str, err); + } + + if (ret_hash != (shash_t **) NULL) + *ret_hash = hash_ptr; + + return hash_ptr->indx; +} + +/* Add debugging information for a symbol. */ + +static localsym_t * +add_ecoff_symbol (const char *str, /* symbol name */ + st_t type, /* symbol type */ + sc_t storage, /* storage class */ + symbolS *sym_value, /* associated symbol. */ + bfd_vma addend, /* addend to sym_value. */ + symint_t value, /* value of symbol */ + symint_t indx /* index to local/aux. syms */) +{ + localsym_t *psym; + register scope_t *pscope; + register thead_t *ptag_head; + register tag_t *ptag; + register tag_t *ptag_next; + register varray_t *vp; + register int scope_delta = 0; + shash_t *hash_ptr = (shash_t *) NULL; + + if (cur_file_ptr == (efdr_t *) NULL) + as_fatal (_("no current file pointer")); + + vp = &cur_file_ptr->symbols; + + if (vp->objects_last_page == vp->objects_per_page) + add_varray_page (vp); + + psym = &vp->last->datum->sym[vp->objects_last_page++]; + + if (str == (const char *) NULL && sym_value != (symbolS *) NULL) + psym->name = S_GET_NAME (sym_value); + else + psym->name = str; + psym->as_sym = sym_value; + if (sym_value != (symbolS *) NULL) + symbol_get_obj (sym_value)->ecoff_symbol = psym; + psym->addend = addend; + psym->file_ptr = cur_file_ptr; + psym->proc_ptr = cur_proc_ptr; + psym->begin_ptr = (localsym_t *) NULL; + psym->index_ptr = (aux_t *) NULL; + psym->forward_ref = (forward_t *) NULL; + psym->sym_index = -1; + memset (&psym->ecoff_sym, 0, sizeof (EXTR)); + psym->ecoff_sym.asym.value = value; + psym->ecoff_sym.asym.st = (unsigned) type; + psym->ecoff_sym.asym.sc = (unsigned) storage; + psym->ecoff_sym.asym.index = indx; + + /* If there is an associated symbol, we wait until the end of the + assembly before deciding where to put the name (it may be just an + external symbol). Otherwise, this is just a debugging symbol and + the name should go with the current file. */ + if (sym_value == (symbolS *) NULL) + psym->ecoff_sym.asym.iss = ((str == (const char *) NULL) + ? 0 + : add_string (&cur_file_ptr->strings, + cur_file_ptr->str_hash, + str, + &hash_ptr)); + + ++vp->num_allocated; + + if (ECOFF_IS_STAB (&psym->ecoff_sym.asym)) + return psym; + + /* Save the symbol within the hash table if this is a static + item, and it has a name. */ + if (hash_ptr != (shash_t *) NULL + && (type == st_Global || type == st_Static || type == st_Label + || type == st_Proc || type == st_StaticProc)) + hash_ptr->sym_ptr = psym; + + /* push or pop a scope if appropriate. */ + switch (type) + { + default: + break; + + case st_File: /* beginning of file */ + case st_Proc: /* procedure */ + case st_StaticProc: /* static procedure */ + case st_Block: /* begin scope */ + pscope = allocate_scope (); + pscope->prev = cur_file_ptr->cur_scope; + pscope->lsym = psym; + pscope->type = type; + cur_file_ptr->cur_scope = pscope; + + if (type != st_File) + scope_delta = 1; + + /* For every block type except file, struct, union, or + enumeration blocks, push a level on the tag stack. We omit + file types, so that tags can span file boundaries. */ + if (type != st_File && storage != sc_Info) + { + ptag_head = allocate_thead (); + ptag_head->first_tag = 0; + ptag_head->prev = cur_tag_head; + cur_tag_head = ptag_head; + } + break; + + case st_End: + pscope = cur_file_ptr->cur_scope; + if (pscope == (scope_t *) NULL) + as_fatal (_("too many st_End's")); + else + { + st_t begin_type = (st_t) pscope->lsym->ecoff_sym.asym.st; + + psym->begin_ptr = pscope->lsym; + + if (begin_type != st_File) + scope_delta = -1; + + /* Except for file, structure, union, or enumeration end + blocks remove all tags created within this scope. */ + if (begin_type != st_File && storage != sc_Info) + { + ptag_head = cur_tag_head; + cur_tag_head = ptag_head->prev; + + for (ptag = ptag_head->first_tag; + ptag != (tag_t *) NULL; + ptag = ptag_next) + { + if (ptag->forward_ref != (forward_t *) NULL) + add_unknown_tag (ptag); + + ptag_next = ptag->same_block; + ptag->hash_ptr->tag_ptr = ptag->same_name; + free_tag (ptag); + } + + free_thead (ptag_head); + } + + cur_file_ptr->cur_scope = pscope->prev; + + /* block begin gets next sym #. This is set when we know + the symbol index value. */ + + /* Functions push two or more aux words as follows: + 1st word: index+1 of the end symbol (filled in later). + 2nd word: type of the function (plus any aux words needed). + Also, tie the external pointer back to the function begin symbol. */ + if (begin_type != st_File && begin_type != st_Block) + { + symint_t ty; + varray_t *svp = &cur_file_ptr->aux_syms; + + pscope->lsym->ecoff_sym.asym.index = add_aux_sym_symint (0); + pscope->lsym->index_ptr = + &svp->last->datum->aux[svp->objects_last_page - 1]; + ty = add_aux_sym_tir (&last_func_type_info, + hash_no, + &cur_file_ptr->thash_head[0]); + (void) ty; +/* This seems to be unnecessary. I'm not even sure what it is + * intended to do. It's from mips-tfile. + * if (last_func_sym_value != (symbolS *) NULL) + * { + * last_func_sym_value->ifd = cur_file_ptr->file_index; + * last_func_sym_value->index = ty; + * } + */ + } + + free_scope (pscope); + } + } + + cur_file_ptr->nested_scopes += scope_delta; + +#ifdef ECOFF_DEBUG + if (debug && type != st_File + && (debug > 2 || type == st_Block || type == st_End + || type == st_Proc || type == st_StaticProc)) + { + char *sc_str = sc_to_string (storage); + char *st_str = st_to_string (type); + int depth = cur_file_ptr->nested_scopes + (scope_delta < 0); + + fprintf (stderr, + "\tlsym\tv= %10ld, depth= %2d, sc= %-12s", + value, depth, sc_str); + + if (str_start && str_end_p1 - str_start > 0) + fprintf (stderr, " st= %-11s name= %.*s\n", + st_str, str_end_p1 - str_start, str_start); + else + { + unsigned long len = strlen (st_str); + fprintf (stderr, " st= %.*s\n", len - 1, st_str); + } + } +#endif + + return psym; +} + +/* Add an auxiliary symbol (passing a symint). This is actually used + for integral aux types, not just symints. */ + +static symint_t +add_aux_sym_symint (symint_t aux_word /* auxiliary information word */) +{ + register varray_t *vp; + register aux_t *aux_ptr; + + if (cur_file_ptr == (efdr_t *) NULL) + as_fatal (_("no current file pointer")); + + vp = &cur_file_ptr->aux_syms; + + if (vp->objects_last_page == vp->objects_per_page) + add_varray_page (vp); + + aux_ptr = &vp->last->datum->aux[vp->objects_last_page++]; + aux_ptr->type = aux_isym; + aux_ptr->data.isym = aux_word; + + return vp->num_allocated++; +} + +/* Add an auxiliary symbol (passing a file/symbol index combo). */ + +static symint_t +add_aux_sym_rndx (int file_index, symint_t sym_index) +{ + register varray_t *vp; + register aux_t *aux_ptr; + + if (cur_file_ptr == (efdr_t *) NULL) + as_fatal (_("no current file pointer")); + + vp = &cur_file_ptr->aux_syms; + + if (vp->objects_last_page == vp->objects_per_page) + add_varray_page (vp); + + aux_ptr = &vp->last->datum->aux[vp->objects_last_page++]; + aux_ptr->type = aux_rndx; + aux_ptr->data.rndx.rfd = file_index; + aux_ptr->data.rndx.index = sym_index; + + return vp->num_allocated++; +} + +/* Add an auxiliary symbol (passing the basic type and possibly + type qualifiers). */ + +static symint_t +add_aux_sym_tir (type_info_t *t, /* current type information */ + hash_state_t state, /* whether to hash type or not */ + thash_t **hash_tbl /* pointer to hash table to use */) +{ + register varray_t *vp; + register aux_t *aux_ptr; + static AUXU init_aux; + symint_t ret; + int i; + AUXU aux; + + if (cur_file_ptr == (efdr_t *) NULL) + as_fatal (_("no current file pointer")); + + vp = &cur_file_ptr->aux_syms; + + aux = init_aux; + aux.ti.bt = (int) t->basic_type; + aux.ti.continued = 0; + aux.ti.fBitfield = t->bitfield; + + aux.ti.tq0 = (int) t->type_qualifiers[0]; + aux.ti.tq1 = (int) t->type_qualifiers[1]; + aux.ti.tq2 = (int) t->type_qualifiers[2]; + aux.ti.tq3 = (int) t->type_qualifiers[3]; + aux.ti.tq4 = (int) t->type_qualifiers[4]; + aux.ti.tq5 = (int) t->type_qualifiers[5]; + + /* For anything that adds additional information, we must not hash, + so check here, and reset our state. */ + + if (state != hash_no + && (t->type_qualifiers[0] == tq_Array + || t->type_qualifiers[1] == tq_Array + || t->type_qualifiers[2] == tq_Array + || t->type_qualifiers[3] == tq_Array + || t->type_qualifiers[4] == tq_Array + || t->type_qualifiers[5] == tq_Array + || t->basic_type == bt_Struct + || t->basic_type == bt_Union + || t->basic_type == bt_Enum + || t->bitfield + || t->num_dims > 0)) + state = hash_no; + + /* See if we can hash this type, and save some space, but some types + can't be hashed (because they contain arrays or continuations), + and others can be put into the hash list, but cannot use existing + types because other aux entries precede this one. */ + + if (state != hash_no) + { + register thash_t *hash_ptr; + register symint_t hi; + + hi = aux.isym & ((1 << HASHBITS) - 1); + hi %= THASH_SIZE; + + for (hash_ptr = hash_tbl[hi]; + hash_ptr != (thash_t *)0; + hash_ptr = hash_ptr->next) + { + if (aux.isym == hash_ptr->type.isym) + break; + } + + if (hash_ptr != (thash_t *) NULL && state == hash_yes) + return hash_ptr->indx; + + if (hash_ptr == (thash_t *) NULL) + { + hash_ptr = allocate_thash (); + hash_ptr->next = hash_tbl[hi]; + hash_ptr->type = aux; + hash_ptr->indx = vp->num_allocated; + hash_tbl[hi] = hash_ptr; + } + } + + /* Everything is set up, add the aux symbol. */ + if (vp->objects_last_page == vp->objects_per_page) + add_varray_page (vp); + + aux_ptr = &vp->last->datum->aux[vp->objects_last_page++]; + aux_ptr->type = aux_tir; + aux_ptr->data = aux; + + ret = vp->num_allocated++; + + /* Add bitfield length if it exists. + + NOTE: Mips documentation claims bitfield goes at the end of the + AUX record, but the DECstation compiler emits it here. + (This would only make a difference for enum bitfields.) + + Also note: We use the last size given since gcc may emit 2 + for an enum bitfield. */ + + if (t->bitfield) + (void) add_aux_sym_symint ((symint_t) t->sizes[t->num_sizes - 1]); + + /* Add tag information if needed. Structure, union, and enum + references add 2 aux symbols: a [file index, symbol index] + pointer to the structure type, and the current file index. */ + + if (t->basic_type == bt_Struct + || t->basic_type == bt_Union + || t->basic_type == bt_Enum) + { + register symint_t file_index = t->tag_ptr->ifd; + register localsym_t *sym = t->tag_ptr->sym; + register forward_t *forward_ref = allocate_forward (); + + if (sym != (localsym_t *) NULL) + { + forward_ref->next = sym->forward_ref; + sym->forward_ref = forward_ref; + } + else + { + forward_ref->next = t->tag_ptr->forward_ref; + t->tag_ptr->forward_ref = forward_ref; + } + + (void) add_aux_sym_rndx (ST_RFDESCAPE, indexNil); + forward_ref->index_ptr + = &vp->last->datum->aux[vp->objects_last_page - 1]; + + (void) add_aux_sym_symint (file_index); + forward_ref->ifd_ptr + = &vp->last->datum->aux[vp->objects_last_page - 1]; + } + + /* Add information about array bounds if they exist. */ + for (i = 0; i < t->num_dims; i++) + { + (void) add_aux_sym_rndx (ST_RFDESCAPE, + cur_file_ptr->int_type); + + (void) add_aux_sym_symint (cur_file_ptr->file_index); /* file index*/ + (void) add_aux_sym_symint ((symint_t) 0); /* low bound */ + (void) add_aux_sym_symint (t->dimensions[i] - 1); /* high bound*/ + (void) add_aux_sym_symint ((t->dimensions[i] == 0) /* stride */ + ? 0 + : (t->sizes[i] * 8) / t->dimensions[i]); + }; + + /* NOTE: Mips documentation claims that the bitfield width goes here. + But it needs to be emitted earlier. */ + + return ret; +} + +/* Add a tag to the tag table (unless it already exists). */ + +static tag_t * +get_tag (const char *tag, /* tag name */ + localsym_t *sym, /* tag start block */ + bt_t basic_type /* bt_Struct, bt_Union, or bt_Enum */) +{ + shash_t *hash_ptr; + const char *err; + tag_t *tag_ptr; + + if (cur_file_ptr == (efdr_t *) NULL) + as_fatal (_("no current file pointer")); + + hash_ptr = (shash_t *) hash_find (tag_hash, tag); + + if (hash_ptr != (shash_t *) NULL + && hash_ptr->tag_ptr != (tag_t *) NULL) + { + tag_ptr = hash_ptr->tag_ptr; + if (sym != (localsym_t *) NULL) + { + tag_ptr->basic_type = basic_type; + tag_ptr->ifd = cur_file_ptr->file_index; + tag_ptr->sym = sym; + } + return tag_ptr; + } + + if (hash_ptr == (shash_t *) NULL) + { + char *perm; + + perm = xstrdup (tag); + hash_ptr = allocate_shash (); + err = hash_insert (tag_hash, perm, (char *) hash_ptr); + if (err) + as_fatal (_("inserting \"%s\" into tag hash table: %s"), + tag, err); + hash_ptr->string = perm; + } + + tag_ptr = allocate_tag (); + tag_ptr->forward_ref = (forward_t *) NULL; + tag_ptr->hash_ptr = hash_ptr; + tag_ptr->same_name = hash_ptr->tag_ptr; + tag_ptr->basic_type = basic_type; + tag_ptr->sym = sym; + tag_ptr->ifd = ((sym == (localsym_t *) NULL) + ? (symint_t) -1 + : cur_file_ptr->file_index); + tag_ptr->same_block = cur_tag_head->first_tag; + + cur_tag_head->first_tag = tag_ptr; + hash_ptr->tag_ptr = tag_ptr; + + return tag_ptr; +} + +/* Add an unknown {struct, union, enum} tag. */ + +static void +add_unknown_tag (tag_t *ptag /* pointer to tag information */) +{ + shash_t *hash_ptr = ptag->hash_ptr; + char *name = hash_ptr->string; + localsym_t *sym; + forward_t **pf; + +#ifdef ECOFF_DEBUG + if (debug > 1) + { + char *agg_type = "{unknown aggregate type}"; + switch (ptag->basic_type) + { + case bt_Struct: agg_type = "struct"; break; + case bt_Union: agg_type = "union"; break; + case bt_Enum: agg_type = "enum"; break; + default: break; + } + + fprintf (stderr, "unknown %s %.*s found\n", agg_type, + hash_ptr->len, name_start); + } +#endif + + sym = add_ecoff_symbol (name, + st_Block, + sc_Info, + (symbolS *) NULL, + (bfd_vma) 0, + (symint_t) 0, + (symint_t) 0); + + (void) add_ecoff_symbol (name, + st_End, + sc_Info, + (symbolS *) NULL, + (bfd_vma) 0, + (symint_t) 0, + (symint_t) 0); + + for (pf = &sym->forward_ref; *pf != (forward_t *) NULL; pf = &(*pf)->next) + ; + *pf = ptag->forward_ref; +} + +/* Add a procedure to the current file's list of procedures, and record + this is the current procedure. */ + +static void +add_procedure (char *func /* func name */) +{ + register varray_t *vp; + register proc_t *new_proc_ptr; + symbolS *sym; + +#ifdef ECOFF_DEBUG + if (debug) + fputc ('\n', stderr); +#endif + + if (cur_file_ptr == (efdr_t *) NULL) + as_fatal (_("no current file pointer")); + + vp = &cur_file_ptr->procs; + + if (vp->objects_last_page == vp->objects_per_page) + add_varray_page (vp); + + cur_proc_ptr = new_proc_ptr = &vp->last->datum->proc[vp->objects_last_page++]; + + if (first_proc_ptr == (proc_t *) NULL) + first_proc_ptr = new_proc_ptr; + + vp->num_allocated++; + + new_proc_ptr->pdr.isym = -1; + new_proc_ptr->pdr.iline = -1; + new_proc_ptr->pdr.lnLow = -1; + new_proc_ptr->pdr.lnHigh = -1; + + /* Set the BSF_FUNCTION flag for the symbol. */ + sym = symbol_find_or_make (func); + symbol_get_bfdsym (sym)->flags |= BSF_FUNCTION; + + /* Push the start of the function. */ + new_proc_ptr->sym = add_ecoff_symbol ((const char *) NULL, st_Proc, sc_Text, + sym, (bfd_vma) 0, (symint_t) 0, + (symint_t) 0); + + ++proc_cnt; + + /* Fill in the linenos preceding the .ent, if any. */ + if (noproc_lineno != (lineno_list_t *) NULL) + { + lineno_list_t *l; + + for (l = noproc_lineno; l != (lineno_list_t *) NULL; l = l->next) + l->proc = new_proc_ptr; + *last_lineno_ptr = noproc_lineno; + while (*last_lineno_ptr != NULL) + { + last_lineno = *last_lineno_ptr; + last_lineno_ptr = &last_lineno->next; + } + noproc_lineno = (lineno_list_t *) NULL; + } +} + +symbolS * +ecoff_get_cur_proc_sym (void) +{ + return (cur_proc_ptr ? cur_proc_ptr->sym->as_sym : NULL); +} + +/* Add a new filename, and set up all of the file relative + virtual arrays (strings, symbols, aux syms, etc.). Record + where the current file structure lives. */ + +static void +add_file (const char *file_name, int indx ATTRIBUTE_UNUSED, int fake) +{ + register int first_ch; + register efdr_t *fil_ptr; + +#ifdef ECOFF_DEBUG + if (debug) + fprintf (stderr, "\tfile\t%.*s\n", len, file_start); +#endif + + /* If the file name is NULL, then no .file symbol appeared, and we + want to use the actual file name. */ + if (file_name == (const char *) NULL) + { + char *file; + + if (first_file != (efdr_t *) NULL) + as_fatal (_("fake .file after real one")); + as_where (&file, (unsigned int *) NULL); + file_name = (const char *) file; + + /* Automatically generate ECOFF debugging information, since I + think that's what other ECOFF assemblers do. We don't do + this if we see a .file directive with a string, since that + implies that some sort of debugging information is being + provided. */ + if (! symbol_table_frozen && debug_type == DEBUG_UNSPECIFIED) + debug_type = DEBUG_ECOFF; + } + else if (debug_type == DEBUG_UNSPECIFIED) + debug_type = DEBUG_NONE; + +#ifndef NO_LISTING + if (listing) + listing_source_file (file_name); +#endif + + current_stabs_filename = file_name; + + /* If we're creating stabs, then we don't actually make a new FDR. + Instead, we just create a stabs symbol. */ + if (stabs_seen) + { + (void) add_ecoff_symbol (file_name, st_Nil, sc_Nil, + symbol_new ("L0\001", now_seg, + (valueT) frag_now_fix (), + frag_now), + (bfd_vma) 0, 0, ECOFF_MARK_STAB (N_SOL)); + return; + } + + first_ch = *file_name; + + /* FIXME: We can't safely merge files which have line number + information (fMerge will be zero in this case). Otherwise, we + get incorrect line number debugging info. See for instance + ecoff_build_lineno, which will end up setting all file->fdr.* + fields multiple times, resulting in incorrect debug info. In + order to make this work right, all line number and symbol info + for the same source file has to be adjacent in the object file, + so that a single file descriptor can be used to point to them. + This would require maintaining file specific lists of line + numbers and symbols for each file, so that they can be merged + together (or output together) when two .file pseudo-ops are + merged into one file descriptor. */ + + /* See if the file has already been created. */ + for (fil_ptr = first_file; + fil_ptr != (efdr_t *) NULL; + fil_ptr = fil_ptr->next_file) + { + if (first_ch == fil_ptr->name[0] + && filename_cmp (file_name, fil_ptr->name) == 0 + && fil_ptr->fdr.fMerge) + { + cur_file_ptr = fil_ptr; + if (! fake) + cur_file_ptr->fake = 0; + break; + } + } + + /* If this is a new file, create it. */ + if (fil_ptr == (efdr_t *) NULL) + { + if (file_desc.objects_last_page == file_desc.objects_per_page) + add_varray_page (&file_desc); + + fil_ptr = cur_file_ptr = + &file_desc.last->datum->file[file_desc.objects_last_page++]; + *fil_ptr = init_file; + + fil_ptr->file_index = current_file_idx++; + ++file_desc.num_allocated; + + fil_ptr->fake = fake; + + /* Allocate the string hash table. */ + fil_ptr->str_hash = hash_new (); + + /* Make sure 0 byte in string table is null */ + add_string (&fil_ptr->strings, + fil_ptr->str_hash, + "", + (shash_t **)0); + + if (strlen (file_name) > PAGE_USIZE - 2) + as_fatal (_("filename goes over one page boundary")); + + /* Push the start of the filename. We assume that the filename + will be stored at string offset 1. */ + (void) add_ecoff_symbol (file_name, st_File, sc_Text, + (symbolS *) NULL, (bfd_vma) 0, + (symint_t) 0, (symint_t) 0); + fil_ptr->fdr.rss = 1; + fil_ptr->name = &fil_ptr->strings.last->datum->byte[1]; + + /* Update the linked list of file descriptors. */ + *last_file_ptr = fil_ptr; + last_file_ptr = &fil_ptr->next_file; + + /* Add void & int types to the file (void should be first to catch + errant 0's within the index fields). */ + fil_ptr->void_type = add_aux_sym_tir (&void_type_info, + hash_yes, + &cur_file_ptr->thash_head[0]); + + fil_ptr->int_type = add_aux_sym_tir (&int_type_info, + hash_yes, + &cur_file_ptr->thash_head[0]); + } +} + +/* This function is called when the assembler notices a preprocessor + directive switching to a new file. This will not happen in + compiler output, only in hand coded assembler. */ + +void +ecoff_new_file (const char *name, int appfile ATTRIBUTE_UNUSED) +{ + if (cur_file_ptr != NULL && filename_cmp (cur_file_ptr->name, name) == 0) + return; + add_file (name, 0, 0); + + /* This is a hand coded assembler file, so automatically turn on + debugging information. */ + if (debug_type == DEBUG_UNSPECIFIED) + debug_type = DEBUG_ECOFF; +} + +#ifdef ECOFF_DEBUG + +/* Convert storage class to string. */ + +static char * +sc_to_string (storage_class) + sc_t storage_class; +{ + switch (storage_class) + { + case sc_Nil: return "Nil,"; + case sc_Text: return "Text,"; + case sc_Data: return "Data,"; + case sc_Bss: return "Bss,"; + case sc_Register: return "Register,"; + case sc_Abs: return "Abs,"; + case sc_Undefined: return "Undefined,"; + case sc_CdbLocal: return "CdbLocal,"; + case sc_Bits: return "Bits,"; + case sc_CdbSystem: return "CdbSystem,"; + case sc_RegImage: return "RegImage,"; + case sc_Info: return "Info,"; + case sc_UserStruct: return "UserStruct,"; + case sc_SData: return "SData,"; + case sc_SBss: return "SBss,"; + case sc_RData: return "RData,"; + case sc_Var: return "Var,"; + case sc_Common: return "Common,"; + case sc_SCommon: return "SCommon,"; + case sc_VarRegister: return "VarRegister,"; + case sc_Variant: return "Variant,"; + case sc_SUndefined: return "SUndefined,"; + case sc_Init: return "Init,"; + case sc_Max: return "Max,"; + } + + return "???,"; +} + +#endif /* DEBUG */ + +#ifdef ECOFF_DEBUG + +/* Convert symbol type to string. */ + +static char * +st_to_string (symbol_type) + st_t symbol_type; +{ + switch (symbol_type) + { + case st_Nil: return "Nil,"; + case st_Global: return "Global,"; + case st_Static: return "Static,"; + case st_Param: return "Param,"; + case st_Local: return "Local,"; + case st_Label: return "Label,"; + case st_Proc: return "Proc,"; + case st_Block: return "Block,"; + case st_End: return "End,"; + case st_Member: return "Member,"; + case st_Typedef: return "Typedef,"; + case st_File: return "File,"; + case st_RegReloc: return "RegReloc,"; + case st_Forward: return "Forward,"; + case st_StaticProc: return "StaticProc,"; + case st_Constant: return "Constant,"; + case st_Str: return "String,"; + case st_Number: return "Number,"; + case st_Expr: return "Expr,"; + case st_Type: return "Type,"; + case st_Max: return "Max,"; + } + + return "???,"; +} + +#endif /* DEBUG */ + +/* Parse .begin directives which have a label as the first argument + which gives the location of the start of the block. */ + +void +ecoff_directive_begin (int ignore ATTRIBUTE_UNUSED) +{ + char *name; + char name_end; + + if (cur_file_ptr == (efdr_t *) NULL) + { + as_warn (_(".begin directive without a preceding .file directive")); + demand_empty_rest_of_line (); + return; + } + + if (cur_proc_ptr == (proc_t *) NULL) + { + as_warn (_(".begin directive without a preceding .ent directive")); + demand_empty_rest_of_line (); + return; + } + + name = input_line_pointer; + name_end = get_symbol_end (); + + (void) add_ecoff_symbol ((const char *) NULL, st_Block, sc_Text, + symbol_find_or_make (name), + (bfd_vma) 0, (symint_t) 0, (symint_t) 0); + + *input_line_pointer = name_end; + + /* The line number follows, but we don't use it. */ + (void) get_absolute_expression (); + demand_empty_rest_of_line (); +} + +/* Parse .bend directives which have a label as the first argument + which gives the location of the end of the block. */ + +void +ecoff_directive_bend (int ignore ATTRIBUTE_UNUSED) +{ + char *name; + char name_end; + symbolS *endsym; + + if (cur_file_ptr == (efdr_t *) NULL) + { + as_warn (_(".bend directive without a preceding .file directive")); + demand_empty_rest_of_line (); + return; + } + + if (cur_proc_ptr == (proc_t *) NULL) + { + as_warn (_(".bend directive without a preceding .ent directive")); + demand_empty_rest_of_line (); + return; + } + + name = input_line_pointer; + name_end = get_symbol_end (); + + /* The value is the distance between the .bend directive and the + corresponding symbol. We fill in the offset when we write out + the symbol. */ + endsym = symbol_find (name); + if (endsym == (symbolS *) NULL) + as_warn (_(".bend directive names unknown symbol")); + else + (void) add_ecoff_symbol ((const char *) NULL, st_End, sc_Text, endsym, + (bfd_vma) 0, (symint_t) 0, (symint_t) 0); + + *input_line_pointer = name_end; + + /* The line number follows, but we don't use it. */ + (void) get_absolute_expression (); + demand_empty_rest_of_line (); +} + +/* COFF debugging information is provided as a series of directives + (.def, .scl, etc.). We build up information as we read the + directives in the following static variables, and file it away when + we reach the .endef directive. */ +static char *coff_sym_name; +static type_info_t coff_type; +static sc_t coff_storage_class; +static st_t coff_symbol_typ; +static int coff_is_function; +static char *coff_tag; +static valueT coff_value; +static symbolS *coff_sym_value; +static bfd_vma coff_sym_addend; +static int coff_inside_enumeration; + +/* Handle a .def directive: start defining a symbol. */ + +void +ecoff_directive_def (int ignore ATTRIBUTE_UNUSED) +{ + char *name; + char name_end; + + ecoff_debugging_seen = 1; + + SKIP_WHITESPACE (); + + name = input_line_pointer; + name_end = get_symbol_end (); + + if (coff_sym_name != (char *) NULL) + as_warn (_(".def pseudo-op used inside of .def/.endef; ignored")); + else if (*name == '\0') + as_warn (_("empty symbol name in .def; ignored")); + else + { + if (coff_sym_name != (char *) NULL) + free (coff_sym_name); + if (coff_tag != (char *) NULL) + free (coff_tag); + + coff_sym_name = xstrdup (name); + coff_type = type_info_init; + coff_storage_class = sc_Nil; + coff_symbol_typ = st_Nil; + coff_is_function = 0; + coff_tag = (char *) NULL; + coff_value = 0; + coff_sym_value = (symbolS *) NULL; + coff_sym_addend = 0; + } + + *input_line_pointer = name_end; + + demand_empty_rest_of_line (); +} + +/* Handle a .dim directive, used to give dimensions for an array. The + arguments are comma separated numbers. mips-tfile assumes that + there will not be more than 6 dimensions, and gdb won't read any + more than that anyhow, so I will also make that assumption. */ + +void +ecoff_directive_dim (int ignore ATTRIBUTE_UNUSED) +{ + int dimens[N_TQ]; + int i; + + if (coff_sym_name == (char *) NULL) + { + as_warn (_(".dim pseudo-op used outside of .def/.endef; ignored")); + demand_empty_rest_of_line (); + return; + } + + for (i = 0; i < N_TQ; i++) + { + SKIP_WHITESPACE (); + dimens[i] = get_absolute_expression (); + if (*input_line_pointer == ',') + ++input_line_pointer; + else + { + if (*input_line_pointer != '\n' + && *input_line_pointer != ';') + as_warn (_("badly formed .dim directive")); + break; + } + } + + if (i == N_TQ) + --i; + + /* The dimensions are stored away in reverse order. */ + for (; i >= 0; i--) + { + if (coff_type.num_dims >= N_TQ) + { + as_warn (_("too many .dim entries")); + break; + } + coff_type.dimensions[coff_type.num_dims] = dimens[i]; + ++coff_type.num_dims; + } + + demand_empty_rest_of_line (); +} + +/* Handle a .scl directive, which sets the COFF storage class of the + symbol. */ + +void +ecoff_directive_scl (int ignore ATTRIBUTE_UNUSED) +{ + long val; + + if (coff_sym_name == (char *) NULL) + { + as_warn (_(".scl pseudo-op used outside of .def/.endef; ignored")); + demand_empty_rest_of_line (); + return; + } + + val = get_absolute_expression (); + + coff_symbol_typ = map_coff_sym_type[val]; + coff_storage_class = map_coff_storage[val]; + + demand_empty_rest_of_line (); +} + +/* Handle a .size directive. For some reason mips-tfile.c thinks that + .size can have multiple arguments. We humor it, although gcc will + never generate more than one argument. */ + +void +ecoff_directive_size (int ignore ATTRIBUTE_UNUSED) +{ + int sizes[N_TQ]; + int i; + + if (coff_sym_name == (char *) NULL) + { + as_warn (_(".size pseudo-op used outside of .def/.endef; ignored")); + demand_empty_rest_of_line (); + return; + } + + for (i = 0; i < N_TQ; i++) + { + SKIP_WHITESPACE (); + sizes[i] = get_absolute_expression (); + if (*input_line_pointer == ',') + ++input_line_pointer; + else + { + if (*input_line_pointer != '\n' + && *input_line_pointer != ';') + as_warn (_("badly formed .size directive")); + break; + } + } + + if (i == N_TQ) + --i; + + /* The sizes are stored away in reverse order. */ + for (; i >= 0; i--) + { + if (coff_type.num_sizes >= N_TQ) + { + as_warn (_("too many .size entries")); + break; + } + coff_type.sizes[coff_type.num_sizes] = sizes[i]; + ++coff_type.num_sizes; + } + + demand_empty_rest_of_line (); +} + +/* Handle the .type directive, which gives the COFF type of the + symbol. */ + +void +ecoff_directive_type (int ignore ATTRIBUTE_UNUSED) +{ + long val; + tq_t *tq_ptr; + tq_t *tq_shft; + + if (coff_sym_name == (char *) NULL) + { + as_warn (_(".type pseudo-op used outside of .def/.endef; ignored")); + demand_empty_rest_of_line (); + return; + } + + val = get_absolute_expression (); + + coff_type.orig_type = BTYPE (val); + coff_type.basic_type = map_coff_types[coff_type.orig_type]; + + tq_ptr = &coff_type.type_qualifiers[N_TQ]; + while (val & ~N_BTMASK) + { + if (tq_ptr == &coff_type.type_qualifiers[0]) + { + /* FIXME: We could handle this by setting the continued bit. + There would still be a limit: the .type argument can not + be infinite. */ + as_warn (_("the type of %s is too complex; it will be simplified"), + coff_sym_name); + break; + } + if (ISPTR (val)) + *--tq_ptr = tq_Ptr; + else if (ISFCN (val)) + *--tq_ptr = tq_Proc; + else if (ISARY (val)) + *--tq_ptr = tq_Array; + else + as_fatal (_("Unrecognized .type argument")); + + val = DECREF (val); + } + + tq_shft = &coff_type.type_qualifiers[0]; + while (tq_ptr != &coff_type.type_qualifiers[N_TQ]) + *tq_shft++ = *tq_ptr++; + + if (tq_shft != &coff_type.type_qualifiers[0] && tq_shft[-1] == tq_Proc) + { + /* If this is a function, ignore it, so that we don't get two + entries (one from the .ent, and one for the .def that + precedes it). Save the type information so that the end + block can properly add it after the begin block index. For + MIPS knows what reason, we must strip off the function type + at this point. */ + coff_is_function = 1; + tq_shft[-1] = tq_Nil; + } + + while (tq_shft != &coff_type.type_qualifiers[N_TQ]) + *tq_shft++ = tq_Nil; + + demand_empty_rest_of_line (); +} + +/* Handle the .tag directive, which gives the name of a structure, + union or enum. */ + +void +ecoff_directive_tag (int ignore ATTRIBUTE_UNUSED) +{ + char *name; + char name_end; + + if (coff_sym_name == (char *) NULL) + { + as_warn (_(".tag pseudo-op used outside of .def/.endef; ignored")); + demand_empty_rest_of_line (); + return; + } + + name = input_line_pointer; + name_end = get_symbol_end (); + + coff_tag = xstrdup (name); + + *input_line_pointer = name_end; + + demand_empty_rest_of_line (); +} + +/* Handle the .val directive, which gives the value of the symbol. It + may be the name of a static or global symbol. */ + +void +ecoff_directive_val (int ignore ATTRIBUTE_UNUSED) +{ + expressionS exp; + + if (coff_sym_name == (char *) NULL) + { + as_warn (_(".val pseudo-op used outside of .def/.endef; ignored")); + demand_empty_rest_of_line (); + return; + } + + expression (&exp); + if (exp.X_op != O_constant && exp.X_op != O_symbol) + { + as_bad (_(".val expression is too complex")); + demand_empty_rest_of_line (); + return; + } + + if (exp.X_op == O_constant) + coff_value = exp.X_add_number; + else + { + coff_sym_value = exp.X_add_symbol; + coff_sym_addend = exp.X_add_number; + } + + demand_empty_rest_of_line (); +} + +/* Handle the .endef directive, which terminates processing of COFF + debugging information for a symbol. */ + +void +ecoff_directive_endef (int ignore ATTRIBUTE_UNUSED) +{ + char *name; + symint_t indx; + localsym_t *sym; + + demand_empty_rest_of_line (); + + if (coff_sym_name == (char *) NULL) + { + as_warn (_(".endef pseudo-op used before .def; ignored")); + return; + } + + name = coff_sym_name; + coff_sym_name = (char *) NULL; + + /* If the symbol is a static or external, we have already gotten the + appropriate type and class, so make sure we don't override those + values. This is needed because there are some type and classes + that are not in COFF, such as short data, etc. */ + if (coff_sym_value != (symbolS *) NULL) + { + coff_symbol_typ = st_Nil; + coff_storage_class = sc_Nil; + } + + coff_type.extra_sizes = coff_tag != (char *) NULL; + if (coff_type.num_dims > 0) + { + int diff = coff_type.num_dims - coff_type.num_sizes; + int i = coff_type.num_dims - 1; + int j; + + if (coff_type.num_sizes != 1 || diff < 0) + { + as_warn (_("bad COFF debugging information")); + return; + } + + /* If this is an array, make sure the same number of dimensions + and sizes were passed, creating extra sizes for multiply + dimensioned arrays if not passed. */ + coff_type.extra_sizes = 0; + if (diff) + { + j = (sizeof (coff_type.sizes) / sizeof (coff_type.sizes[0])) - 1; + while (j >= 0) + { + coff_type.sizes[j] = (((j - diff) >= 0) + ? coff_type.sizes[j - diff] + : 0); + j--; + } + + coff_type.num_sizes = i + 1; + for (i--; i >= 0; i--) + coff_type.sizes[i] = (coff_type.dimensions[i + 1] == 0 + ? 0 + : (coff_type.sizes[i + 1] + / coff_type.dimensions[i + 1])); + } + } + else if (coff_symbol_typ == st_Member + && coff_type.num_sizes - coff_type.extra_sizes == 1) + { + /* Is this a bitfield? This is indicated by a structure member + having a size field that isn't an array. */ + coff_type.bitfield = 1; + } + + /* Except for enumeration members & begin/ending of scopes, put the + type word in the aux. symbol table. */ + if (coff_symbol_typ == st_Block || coff_symbol_typ == st_End) + indx = 0; + else if (coff_inside_enumeration) + indx = cur_file_ptr->void_type; + else + { + if (coff_type.basic_type == bt_Struct + || coff_type.basic_type == bt_Union + || coff_type.basic_type == bt_Enum) + { + if (coff_tag == (char *) NULL) + { + as_warn (_("no tag specified for %s"), name); + return; + } + + coff_type.tag_ptr = get_tag (coff_tag, (localsym_t *) NULL, + coff_type.basic_type); + } + + if (coff_is_function) + { + last_func_type_info = coff_type; + last_func_sym_value = coff_sym_value; + return; + } + + indx = add_aux_sym_tir (&coff_type, + hash_yes, + &cur_file_ptr->thash_head[0]); + } + + /* Do any last minute adjustments that are necessary. */ + switch (coff_symbol_typ) + { + default: + break; + + /* For the beginning of structs, unions, and enumerations, the + size info needs to be passed in the value field. */ + case st_Block: + if (coff_type.num_sizes - coff_type.num_dims - coff_type.extra_sizes + != 1) + { + as_warn (_("bad COFF debugging information")); + return; + } + else + coff_value = coff_type.sizes[0]; + + coff_inside_enumeration = (coff_type.orig_type == T_ENUM); + break; + + /* For the end of structs, unions, and enumerations, omit the + name which is always ".eos". This needs to be done last, so + that any error reporting above gives the correct name. */ + case st_End: + free (name); + name = (char *) NULL; + coff_value = 0; + coff_inside_enumeration = 0; + break; + + /* Members of structures and unions that aren't bitfields, need + to adjust the value from a byte offset to a bit offset. + Members of enumerations do not have the value adjusted, and + can be distinguished by indx == indexNil. For enumerations, + update the maximum enumeration value. */ + case st_Member: + if (! coff_type.bitfield && ! coff_inside_enumeration) + coff_value *= 8; + + break; + } + + /* Add the symbol. */ + sym = add_ecoff_symbol (name, + coff_symbol_typ, + coff_storage_class, + coff_sym_value, + coff_sym_addend, + (symint_t) coff_value, + indx); + + /* deal with struct, union, and enum tags. */ + if (coff_symbol_typ == st_Block) + { + /* Create or update the tag information. */ + tag_t *tag_ptr = get_tag (name, + sym, + coff_type.basic_type); + forward_t **pf; + + /* Remember any forward references. */ + for (pf = &sym->forward_ref; + *pf != (forward_t *) NULL; + pf = &(*pf)->next) + ; + *pf = tag_ptr->forward_ref; + tag_ptr->forward_ref = (forward_t *) NULL; + } +} + +/* Parse .end directives. */ + +void +ecoff_directive_end (int ignore ATTRIBUTE_UNUSED) +{ + char *name; + char name_end; + symbolS *ent; + + if (cur_file_ptr == (efdr_t *) NULL) + { + as_warn (_(".end directive without a preceding .file directive")); + demand_empty_rest_of_line (); + return; + } + + if (cur_proc_ptr == (proc_t *) NULL) + { + as_warn (_(".end directive without a preceding .ent directive")); + demand_empty_rest_of_line (); + return; + } + + name = input_line_pointer; + name_end = get_symbol_end (); + + if (name == input_line_pointer) + { + as_warn (_(".end directive has no name")); + *input_line_pointer = name_end; + demand_empty_rest_of_line (); + return; + } + + /* The value is the distance between the .end directive and the + corresponding symbol. We create a fake symbol to hold the + current location, and put in the offset when we write out the + symbol. */ + ent = symbol_find (name); + if (ent == (symbolS *) NULL) + as_warn (_(".end directive names unknown symbol")); + else + (void) add_ecoff_symbol ((const char *) NULL, st_End, sc_Text, + symbol_new ("L0\001", now_seg, + (valueT) frag_now_fix (), + frag_now), + (bfd_vma) 0, (symint_t) 0, (symint_t) 0); + + cur_proc_ptr = (proc_t *) NULL; + + *input_line_pointer = name_end; + demand_empty_rest_of_line (); +} + +/* Parse .ent directives. */ + +void +ecoff_directive_ent (int ignore ATTRIBUTE_UNUSED) +{ + char *name; + char name_end; + + if (cur_file_ptr == (efdr_t *) NULL) + add_file ((const char *) NULL, 0, 1); + + if (cur_proc_ptr != (proc_t *) NULL) + { + as_warn (_("second .ent directive found before .end directive")); + demand_empty_rest_of_line (); + return; + } + + name = input_line_pointer; + name_end = get_symbol_end (); + + if (name == input_line_pointer) + { + as_warn (_(".ent directive has no name")); + *input_line_pointer = name_end; + demand_empty_rest_of_line (); + return; + } + + add_procedure (name); + + *input_line_pointer = name_end; + + /* The .ent directive is sometimes followed by a number. I'm not + really sure what the number means. I don't see any way to store + the information in the PDR. The Irix 4 assembler seems to ignore + the information. */ + SKIP_WHITESPACE (); + if (*input_line_pointer == ',') + { + ++input_line_pointer; + SKIP_WHITESPACE (); + } + if (ISDIGIT (*input_line_pointer) + || *input_line_pointer == '-') + (void) get_absolute_expression (); + + demand_empty_rest_of_line (); +} + +/* Parse .extern directives. */ + +void +ecoff_directive_extern (int ignore ATTRIBUTE_UNUSED) +{ + char *name; + int c; + symbolS *symbolp; + valueT size; + + name = input_line_pointer; + c = get_symbol_end (); + symbolp = symbol_find_or_make (name); + *input_line_pointer = c; + + S_SET_EXTERNAL (symbolp); + + if (*input_line_pointer == ',') + ++input_line_pointer; + size = get_absolute_expression (); + + symbol_get_obj (symbolp)->ecoff_extern_size = size; +} + +/* Parse .file directives. */ + +void +ecoff_directive_file (int ignore ATTRIBUTE_UNUSED) +{ + int indx; + char *name; + int len; + + if (cur_proc_ptr != (proc_t *) NULL) + { + as_warn (_("no way to handle .file within .ent/.end section")); + demand_empty_rest_of_line (); + return; + } + + indx = (int) get_absolute_expression (); + + /* FIXME: we don't have to save the name here. */ + name = demand_copy_C_string (&len); + + add_file (name, indx - 1, 0); + + demand_empty_rest_of_line (); +} + +/* Parse .fmask directives. */ + +void +ecoff_directive_fmask (int ignore ATTRIBUTE_UNUSED) +{ + long val; + + if (cur_proc_ptr == (proc_t *) NULL) + { + as_warn (_(".fmask outside of .ent")); + demand_empty_rest_of_line (); + return; + } + + if (get_absolute_expression_and_terminator (&val) != ',') + { + as_warn (_("bad .fmask directive")); + --input_line_pointer; + demand_empty_rest_of_line (); + return; + } + + cur_proc_ptr->pdr.fregmask = val; + cur_proc_ptr->pdr.fregoffset = get_absolute_expression (); + + demand_empty_rest_of_line (); +} + +/* Parse .frame directives. */ + +void +ecoff_directive_frame (int ignore ATTRIBUTE_UNUSED) +{ + long val; + + if (cur_proc_ptr == (proc_t *) NULL) + { + as_warn (_(".frame outside of .ent")); + demand_empty_rest_of_line (); + return; + } + + cur_proc_ptr->pdr.framereg = tc_get_register (1); + + SKIP_WHITESPACE (); + if (*input_line_pointer++ != ',' + || get_absolute_expression_and_terminator (&val) != ',') + { + as_warn (_("bad .frame directive")); + --input_line_pointer; + demand_empty_rest_of_line (); + return; + } + + cur_proc_ptr->pdr.frameoffset = val; + + cur_proc_ptr->pdr.pcreg = tc_get_register (0); + + /* Alpha-OSF1 adds "the offset of saved $a0 from $sp", according to + Sandro. I don't yet know where this value should be stored, if + anywhere. Don't call demand_empty_rest_of_line (). */ + s_ignore (42); +} + +/* Parse .mask directives. */ + +void +ecoff_directive_mask (int ignore ATTRIBUTE_UNUSED) +{ + long val; + + if (cur_proc_ptr == (proc_t *) NULL) + { + as_warn (_(".mask outside of .ent")); + demand_empty_rest_of_line (); + return; + } + + if (get_absolute_expression_and_terminator (&val) != ',') + { + as_warn (_("bad .mask directive")); + --input_line_pointer; + demand_empty_rest_of_line (); + return; + } + + cur_proc_ptr->pdr.regmask = val; + cur_proc_ptr->pdr.regoffset = get_absolute_expression (); + + demand_empty_rest_of_line (); +} + +/* Parse .loc directives. */ + +void +ecoff_directive_loc (int ignore ATTRIBUTE_UNUSED) +{ + lineno_list_t *list; + symint_t lineno; + + if (cur_file_ptr == (efdr_t *) NULL) + { + as_warn (_(".loc before .file")); + demand_empty_rest_of_line (); + return; + } + + if (now_seg != text_section) + { + as_warn (_(".loc outside of .text")); + demand_empty_rest_of_line (); + return; + } + + /* Skip the file number. */ + SKIP_WHITESPACE (); + get_absolute_expression (); + SKIP_WHITESPACE (); + + lineno = get_absolute_expression (); + +#ifndef NO_LISTING + if (listing) + listing_source_line (lineno); +#endif + + /* If we're building stabs, then output a special label rather than + ECOFF line number info. */ + if (stabs_seen) + { + (void) add_ecoff_symbol ((char *) NULL, st_Label, sc_Text, + symbol_new ("L0\001", now_seg, + (valueT) frag_now_fix (), + frag_now), + (bfd_vma) 0, 0, lineno); + return; + } + + list = allocate_lineno_list (); + + list->next = (lineno_list_t *) NULL; + list->file = cur_file_ptr; + list->proc = cur_proc_ptr; + list->frag = frag_now; + list->paddr = frag_now_fix (); + list->lineno = lineno; + + /* We don't want to merge files which have line numbers. */ + cur_file_ptr->fdr.fMerge = 0; + + /* A .loc directive will sometimes appear before a .ent directive, + which means that cur_proc_ptr will be NULL here. Arrange to + patch this up. */ + if (cur_proc_ptr == (proc_t *) NULL) + { + lineno_list_t **pl; + + pl = &noproc_lineno; + while (*pl != (lineno_list_t *) NULL) + pl = &(*pl)->next; + *pl = list; + } + else + { + last_lineno = list; + *last_lineno_ptr = list; + last_lineno_ptr = &list->next; + } +} + +/* The MIPS assembler sometimes inserts nop instructions in the + instruction stream. When this happens, we must patch up the .loc + information so that it points to the instruction after the nop. */ + +void +ecoff_fix_loc (fragS *old_frag, unsigned long old_frag_offset) +{ + if (last_lineno != NULL + && last_lineno->frag == old_frag + && last_lineno->paddr == old_frag_offset) + { + last_lineno->frag = frag_now; + last_lineno->paddr = frag_now_fix (); + } +} + +/* Make sure the @stabs symbol is emitted. */ + +static void +mark_stabs (int ignore ATTRIBUTE_UNUSED) +{ + if (! stabs_seen) + { + /* Add a dummy @stabs dymbol. */ + stabs_seen = 1; + (void) add_ecoff_symbol (stabs_symbol, st_Nil, sc_Info, + (symbolS *) NULL, + (bfd_vma) 0, (symint_t) -1, + ECOFF_MARK_STAB (0)); + } +} + +/* Parse .weakext directives. */ +#ifndef TC_MIPS +/* For TC_MIPS use the version in tc-mips.c. */ +void +ecoff_directive_weakext (int ignore ATTRIBUTE_UNUSED) +{ + char *name; + int c; + symbolS *symbolP; + expressionS exp; + + name = input_line_pointer; + c = get_symbol_end (); + symbolP = symbol_find_or_make (name); + *input_line_pointer = c; + + SKIP_WHITESPACE (); + + if (*input_line_pointer == ',') + { + if (S_IS_DEFINED (symbolP)) + { + as_bad (_("symbol `%s' is already defined"), + S_GET_NAME (symbolP)); + ignore_rest_of_line (); + return; + } + + ++input_line_pointer; + SKIP_WHITESPACE (); + if (! is_end_of_line[(unsigned char) *input_line_pointer]) + { + expression (&exp); + if (exp.X_op != O_symbol) + { + as_bad (_("bad .weakext directive")); + ignore_rest_of_line (); + return; + } + symbol_set_value_expression (symbolP, &exp); + } + } + + S_SET_WEAK (symbolP); + + demand_empty_rest_of_line (); +} +#endif /* not TC_MIPS */ + +/* Handle .stabs directives. The actual parsing routine is done by a + generic routine. This routine is called via OBJ_PROCESS_STAB. + When this is called, input_line_pointer will be pointing at the + value field of the stab. + + .stabs directives have five fields: + "string" a string, encoding the type information. + code a numeric code, defined in + 0 a zero + desc a zero or line number + value a numeric value or an address. + + If the value is relocatable, we transform this into: + iss points as an index into string space + value value from lookup of the name + st st from lookup of the name + sc sc from lookup of the name + index code|CODE_MASK + + If the value is not relocatable, we transform this into: + iss points as an index into string space + value value + st st_Nil + sc sc_Nil + index code|CODE_MASK + + .stabn directives have four fields (string is null): + code a numeric code, defined in + 0 a zero + desc a zero or a line number + value a numeric value or an address. */ + +void +ecoff_stab (segT sec ATTRIBUTE_UNUSED, + int what, + const char *string, + int type, + int other, + int desc) +{ + efdr_t *save_file_ptr = cur_file_ptr; + symbolS *sym; + symint_t value; + bfd_vma addend; + st_t st; + sc_t sc; + symint_t indx; + localsym_t *hold = NULL; + + ecoff_debugging_seen = 1; + + /* We don't handle .stabd. */ + if (what != 's' && what != 'n') + { + as_bad (_(".stab%c is not supported"), what); + return; + } + + /* A .stabn uses a null name, not an empty string. */ + if (what == 'n') + string = NULL; + + /* We ignore the other field. */ + if (other != 0) + as_warn (_(".stab%c: ignoring non-zero other field"), what); + + /* Make sure we have a current file. */ + if (cur_file_ptr == (efdr_t *) NULL) + { + add_file ((const char *) NULL, 0, 1); + save_file_ptr = cur_file_ptr; + } + + /* For stabs in ECOFF, the first symbol must be @stabs. This is a + signal to gdb. */ + if (stabs_seen == 0) + mark_stabs (0); + + /* Line number stabs are handled differently, since they have two + values, the line number and the address of the label. We use the + index field (aka desc) to hold the line number, and the value + field to hold the address. The symbol type is st_Label, which + should be different from the other stabs, so that gdb can + recognize it. */ + if (type == N_SLINE) + { + SYMR dummy_symr; + char *name; + char name_end; + +#ifndef NO_LISTING + if (listing) + listing_source_line ((unsigned int) desc); +#endif + + dummy_symr.index = desc; + if (dummy_symr.index != desc) + { + as_warn (_("line number (%d) for .stab%c directive cannot fit in index field (20 bits)"), + desc, what); + return; + } + + name = input_line_pointer; + name_end = get_symbol_end (); + + sym = symbol_find_or_make (name); + *input_line_pointer = name_end; + + value = 0; + addend = 0; + st = st_Label; + sc = sc_Text; + indx = desc; + } + else + { +#ifndef NO_LISTING + if (listing && (type == N_SO || type == N_SOL)) + listing_source_file (string); +#endif + + if (ISDIGIT (*input_line_pointer) + || *input_line_pointer == '-' + || *input_line_pointer == '+') + { + st = st_Nil; + sc = sc_Nil; + sym = (symbolS *) NULL; + value = get_absolute_expression (); + addend = 0; + } + else if (! is_name_beginner ((unsigned char) *input_line_pointer)) + { + as_warn (_("illegal .stab%c directive, bad character"), what); + return; + } + else + { + expressionS exp; + + sc = sc_Nil; + st = st_Nil; + + expression (&exp); + if (exp.X_op == O_constant) + { + sym = NULL; + value = exp.X_add_number; + addend = 0; + } + else if (exp.X_op == O_symbol) + { + sym = exp.X_add_symbol; + value = 0; + addend = exp.X_add_number; + } + else + { + sym = make_expr_symbol (&exp); + value = 0; + addend = 0; + } + } + + indx = ECOFF_MARK_STAB (type); + } + + /* Don't store the stabs symbol we are creating as the type of the + ECOFF symbol. We want to compute the type of the ECOFF symbol + independently. */ + if (sym != (symbolS *) NULL) + hold = symbol_get_obj (sym)->ecoff_symbol; + + (void) add_ecoff_symbol (string, st, sc, sym, addend, value, indx); + + if (sym != (symbolS *) NULL) + symbol_get_obj (sym)->ecoff_symbol = hold; + + /* Restore normal file type. */ + cur_file_ptr = save_file_ptr; +} + +/* Frob an ECOFF symbol. Small common symbols go into a special + .scommon section rather than bfd_com_section. */ + +void +ecoff_frob_symbol (symbolS *sym) +{ + if (S_IS_COMMON (sym) + && S_GET_VALUE (sym) > 0 + && S_GET_VALUE (sym) <= bfd_get_gp_size (stdoutput)) + { + static asection scom_section; + static asymbol scom_symbol; + + /* We must construct a fake section similar to bfd_com_section + but with the name .scommon. */ + if (scom_section.name == NULL) + { + scom_section = *bfd_com_section_ptr; + scom_section.name = ".scommon"; + scom_section.output_section = &scom_section; + scom_section.symbol = &scom_symbol; + scom_section.symbol_ptr_ptr = &scom_section.symbol; + scom_symbol = *bfd_com_section_ptr->symbol; + scom_symbol.name = ".scommon"; + scom_symbol.section = &scom_section; + } + S_SET_SEGMENT (sym, &scom_section); + } + + /* Double check weak symbols. */ + if (S_IS_WEAK (sym)) + { + if (S_IS_COMMON (sym)) + as_bad (_("symbol `%s' can not be both weak and common"), + S_GET_NAME (sym)); + } +} + +/* Add bytes to the symbolic information buffer. */ + +static char * +ecoff_add_bytes (char **buf, + char **bufend, + char *bufptr, + unsigned long need) +{ + unsigned long at; + unsigned long want; + + at = bufptr - *buf; + need -= *bufend - bufptr; + if (need < PAGE_SIZE) + need = PAGE_SIZE; + want = (*bufend - *buf) + need; + *buf = (char *) xrealloc (*buf, want); + *bufend = *buf + want; + return *buf + at; +} + +/* Adjust the symbolic information buffer to the alignment required + for the ECOFF target debugging information. */ + +static unsigned long +ecoff_padding_adjust (const struct ecoff_debug_swap *backend, + char **buf, + char **bufend, + unsigned long offset, + char **bufptrptr) +{ + bfd_size_type align; + + align = backend->debug_align; + if ((offset & (align - 1)) != 0) + { + unsigned long add; + + add = align - (offset & (align - 1)); + if ((unsigned long) (*bufend - (*buf + offset)) < add) + (void) ecoff_add_bytes (buf, bufend, *buf + offset, add); + memset (*buf + offset, 0, add); + offset += add; + if (bufptrptr != (char **) NULL) + *bufptrptr = *buf + offset; + } + + return offset; +} + +/* Build the line number information. */ + +static unsigned long +ecoff_build_lineno (const struct ecoff_debug_swap *backend, + char **buf, + char **bufend, + unsigned long offset, + long *linecntptr) +{ + char *bufptr; + register lineno_list_t *l; + lineno_list_t *last; + efdr_t *file; + proc_t *proc; + unsigned long c; + long iline; + long totcount; + lineno_list_t first; + lineno_list_t *local_first_lineno = first_lineno; + + if (linecntptr != (long *) NULL) + *linecntptr = 0; + + bufptr = *buf + offset; + + file = (efdr_t *) NULL; + proc = (proc_t *) NULL; + last = (lineno_list_t *) NULL; + c = offset; + iline = 0; + totcount = 0; + + /* FIXME? Now that MIPS embedded-PIC is gone, it may be safe to + remove this code. */ + /* For some reason the address of the first procedure is ignored + when reading line numbers. This doesn't matter if the address of + the first procedure is 0, but when gcc is generating MIPS + embedded PIC code, it will put strings in the .text section + before the first procedure. We cope by inserting a dummy line if + the address of the first procedure is not 0. Hopefully this + won't screw things up too badly. + + Don't do this for ECOFF assembly source line numbers. They work + without this extra attention. */ + if (debug_type != DEBUG_ECOFF + && first_proc_ptr != (proc_t *) NULL + && local_first_lineno != (lineno_list_t *) NULL + && ((S_GET_VALUE (first_proc_ptr->sym->as_sym) + + bfd_get_section_vma (stdoutput, + S_GET_SEGMENT (first_proc_ptr->sym->as_sym))) + != 0)) + { + first.file = local_first_lineno->file; + first.proc = local_first_lineno->proc; + first.frag = &zero_address_frag; + first.paddr = 0; + first.lineno = 0; + + first.next = local_first_lineno; + local_first_lineno = &first; + } + + for (l = local_first_lineno; l != (lineno_list_t *) NULL; l = l->next) + { + long count; + long delta; + + /* Get the offset to the memory address of the next line number + (in words). Do this first, so that we can skip ahead to the + next useful line number entry. */ + if (l->next == (lineno_list_t *) NULL) + { + /* We want a count of zero, but it will be decremented + before it is used. */ + count = 1; + } + else if (l->next->frag->fr_address + l->next->paddr + > l->frag->fr_address + l->paddr) + { + count = ((l->next->frag->fr_address + l->next->paddr + - (l->frag->fr_address + l->paddr)) + >> 2); + } + else + { + /* Don't change last, so we still get the right delta. */ + continue; + } + + if (l->file != file || l->proc != proc) + { + if (l->proc != proc && proc != (proc_t *) NULL) + proc->pdr.lnHigh = last->lineno; + if (l->file != file && file != (efdr_t *) NULL) + { + file->fdr.cbLine = c - file->fdr.cbLineOffset; + file->fdr.cline = totcount + count; + if (linecntptr != (long *) NULL) + *linecntptr += totcount + count; + totcount = 0; + } + + if (l->file != file) + { + efdr_t *last_file = file; + + file = l->file; + if (last_file != (efdr_t *) NULL) + file->fdr.ilineBase + = last_file->fdr.ilineBase + last_file->fdr.cline; + else + file->fdr.ilineBase = 0; + file->fdr.cbLineOffset = c; + } + if (l->proc != proc) + { + proc = l->proc; + if (proc != (proc_t *) NULL) + { + proc->pdr.lnLow = l->lineno; + proc->pdr.cbLineOffset = c - file->fdr.cbLineOffset; + proc->pdr.iline = totcount; + } + } + + last = (lineno_list_t *) NULL; + } + + totcount += count; + + /* Get the offset to this line number. */ + if (last == (lineno_list_t *) NULL) + delta = 0; + else + delta = l->lineno - last->lineno; + + /* Put in the offset to this line number. */ + while (delta != 0) + { + int setcount; + + /* 1 is added to each count read. */ + --count; + /* We can only adjust the word count by up to 15 words at a + time. */ + if (count <= 0x0f) + { + setcount = count; + count = 0; + } + else + { + setcount = 0x0f; + count -= 0x0f; + } + if (delta >= -7 && delta <= 7) + { + if (bufptr >= *bufend) + bufptr = ecoff_add_bytes (buf, bufend, bufptr, (long) 1); + *bufptr++ = setcount + (delta << 4); + delta = 0; + ++c; + } + else + { + int set; + + if (*bufend - bufptr < 3) + bufptr = ecoff_add_bytes (buf, bufend, bufptr, (long) 3); + *bufptr++ = setcount + (8 << 4); + if (delta < -0x8000) + { + set = -0x8000; + delta += 0x8000; + } + else if (delta > 0x7fff) + { + set = 0x7fff; + delta -= 0x7fff; + } + else + { + set = delta; + delta = 0; + } + *bufptr++ = set >> 8; + *bufptr++ = set & 0xffff; + c += 3; + } + } + + /* Finish adjusting the count. */ + while (count > 0) + { + if (bufptr >= *bufend) + bufptr = ecoff_add_bytes (buf, bufend, bufptr, (long) 1); + /* 1 is added to each count read. */ + --count; + if (count > 0x0f) + { + *bufptr++ = 0x0f; + count -= 0x0f; + } + else + { + *bufptr++ = count; + count = 0; + } + ++c; + } + + ++iline; + last = l; + } + + if (proc != (proc_t *) NULL) + proc->pdr.lnHigh = last->lineno; + if (file != (efdr_t *) NULL) + { + file->fdr.cbLine = c - file->fdr.cbLineOffset; + file->fdr.cline = totcount; + } + + if (linecntptr != (long *) NULL) + *linecntptr += totcount; + + c = ecoff_padding_adjust (backend, buf, bufend, c, &bufptr); + + return c; +} + +/* Build and swap out the symbols. */ + +static unsigned long +ecoff_build_symbols (const struct ecoff_debug_swap *backend, + char **buf, + char **bufend, + unsigned long offset) +{ + const bfd_size_type external_sym_size = backend->external_sym_size; + void (* const swap_sym_out) (bfd *, const SYMR *, void *) + = backend->swap_sym_out; + char *sym_out; + long isym; + vlinks_t *file_link; + + sym_out = *buf + offset; + + isym = 0; + + /* The symbols are stored by file. */ + for (file_link = file_desc.first; + file_link != (vlinks_t *) NULL; + file_link = file_link->next) + { + int ifilesym; + int fil_cnt; + efdr_t *fil_ptr; + efdr_t *fil_end; + + if (file_link->next == (vlinks_t *) NULL) + fil_cnt = file_desc.objects_last_page; + else + fil_cnt = file_desc.objects_per_page; + fil_ptr = file_link->datum->file; + fil_end = fil_ptr + fil_cnt; + for (; fil_ptr < fil_end; fil_ptr++) + { + vlinks_t *sym_link; + + fil_ptr->fdr.isymBase = isym; + ifilesym = isym; + for (sym_link = fil_ptr->symbols.first; + sym_link != (vlinks_t *) NULL; + sym_link = sym_link->next) + { + int sym_cnt; + localsym_t *sym_ptr; + localsym_t *sym_end; + + if (sym_link->next == (vlinks_t *) NULL) + sym_cnt = fil_ptr->symbols.objects_last_page; + else + sym_cnt = fil_ptr->symbols.objects_per_page; + sym_ptr = sym_link->datum->sym; + sym_end = sym_ptr + sym_cnt; + for (; sym_ptr < sym_end; sym_ptr++) + { + int local; + symbolS *as_sym; + forward_t *f; + + know (sym_ptr->file_ptr == fil_ptr); + + /* If there is no associated gas symbol, then this + is a pure debugging symbol. We have already + added the name (if any) to fil_ptr->strings. + Otherwise we must decide whether this is an + external or a local symbol (actually, it may be + both if the local provides additional debugging + information for the external). */ + local = 1; + as_sym = sym_ptr->as_sym; + if (as_sym != (symbolS *) NULL) + { + symint_t indx; + + /* The value of a block start symbol is the + offset from the start of the procedure. For + other symbols we just use the gas value (but + we must offset it by the vma of the section, + just as BFD does, because BFD will not see + this value). */ + if (sym_ptr->ecoff_sym.asym.st == (int) st_Block + && sym_ptr->ecoff_sym.asym.sc == (int) sc_Text) + { + symbolS *begin_sym; + + know (sym_ptr->proc_ptr != (proc_t *) NULL); + begin_sym = sym_ptr->proc_ptr->sym->as_sym; + if (S_GET_SEGMENT (as_sym) + != S_GET_SEGMENT (begin_sym)) + as_warn (_(".begin/.bend in different segments")); + sym_ptr->ecoff_sym.asym.value = + S_GET_VALUE (as_sym) - S_GET_VALUE (begin_sym); + } + else + sym_ptr->ecoff_sym.asym.value = + (S_GET_VALUE (as_sym) + + bfd_get_section_vma (stdoutput, + S_GET_SEGMENT (as_sym)) + + sym_ptr->addend); + + sym_ptr->ecoff_sym.weakext = S_IS_WEAK (as_sym); + + /* Set st_Proc to st_StaticProc for local + functions. */ + if (sym_ptr->ecoff_sym.asym.st == st_Proc + && S_IS_DEFINED (as_sym) + && ! S_IS_EXTERNAL (as_sym) + && ! S_IS_WEAK (as_sym)) + sym_ptr->ecoff_sym.asym.st = st_StaticProc; + + /* Get the type and storage class based on where + the symbol actually wound up. Traditionally, + N_LBRAC and N_RBRAC are *not* relocated. */ + indx = sym_ptr->ecoff_sym.asym.index; + if (sym_ptr->ecoff_sym.asym.st == st_Nil + && sym_ptr->ecoff_sym.asym.sc == sc_Nil + && (! ECOFF_IS_STAB (&sym_ptr->ecoff_sym.asym) + || ((ECOFF_UNMARK_STAB (indx) != N_LBRAC) + && (ECOFF_UNMARK_STAB (indx) != N_RBRAC)))) + { + segT seg; + const char *segname; + st_t st; + sc_t sc; + + seg = S_GET_SEGMENT (as_sym); + segname = segment_name (seg); + + if (! ECOFF_IS_STAB (&sym_ptr->ecoff_sym.asym) + && (S_IS_EXTERNAL (as_sym) + || S_IS_WEAK (as_sym) + || ! S_IS_DEFINED (as_sym))) + { + if ((symbol_get_bfdsym (as_sym)->flags + & BSF_FUNCTION) != 0) + st = st_Proc; + else + st = st_Global; + } + else if (seg == text_section) + st = st_Label; + else + st = st_Static; + + if (! S_IS_DEFINED (as_sym)) + { + valueT s; + + s = symbol_get_obj (as_sym)->ecoff_extern_size; + if (s == 0 + || s > bfd_get_gp_size (stdoutput)) + sc = sc_Undefined; + else + { + sc = sc_SUndefined; + sym_ptr->ecoff_sym.asym.value = s; + } +#ifdef S_SET_SIZE + S_SET_SIZE (as_sym, s); +#endif + } + else if (S_IS_COMMON (as_sym)) + { + if (S_GET_VALUE (as_sym) > 0 + && (S_GET_VALUE (as_sym) + <= bfd_get_gp_size (stdoutput))) + sc = sc_SCommon; + else + sc = sc_Common; + } + else if (seg == text_section) + sc = sc_Text; + else if (seg == data_section) + sc = sc_Data; + else if (strcmp (segname, ".rdata") == 0 + || strcmp (segname, ".rodata") == 0) + sc = sc_RData; + else if (strcmp (segname, ".sdata") == 0) + sc = sc_SData; + else if (seg == bss_section) + sc = sc_Bss; + else if (strcmp (segname, ".sbss") == 0) + sc = sc_SBss; + else if (seg == bfd_abs_section_ptr) + sc = sc_Abs; + else + { + /* This must be a user named section. + This is not possible in ECOFF, but it + is in ELF. */ + sc = sc_Data; + } + + sym_ptr->ecoff_sym.asym.st = (int) st; + sym_ptr->ecoff_sym.asym.sc = (int) sc; + } + + /* This is just an external symbol if it is + outside a procedure and it has a type. + FIXME: g++ will generate symbols which have + different names in the debugging information + than the actual symbol. Should we handle + them here? */ + if ((S_IS_EXTERNAL (as_sym) + || S_IS_WEAK (as_sym) + || ! S_IS_DEFINED (as_sym)) + && sym_ptr->proc_ptr == (proc_t *) NULL + && sym_ptr->ecoff_sym.asym.st != (int) st_Nil + && ! ECOFF_IS_STAB (&sym_ptr->ecoff_sym.asym)) + local = 0; + + /* This is just an external symbol if it is a + common symbol. */ + if (S_IS_COMMON (as_sym)) + local = 0; + + /* If an st_end symbol has an associated gas + symbol, then it is a local label created for + a .bend or .end directive. Stabs line + numbers will have \001 in the names. */ + if (local + && sym_ptr->ecoff_sym.asym.st != st_End + && strchr (sym_ptr->name, '\001') == 0) + sym_ptr->ecoff_sym.asym.iss = + add_string (&fil_ptr->strings, + fil_ptr->str_hash, + sym_ptr->name, + (shash_t **) NULL); + } + + /* We now know the index of this symbol; fill in + locations that have been waiting for that + information. */ + if (sym_ptr->begin_ptr != (localsym_t *) NULL) + { + localsym_t *begin_ptr; + st_t begin_type; + + know (local); + begin_ptr = sym_ptr->begin_ptr; + know (begin_ptr->sym_index != -1); + sym_ptr->ecoff_sym.asym.index = begin_ptr->sym_index; + if (sym_ptr->ecoff_sym.asym.sc != (int) sc_Info) + sym_ptr->ecoff_sym.asym.iss = + begin_ptr->ecoff_sym.asym.iss; + + begin_type = (st_t) begin_ptr->ecoff_sym.asym.st; + if (begin_type == st_File + || begin_type == st_Block) + { + begin_ptr->ecoff_sym.asym.index = + isym - ifilesym + 1; + (*swap_sym_out) (stdoutput, + &begin_ptr->ecoff_sym.asym, + (*buf + + offset + + (begin_ptr->sym_index + * external_sym_size))); + } + else + { + know (begin_ptr->index_ptr != (aux_t *) NULL); + begin_ptr->index_ptr->data.isym = + isym - ifilesym + 1; + } + + /* The value of the symbol marking the end of a + procedure is the size of the procedure. The + value of the symbol marking the end of a + block is the offset from the start of the + procedure to the block. */ + if (begin_type == st_Proc + || begin_type == st_StaticProc) + { + know (as_sym != (symbolS *) NULL); + know (begin_ptr->as_sym != (symbolS *) NULL); + if (S_GET_SEGMENT (as_sym) + != S_GET_SEGMENT (begin_ptr->as_sym)) + as_warn (_(".begin/.bend in different segments")); + sym_ptr->ecoff_sym.asym.value = + (S_GET_VALUE (as_sym) + - S_GET_VALUE (begin_ptr->as_sym)); + + /* If the size is odd, this is probably a + mips16 function; force it to be even. */ + if ((sym_ptr->ecoff_sym.asym.value & 1) != 0) + ++sym_ptr->ecoff_sym.asym.value; + +#ifdef S_SET_SIZE + S_SET_SIZE (begin_ptr->as_sym, + sym_ptr->ecoff_sym.asym.value); +#endif + } + else if (begin_type == st_Block + && sym_ptr->ecoff_sym.asym.sc != (int) sc_Info) + { + symbolS *begin_sym; + + know (as_sym != (symbolS *) NULL); + know (sym_ptr->proc_ptr != (proc_t *) NULL); + begin_sym = sym_ptr->proc_ptr->sym->as_sym; + if (S_GET_SEGMENT (as_sym) + != S_GET_SEGMENT (begin_sym)) + as_warn (_(".begin/.bend in different segments")); + sym_ptr->ecoff_sym.asym.value = + S_GET_VALUE (as_sym) - S_GET_VALUE (begin_sym); + } + } + + for (f = sym_ptr->forward_ref; + f != (forward_t *) NULL; + f = f->next) + { + know (local); + f->ifd_ptr->data.isym = fil_ptr->file_index; + f->index_ptr->data.rndx.index = isym - ifilesym; + } + + if (local) + { + if ((bfd_size_type)(*bufend - sym_out) < external_sym_size) + sym_out = ecoff_add_bytes (buf, bufend, + sym_out, + external_sym_size); + (*swap_sym_out) (stdoutput, &sym_ptr->ecoff_sym.asym, + sym_out); + sym_out += external_sym_size; + + sym_ptr->sym_index = isym; + + if (sym_ptr->proc_ptr != (proc_t *) NULL + && sym_ptr->proc_ptr->sym == sym_ptr) + sym_ptr->proc_ptr->pdr.isym = isym - ifilesym; + + ++isym; + } + + /* Record the local symbol index and file number in + case this is an external symbol. Note that this + destroys the asym.index field. */ + if (as_sym != (symbolS *) NULL + && symbol_get_obj (as_sym)->ecoff_symbol == sym_ptr) + { + if ((sym_ptr->ecoff_sym.asym.st == st_Proc + || sym_ptr->ecoff_sym.asym.st == st_StaticProc) + && local) + sym_ptr->ecoff_sym.asym.index = isym - ifilesym - 1; + sym_ptr->ecoff_sym.ifd = fil_ptr->file_index; + + /* Don't try to merge an FDR which has an + external symbol attached to it. */ + if (S_IS_EXTERNAL (as_sym) || S_IS_WEAK (as_sym)) + fil_ptr->fdr.fMerge = 0; + } + } + } + fil_ptr->fdr.csym = isym - fil_ptr->fdr.isymBase; + } + } + + return offset + isym * external_sym_size; +} + +/* Swap out the procedure information. */ + +static unsigned long +ecoff_build_procs (const struct ecoff_debug_swap *backend, + char **buf, + char **bufend, + unsigned long offset) +{ + const bfd_size_type external_pdr_size = backend->external_pdr_size; + void (* const swap_pdr_out) (bfd *, const PDR *, void *) + = backend->swap_pdr_out; + char *pdr_out; + long iproc; + vlinks_t *file_link; + + pdr_out = *buf + offset; + + iproc = 0; + + /* The procedures are stored by file. */ + for (file_link = file_desc.first; + file_link != (vlinks_t *) NULL; + file_link = file_link->next) + { + int fil_cnt; + efdr_t *fil_ptr; + efdr_t *fil_end; + + if (file_link->next == (vlinks_t *) NULL) + fil_cnt = file_desc.objects_last_page; + else + fil_cnt = file_desc.objects_per_page; + fil_ptr = file_link->datum->file; + fil_end = fil_ptr + fil_cnt; + for (; fil_ptr < fil_end; fil_ptr++) + { + vlinks_t *proc_link; + int first; + + fil_ptr->fdr.ipdFirst = iproc; + first = 1; + for (proc_link = fil_ptr->procs.first; + proc_link != (vlinks_t *) NULL; + proc_link = proc_link->next) + { + int prc_cnt; + proc_t *proc_ptr; + proc_t *proc_end; + + if (proc_link->next == (vlinks_t *) NULL) + prc_cnt = fil_ptr->procs.objects_last_page; + else + prc_cnt = fil_ptr->procs.objects_per_page; + proc_ptr = proc_link->datum->proc; + proc_end = proc_ptr + prc_cnt; + for (; proc_ptr < proc_end; proc_ptr++) + { + symbolS *adr_sym; + unsigned long adr; + + adr_sym = proc_ptr->sym->as_sym; + adr = (S_GET_VALUE (adr_sym) + + bfd_get_section_vma (stdoutput, + S_GET_SEGMENT (adr_sym))); + if (first) + { + /* This code used to force the adr of the very + first fdr to be 0. However, the native tools + don't do that, and I can't remember why it + used to work that way, so I took it out. */ + fil_ptr->fdr.adr = adr; + first = 0; + } + proc_ptr->pdr.adr = adr - fil_ptr->fdr.adr; + if ((bfd_size_type)(*bufend - pdr_out) < external_pdr_size) + pdr_out = ecoff_add_bytes (buf, bufend, + pdr_out, + external_pdr_size); + (*swap_pdr_out) (stdoutput, &proc_ptr->pdr, pdr_out); + pdr_out += external_pdr_size; + ++iproc; + } + } + fil_ptr->fdr.cpd = iproc - fil_ptr->fdr.ipdFirst; + } + } + + return offset + iproc * external_pdr_size; +} + +/* Swap out the aux information. */ + +static unsigned long +ecoff_build_aux (const struct ecoff_debug_swap *backend, + char **buf, + char **bufend, + unsigned long offset) +{ + int bigendian; + union aux_ext *aux_out; + long iaux; + vlinks_t *file_link; + + bigendian = bfd_big_endian (stdoutput); + + aux_out = (union aux_ext *) (*buf + offset); + + iaux = 0; + + /* The aux entries are stored by file. */ + for (file_link = file_desc.first; + file_link != (vlinks_t *) NULL; + file_link = file_link->next) + { + int fil_cnt; + efdr_t *fil_ptr; + efdr_t *fil_end; + + if (file_link->next == (vlinks_t *) NULL) + fil_cnt = file_desc.objects_last_page; + else + fil_cnt = file_desc.objects_per_page; + fil_ptr = file_link->datum->file; + fil_end = fil_ptr + fil_cnt; + for (; fil_ptr < fil_end; fil_ptr++) + { + vlinks_t *aux_link; + + fil_ptr->fdr.fBigendian = bigendian; + fil_ptr->fdr.iauxBase = iaux; + for (aux_link = fil_ptr->aux_syms.first; + aux_link != (vlinks_t *) NULL; + aux_link = aux_link->next) + { + int aux_cnt; + aux_t *aux_ptr; + aux_t *aux_end; + + if (aux_link->next == (vlinks_t *) NULL) + aux_cnt = fil_ptr->aux_syms.objects_last_page; + else + aux_cnt = fil_ptr->aux_syms.objects_per_page; + aux_ptr = aux_link->datum->aux; + aux_end = aux_ptr + aux_cnt; + for (; aux_ptr < aux_end; aux_ptr++) + { + if ((unsigned long) (*bufend - (char *) aux_out) + < sizeof (union aux_ext)) + aux_out = ((union aux_ext *) + ecoff_add_bytes (buf, bufend, + (char *) aux_out, + sizeof (union aux_ext))); + switch (aux_ptr->type) + { + case aux_tir: + (*backend->swap_tir_out) (bigendian, + &aux_ptr->data.ti, + &aux_out->a_ti); + break; + case aux_rndx: + (*backend->swap_rndx_out) (bigendian, + &aux_ptr->data.rndx, + &aux_out->a_rndx); + break; + case aux_dnLow: + AUX_PUT_DNLOW (bigendian, aux_ptr->data.dnLow, + aux_out); + break; + case aux_dnHigh: + AUX_PUT_DNHIGH (bigendian, aux_ptr->data.dnHigh, + aux_out); + break; + case aux_isym: + AUX_PUT_ISYM (bigendian, aux_ptr->data.isym, + aux_out); + break; + case aux_iss: + AUX_PUT_ISS (bigendian, aux_ptr->data.iss, + aux_out); + break; + case aux_width: + AUX_PUT_WIDTH (bigendian, aux_ptr->data.width, + aux_out); + break; + case aux_count: + AUX_PUT_COUNT (bigendian, aux_ptr->data.count, + aux_out); + break; + } + + ++aux_out; + ++iaux; + } + } + fil_ptr->fdr.caux = iaux - fil_ptr->fdr.iauxBase; + } + } + + return ecoff_padding_adjust (backend, buf, bufend, + offset + iaux * sizeof (union aux_ext), + (char **) NULL); +} + +/* Copy out the strings from a varray_t. This returns the number of + bytes copied, rather than the new offset. */ + +static unsigned long +ecoff_build_strings (char **buf, + char **bufend, + unsigned long offset, + varray_t *vp) +{ + unsigned long istr; + char *str_out; + vlinks_t *str_link; + + str_out = *buf + offset; + + istr = 0; + + for (str_link = vp->first; + str_link != (vlinks_t *) NULL; + str_link = str_link->next) + { + unsigned long str_cnt; + + if (str_link->next == (vlinks_t *) NULL) + str_cnt = vp->objects_last_page; + else + str_cnt = vp->objects_per_page; + + if ((unsigned long)(*bufend - str_out) < str_cnt) + str_out = ecoff_add_bytes (buf, bufend, str_out, str_cnt); + + memcpy (str_out, str_link->datum->byte, str_cnt); + str_out += str_cnt; + istr += str_cnt; + } + + return istr; +} + +/* Dump out the local strings. */ + +static unsigned long +ecoff_build_ss (const struct ecoff_debug_swap *backend, + char **buf, + char **bufend, + unsigned long offset) +{ + long iss; + vlinks_t *file_link; + + iss = 0; + + for (file_link = file_desc.first; + file_link != (vlinks_t *) NULL; + file_link = file_link->next) + { + int fil_cnt; + efdr_t *fil_ptr; + efdr_t *fil_end; + + if (file_link->next == (vlinks_t *) NULL) + fil_cnt = file_desc.objects_last_page; + else + fil_cnt = file_desc.objects_per_page; + fil_ptr = file_link->datum->file; + fil_end = fil_ptr + fil_cnt; + for (; fil_ptr < fil_end; fil_ptr++) + { + long ss_cnt; + + fil_ptr->fdr.issBase = iss; + ss_cnt = ecoff_build_strings (buf, bufend, offset + iss, + &fil_ptr->strings); + fil_ptr->fdr.cbSs = ss_cnt; + iss += ss_cnt; + } + } + + return ecoff_padding_adjust (backend, buf, bufend, offset + iss, + (char **) NULL); +} + +/* Swap out the file descriptors. */ + +static unsigned long +ecoff_build_fdr (const struct ecoff_debug_swap *backend, + char **buf, + char **bufend, + unsigned long offset) +{ + const bfd_size_type external_fdr_size = backend->external_fdr_size; + void (* const swap_fdr_out) (bfd *, const FDR *, void *) + = backend->swap_fdr_out; + long ifile; + char *fdr_out; + vlinks_t *file_link; + + ifile = 0; + + fdr_out = *buf + offset; + + for (file_link = file_desc.first; + file_link != (vlinks_t *) NULL; + file_link = file_link->next) + { + int fil_cnt; + efdr_t *fil_ptr; + efdr_t *fil_end; + + if (file_link->next == (vlinks_t *) NULL) + fil_cnt = file_desc.objects_last_page; + else + fil_cnt = file_desc.objects_per_page; + fil_ptr = file_link->datum->file; + fil_end = fil_ptr + fil_cnt; + for (; fil_ptr < fil_end; fil_ptr++) + { + if ((bfd_size_type)(*bufend - fdr_out) < external_fdr_size) + fdr_out = ecoff_add_bytes (buf, bufend, fdr_out, + external_fdr_size); + (*swap_fdr_out) (stdoutput, &fil_ptr->fdr, fdr_out); + fdr_out += external_fdr_size; + ++ifile; + } + } + + return offset + ifile * external_fdr_size; +} + +/* Set up the external symbols. These are supposed to be handled by + the backend. This routine just gets the right information and + calls a backend function to deal with it. */ + +static void +ecoff_setup_ext (void) +{ + register symbolS *sym; + + for (sym = symbol_rootP; sym != (symbolS *) NULL; sym = symbol_next (sym)) + { + if (symbol_get_obj (sym)->ecoff_symbol == NULL) + continue; + + /* If this is a local symbol, then force the fields to zero. */ + if (! S_IS_EXTERNAL (sym) + && ! S_IS_WEAK (sym) + && S_IS_DEFINED (sym)) + { + struct localsym *lsym; + + lsym = symbol_get_obj (sym)->ecoff_symbol; + lsym->ecoff_sym.asym.value = 0; + lsym->ecoff_sym.asym.st = (int) st_Nil; + lsym->ecoff_sym.asym.sc = (int) sc_Nil; + lsym->ecoff_sym.asym.index = indexNil; + } + + obj_ecoff_set_ext (sym, &symbol_get_obj (sym)->ecoff_symbol->ecoff_sym); + } +} + +/* Build the ECOFF debugging information. */ + +unsigned long +ecoff_build_debug (HDRR *hdr, + char **bufp, + const struct ecoff_debug_swap *backend) +{ + const bfd_size_type external_pdr_size = backend->external_pdr_size; + tag_t *ptag; + tag_t *ptag_next; + efdr_t *fil_ptr; + int end_warning; + efdr_t *hold_file_ptr; + proc_t *hold_proc_ptr; + symbolS *sym; + char *buf; + char *bufend; + unsigned long offset; + + /* Make sure we have a file. */ + if (first_file == (efdr_t *) NULL) + add_file ((const char *) NULL, 0, 1); + + /* Handle any top level tags. */ + for (ptag = top_tag_head->first_tag; + ptag != (tag_t *) NULL; + ptag = ptag_next) + { + if (ptag->forward_ref != (forward_t *) NULL) + add_unknown_tag (ptag); + + ptag_next = ptag->same_block; + ptag->hash_ptr->tag_ptr = ptag->same_name; + free_tag (ptag); + } + + free_thead (top_tag_head); + + /* Look through the symbols. Add debugging information for each + symbol that has not already received it. */ + hold_file_ptr = cur_file_ptr; + hold_proc_ptr = cur_proc_ptr; + cur_proc_ptr = (proc_t *) NULL; + for (sym = symbol_rootP; sym != (symbolS *) NULL; sym = symbol_next (sym)) + { + if (symbol_get_obj (sym)->ecoff_symbol != NULL + || symbol_get_obj (sym)->ecoff_file == (efdr_t *) NULL + || (symbol_get_bfdsym (sym)->flags & BSF_SECTION_SYM) != 0) + continue; + + cur_file_ptr = symbol_get_obj (sym)->ecoff_file; + add_ecoff_symbol ((const char *) NULL, st_Nil, sc_Nil, sym, + (bfd_vma) 0, S_GET_VALUE (sym), indexNil); + } + cur_proc_ptr = hold_proc_ptr; + cur_file_ptr = hold_file_ptr; + + /* Output an ending symbol for all the files. We have to do this + here for the last file, so we may as well do it for all of the + files. */ + end_warning = 0; + for (fil_ptr = first_file; + fil_ptr != (efdr_t *) NULL; + fil_ptr = fil_ptr->next_file) + { + cur_file_ptr = fil_ptr; + while (cur_file_ptr->cur_scope != (scope_t *) NULL + && cur_file_ptr->cur_scope->prev != (scope_t *) NULL) + { + cur_file_ptr->cur_scope = cur_file_ptr->cur_scope->prev; + if (! end_warning && ! cur_file_ptr->fake) + { + as_warn (_("missing .end or .bend at end of file")); + end_warning = 1; + } + } + if (cur_file_ptr->cur_scope != (scope_t *) NULL) + (void) add_ecoff_symbol ((const char *) NULL, + st_End, sc_Text, + (symbolS *) NULL, + (bfd_vma) 0, + (symint_t) 0, + (symint_t) 0); + } + + /* Build the symbolic information. */ + offset = 0; + buf = (char *) xmalloc (PAGE_SIZE); + bufend = buf + PAGE_SIZE; + + /* Build the line number information. */ + hdr->cbLineOffset = offset; + offset = ecoff_build_lineno (backend, &buf, &bufend, offset, + &hdr->ilineMax); + hdr->cbLine = offset - hdr->cbLineOffset; + + /* We don't use dense numbers at all. */ + hdr->idnMax = 0; + hdr->cbDnOffset = 0; + + /* We can't build the PDR table until we have built the symbols, + because a PDR contains a symbol index. However, we set aside + space at this point. */ + hdr->ipdMax = proc_cnt; + hdr->cbPdOffset = offset; + if ((bfd_size_type)(bufend - (buf + offset)) < proc_cnt * external_pdr_size) + (void) ecoff_add_bytes (&buf, &bufend, buf + offset, + proc_cnt * external_pdr_size); + offset += proc_cnt * external_pdr_size; + + /* Build the local symbols. */ + hdr->cbSymOffset = offset; + offset = ecoff_build_symbols (backend, &buf, &bufend, offset); + hdr->isymMax = (offset - hdr->cbSymOffset) / backend->external_sym_size; + + /* Building the symbols initializes the symbol index in the PDR's. + Now we can swap out the PDR's. */ + (void) ecoff_build_procs (backend, &buf, &bufend, hdr->cbPdOffset); + + /* We don't use optimization symbols. */ + hdr->ioptMax = 0; + hdr->cbOptOffset = 0; + + /* Swap out the auxiliary type information. */ + hdr->cbAuxOffset = offset; + offset = ecoff_build_aux (backend, &buf, &bufend, offset); + hdr->iauxMax = (offset - hdr->cbAuxOffset) / sizeof (union aux_ext); + + /* Copy out the local strings. */ + hdr->cbSsOffset = offset; + offset = ecoff_build_ss (backend, &buf, &bufend, offset); + hdr->issMax = offset - hdr->cbSsOffset; + + /* We don't use relative file descriptors. */ + hdr->crfd = 0; + hdr->cbRfdOffset = 0; + + /* Swap out the file descriptors. */ + hdr->cbFdOffset = offset; + offset = ecoff_build_fdr (backend, &buf, &bufend, offset); + hdr->ifdMax = (offset - hdr->cbFdOffset) / backend->external_fdr_size; + + /* Set up the external symbols, which are handled by the BFD back + end. */ + hdr->issExtMax = 0; + hdr->cbSsExtOffset = 0; + hdr->iextMax = 0; + hdr->cbExtOffset = 0; + ecoff_setup_ext (); + + know ((offset & (backend->debug_align - 1)) == 0); + + /* FIXME: This value should be determined from the .verstamp directive, + with reasonable defaults in config files. */ +#ifdef TC_ALPHA + hdr->vstamp = 0x030b; +#else + hdr->vstamp = 0x020b; +#endif + + *bufp = buf; + return offset; +} + +/* Allocate a cluster of pages. */ + +#ifndef MALLOC_CHECK + +static page_type * +allocate_cluster (unsigned long npages) +{ + register page_type *value = (page_type *) xmalloc (npages * PAGE_USIZE); + +#ifdef ECOFF_DEBUG + if (debug > 3) + fprintf (stderr, "\talloc\tnpages = %d, value = 0x%.8x\n", npages, value); +#endif + + memset (value, 0, npages * PAGE_USIZE); + + return value; +} + +static page_type *cluster_ptr = NULL; +static unsigned long pages_left = 0; + +#endif /* MALLOC_CHECK */ + +/* Allocate one page (which is initialized to 0). */ + +static page_type * +allocate_page (void) +{ +#ifndef MALLOC_CHECK + + if (pages_left == 0) + { + pages_left = MAX_CLUSTER_PAGES; + cluster_ptr = allocate_cluster (pages_left); + } + + pages_left--; + return cluster_ptr++; + +#else /* MALLOC_CHECK */ + + page_type *ptr; + + ptr = xmalloc (PAGE_USIZE); + memset (ptr, 0, PAGE_USIZE); + return ptr; + +#endif /* MALLOC_CHECK */ +} + +/* Allocate scoping information. */ + +static scope_t * +allocate_scope (void) +{ + register scope_t *ptr; + static scope_t initial_scope; + +#ifndef MALLOC_CHECK + + ptr = alloc_counts[(int) alloc_type_scope].free_list.f_scope; + if (ptr != (scope_t *) NULL) + alloc_counts[(int) alloc_type_scope].free_list.f_scope = ptr->free; + else + { + register int unallocated = alloc_counts[(int) alloc_type_scope].unallocated; + register page_type *cur_page = alloc_counts[(int) alloc_type_scope].cur_page; + + if (unallocated == 0) + { + unallocated = PAGE_SIZE / sizeof (scope_t); + alloc_counts[(int) alloc_type_scope].cur_page = cur_page = allocate_page (); + alloc_counts[(int) alloc_type_scope].total_pages++; + } + + ptr = &cur_page->scope[--unallocated]; + alloc_counts[(int) alloc_type_scope].unallocated = unallocated; + } + +#else + + ptr = (scope_t *) xmalloc (sizeof (scope_t)); + +#endif + + alloc_counts[(int) alloc_type_scope].total_alloc++; + *ptr = initial_scope; + return ptr; +} + +/* Free scoping information. */ + +static void +free_scope (scope_t *ptr) +{ + alloc_counts[(int) alloc_type_scope].total_free++; + +#ifndef MALLOC_CHECK + ptr->free = alloc_counts[(int) alloc_type_scope].free_list.f_scope; + alloc_counts[(int) alloc_type_scope].free_list.f_scope = ptr; +#else + free ((void *) ptr); +#endif +} + +/* Allocate links for pages in a virtual array. */ + +static vlinks_t * +allocate_vlinks (void) +{ + register vlinks_t *ptr; + static vlinks_t initial_vlinks; + +#ifndef MALLOC_CHECK + + register int unallocated = alloc_counts[(int) alloc_type_vlinks].unallocated; + register page_type *cur_page = alloc_counts[(int) alloc_type_vlinks].cur_page; + + if (unallocated == 0) + { + unallocated = PAGE_SIZE / sizeof (vlinks_t); + alloc_counts[(int) alloc_type_vlinks].cur_page = cur_page = allocate_page (); + alloc_counts[(int) alloc_type_vlinks].total_pages++; + } + + ptr = &cur_page->vlinks[--unallocated]; + alloc_counts[(int) alloc_type_vlinks].unallocated = unallocated; + +#else + + ptr = (vlinks_t *) xmalloc (sizeof (vlinks_t)); + +#endif + + alloc_counts[(int) alloc_type_vlinks].total_alloc++; + *ptr = initial_vlinks; + return ptr; +} + +/* Allocate string hash buckets. */ + +static shash_t * +allocate_shash (void) +{ + register shash_t *ptr; + static shash_t initial_shash; + +#ifndef MALLOC_CHECK + + register int unallocated = alloc_counts[(int) alloc_type_shash].unallocated; + register page_type *cur_page = alloc_counts[(int) alloc_type_shash].cur_page; + + if (unallocated == 0) + { + unallocated = PAGE_SIZE / sizeof (shash_t); + alloc_counts[(int) alloc_type_shash].cur_page = cur_page = allocate_page (); + alloc_counts[(int) alloc_type_shash].total_pages++; + } + + ptr = &cur_page->shash[--unallocated]; + alloc_counts[(int) alloc_type_shash].unallocated = unallocated; + +#else + + ptr = (shash_t *) xmalloc (sizeof (shash_t)); + +#endif + + alloc_counts[(int) alloc_type_shash].total_alloc++; + *ptr = initial_shash; + return ptr; +} + +/* Allocate type hash buckets. */ + +static thash_t * +allocate_thash (void) +{ + register thash_t *ptr; + static thash_t initial_thash; + +#ifndef MALLOC_CHECK + + register int unallocated = alloc_counts[(int) alloc_type_thash].unallocated; + register page_type *cur_page = alloc_counts[(int) alloc_type_thash].cur_page; + + if (unallocated == 0) + { + unallocated = PAGE_SIZE / sizeof (thash_t); + alloc_counts[(int) alloc_type_thash].cur_page = cur_page = allocate_page (); + alloc_counts[(int) alloc_type_thash].total_pages++; + } + + ptr = &cur_page->thash[--unallocated]; + alloc_counts[(int) alloc_type_thash].unallocated = unallocated; + +#else + + ptr = (thash_t *) xmalloc (sizeof (thash_t)); + +#endif + + alloc_counts[(int) alloc_type_thash].total_alloc++; + *ptr = initial_thash; + return ptr; +} + +/* Allocate structure, union, or enum tag information. */ + +static tag_t * +allocate_tag (void) +{ + register tag_t *ptr; + static tag_t initial_tag; + +#ifndef MALLOC_CHECK + + ptr = alloc_counts[(int) alloc_type_tag].free_list.f_tag; + if (ptr != (tag_t *) NULL) + alloc_counts[(int) alloc_type_tag].free_list.f_tag = ptr->free; + else + { + register int unallocated = alloc_counts[(int) alloc_type_tag].unallocated; + register page_type *cur_page = alloc_counts[(int) alloc_type_tag].cur_page; + + if (unallocated == 0) + { + unallocated = PAGE_SIZE / sizeof (tag_t); + alloc_counts[(int) alloc_type_tag].cur_page = cur_page = allocate_page (); + alloc_counts[(int) alloc_type_tag].total_pages++; + } + + ptr = &cur_page->tag[--unallocated]; + alloc_counts[(int) alloc_type_tag].unallocated = unallocated; + } + +#else + + ptr = (tag_t *) xmalloc (sizeof (tag_t)); + +#endif + + alloc_counts[(int) alloc_type_tag].total_alloc++; + *ptr = initial_tag; + return ptr; +} + +/* Free scoping information. */ + +static void +free_tag (tag_t *ptr) +{ + alloc_counts[(int) alloc_type_tag].total_free++; + +#ifndef MALLOC_CHECK + ptr->free = alloc_counts[(int) alloc_type_tag].free_list.f_tag; + alloc_counts[(int) alloc_type_tag].free_list.f_tag = ptr; +#else + free ((PTR_T) ptr); +#endif +} + +/* Allocate forward reference to a yet unknown tag. */ + +static forward_t * +allocate_forward (void) +{ + register forward_t *ptr; + static forward_t initial_forward; + +#ifndef MALLOC_CHECK + + register int unallocated = alloc_counts[(int) alloc_type_forward].unallocated; + register page_type *cur_page = alloc_counts[(int) alloc_type_forward].cur_page; + + if (unallocated == 0) + { + unallocated = PAGE_SIZE / sizeof (forward_t); + alloc_counts[(int) alloc_type_forward].cur_page = cur_page = allocate_page (); + alloc_counts[(int) alloc_type_forward].total_pages++; + } + + ptr = &cur_page->forward[--unallocated]; + alloc_counts[(int) alloc_type_forward].unallocated = unallocated; + +#else + + ptr = (forward_t *) xmalloc (sizeof (forward_t)); + +#endif + + alloc_counts[(int) alloc_type_forward].total_alloc++; + *ptr = initial_forward; + return ptr; +} + +/* Allocate head of type hash list. */ + +static thead_t * +allocate_thead (void) +{ + register thead_t *ptr; + static thead_t initial_thead; + +#ifndef MALLOC_CHECK + + ptr = alloc_counts[(int) alloc_type_thead].free_list.f_thead; + if (ptr != (thead_t *) NULL) + alloc_counts[(int) alloc_type_thead].free_list.f_thead = ptr->free; + else + { + register int unallocated = alloc_counts[(int) alloc_type_thead].unallocated; + register page_type *cur_page = alloc_counts[(int) alloc_type_thead].cur_page; + + if (unallocated == 0) + { + unallocated = PAGE_SIZE / sizeof (thead_t); + alloc_counts[(int) alloc_type_thead].cur_page = cur_page = allocate_page (); + alloc_counts[(int) alloc_type_thead].total_pages++; + } + + ptr = &cur_page->thead[--unallocated]; + alloc_counts[(int) alloc_type_thead].unallocated = unallocated; + } + +#else + + ptr = (thead_t *) xmalloc (sizeof (thead_t)); + +#endif + + alloc_counts[(int) alloc_type_thead].total_alloc++; + *ptr = initial_thead; + return ptr; +} + +/* Free scoping information. */ + +static void +free_thead (thead_t *ptr) +{ + alloc_counts[(int) alloc_type_thead].total_free++; + +#ifndef MALLOC_CHECK + ptr->free = (thead_t *) alloc_counts[(int) alloc_type_thead].free_list.f_thead; + alloc_counts[(int) alloc_type_thead].free_list.f_thead = ptr; +#else + free ((PTR_T) ptr); +#endif +} + +static lineno_list_t * +allocate_lineno_list (void) +{ + register lineno_list_t *ptr; + static lineno_list_t initial_lineno_list; + +#ifndef MALLOC_CHECK + + register int unallocated = alloc_counts[(int) alloc_type_lineno].unallocated; + register page_type *cur_page = alloc_counts[(int) alloc_type_lineno].cur_page; + + if (unallocated == 0) + { + unallocated = PAGE_SIZE / sizeof (lineno_list_t); + alloc_counts[(int) alloc_type_lineno].cur_page = cur_page = allocate_page (); + alloc_counts[(int) alloc_type_lineno].total_pages++; + } + + ptr = &cur_page->lineno[--unallocated]; + alloc_counts[(int) alloc_type_lineno].unallocated = unallocated; + +#else + + ptr = (lineno_list_t *) xmalloc (sizeof (lineno_list_t)); + +#endif + + alloc_counts[(int) alloc_type_lineno].total_alloc++; + *ptr = initial_lineno_list; + return ptr; +} + +void +ecoff_set_gp_prolog_size (int sz) +{ + if (cur_proc_ptr == 0) + return; + + cur_proc_ptr->pdr.gp_prologue = sz; + if (cur_proc_ptr->pdr.gp_prologue != sz) + { + as_warn (_("GP prologue size exceeds field size, using 0 instead")); + cur_proc_ptr->pdr.gp_prologue = 0; + } + + cur_proc_ptr->pdr.gp_used = 1; +} + +int +ecoff_no_current_file (void) +{ + return cur_file_ptr == (efdr_t *) NULL; +} + +void +ecoff_generate_asm_lineno (void) +{ + unsigned int lineno; + char *filename; + lineno_list_t *list; + + as_where (&filename, &lineno); + + if (current_stabs_filename == (char *) NULL + || filename_cmp (current_stabs_filename, filename)) + add_file (filename, 0, 1); + + list = allocate_lineno_list (); + + list->next = (lineno_list_t *) NULL; + list->file = cur_file_ptr; + list->proc = cur_proc_ptr; + list->frag = frag_now; + list->paddr = frag_now_fix (); + list->lineno = lineno; + + /* We don't want to merge files which have line numbers. */ + cur_file_ptr->fdr.fMerge = 0; + + /* A .loc directive will sometimes appear before a .ent directive, + which means that cur_proc_ptr will be NULL here. Arrange to + patch this up. */ + if (cur_proc_ptr == (proc_t *) NULL) + { + lineno_list_t **pl; + + pl = &noproc_lineno; + while (*pl != (lineno_list_t *) NULL) + pl = &(*pl)->next; + *pl = list; + } + else + { + last_lineno = list; + *last_lineno_ptr = list; + last_lineno_ptr = &list->next; + } +} + +#else + +void +ecoff_generate_asm_lineno (void) +{ +} + +#endif /* ECOFF_DEBUGGING */ diff --git a/contrib/toolchain/binutils/gas/ecoff.h b/contrib/toolchain/binutils/gas/ecoff.h new file mode 100644 index 0000000000..f4f8356147 --- /dev/null +++ b/contrib/toolchain/binutils/gas/ecoff.h @@ -0,0 +1,113 @@ +/* ecoff.h -- header file for ECOFF debugging support + Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2003, 2004, 2005, + 2007, 2009 Free Software Foundation, Inc. + Contributed by Cygnus Support. + Put together by Ian Lance Taylor . + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef GAS_ECOFF_H +#define GAS_ECOFF_H + +#ifdef ECOFF_DEBUGGING + +#include "coff/sym.h" +#include "coff/ecoff.h" + +/* Whether we have seen any ECOFF debugging information. */ +extern int ecoff_debugging_seen; + +/* This function should be called at the start of assembly, by + obj_read_begin_hook. */ +extern void ecoff_read_begin_hook (void); + +/* This function should be called when the assembler switches to a new + file. */ +extern void ecoff_new_file (const char *, int); + +/* This function should be called when a new symbol is created, by + obj_symbol_new_hook. */ +extern void ecoff_symbol_new_hook (symbolS *); + +extern void ecoff_symbol_clone_hook (symbolS *, symbolS *); + +/* This function should be called by the obj_frob_symbol hook. */ +extern void ecoff_frob_symbol (symbolS *); + +/* Build the ECOFF debugging information. This should be called by + obj_frob_file. This fills in the counts in *HDR; the offsets are + filled in relative to the start of the *BUFP. It sets *BUFP to a + block of memory holding the debugging information. It returns the + length of *BUFP. */ +extern unsigned long ecoff_build_debug + (HDRR *hdr, char **bufp, const struct ecoff_debug_swap *); + +/* Functions to handle the ECOFF debugging directives. */ +extern void ecoff_directive_begin (int); +extern void ecoff_directive_bend (int); +extern void ecoff_directive_end (int); +extern void ecoff_directive_ent (int); +extern void ecoff_directive_fmask (int); +extern void ecoff_directive_frame (int); +extern void ecoff_directive_loc (int); +extern void ecoff_directive_mask (int); + +/* Other ECOFF directives. */ +extern void ecoff_directive_extern (int); +extern void ecoff_directive_weakext (int); + +/* Functions to handle the COFF debugging directives. */ +extern void ecoff_directive_def (int); +extern void ecoff_directive_dim (int); +extern void ecoff_directive_endef (int); +extern void ecoff_directive_file (int); +extern void ecoff_directive_scl (int); +extern void ecoff_directive_size (int); +extern void ecoff_directive_tag (int); +extern void ecoff_directive_type (int); +extern void ecoff_directive_val (int); + +/* Handle stabs. */ +extern void ecoff_stab (segT sec, int what, const char *string, + int type, int other, int desc); + +/* Set the GP prologue size. */ +extern void ecoff_set_gp_prolog_size (int sz); + +/* This routine is called from the ECOFF code to set the external + information for a symbol. */ +#ifndef obj_ecoff_set_ext +extern void obj_ecoff_set_ext (symbolS *, EXTR *); +#endif + +/* This routine is used to patch up a line number directive when + instructions are moved around. */ +extern void ecoff_fix_loc (fragS *, unsigned long); + +/* This function is called from read.c to peek at cur_file_ptr. */ +extern int ecoff_no_current_file (void); + +/* This function returns the symbol associated with the current proc. */ +extern symbolS *ecoff_get_cur_proc_sym (void); + +#endif /* ECOFF_DEBUGGING */ + +/* This routine is called from read.c to generate line number for .s file. */ +extern void ecoff_generate_asm_lineno (void); + +#endif /* ! GAS_ECOFF_H */ diff --git a/contrib/toolchain/binutils/gas/ehopt.c b/contrib/toolchain/binutils/gas/ehopt.c new file mode 100644 index 0000000000..70e1a00f4e --- /dev/null +++ b/contrib/toolchain/binutils/gas/ehopt.c @@ -0,0 +1,557 @@ +/* ehopt.c--optimize gcc exception frame information. + Copyright 1998, 2000, 2001, 2003, 2005, 2007, 2008, 2009 + Free Software Foundation, Inc. + Written by Ian Lance Taylor . + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" +#include "subsegs.h" +#include "struc-symbol.h" + +/* We include this ELF file, even though we may not be assembling for + ELF, since the exception frame information is always in a format + derived from DWARF. */ + +#include "dwarf2.h" + +/* Try to optimize gcc 2.8 exception frame information. + + Exception frame information is emitted for every function in the + .eh_frame or .debug_frame sections. Simple information for a function + with no exceptions looks like this: + +__FRAME_BEGIN__: + .4byte .LLCIE1 / Length of Common Information Entry +.LSCIE1: +#if .eh_frame + .4byte 0x0 / CIE Identifier Tag +#elif .debug_frame + .4byte 0xffffffff / CIE Identifier Tag +#endif + .byte 0x1 / CIE Version + .byte 0x0 / CIE Augmentation (none) + .byte 0x1 / ULEB128 0x1 (CIE Code Alignment Factor) + .byte 0x7c / SLEB128 -4 (CIE Data Alignment Factor) + .byte 0x8 / CIE RA Column + .byte 0xc / DW_CFA_def_cfa + .byte 0x4 / ULEB128 0x4 + .byte 0x4 / ULEB128 0x4 + .byte 0x88 / DW_CFA_offset, column 0x8 + .byte 0x1 / ULEB128 0x1 + .align 4 +.LECIE1: + .set .LLCIE1,.LECIE1-.LSCIE1 / CIE Length Symbol + .4byte .LLFDE1 / FDE Length +.LSFDE1: + .4byte .LSFDE1-__FRAME_BEGIN__ / FDE CIE offset + .4byte .LFB1 / FDE initial location + .4byte .LFE1-.LFB1 / FDE address range + .byte 0x4 / DW_CFA_advance_loc4 + .4byte .LCFI0-.LFB1 + .byte 0xe / DW_CFA_def_cfa_offset + .byte 0x8 / ULEB128 0x8 + .byte 0x85 / DW_CFA_offset, column 0x5 + .byte 0x2 / ULEB128 0x2 + .byte 0x4 / DW_CFA_advance_loc4 + .4byte .LCFI1-.LCFI0 + .byte 0xd / DW_CFA_def_cfa_register + .byte 0x5 / ULEB128 0x5 + .byte 0x4 / DW_CFA_advance_loc4 + .4byte .LCFI2-.LCFI1 + .byte 0x2e / DW_CFA_GNU_args_size + .byte 0x4 / ULEB128 0x4 + .byte 0x4 / DW_CFA_advance_loc4 + .4byte .LCFI3-.LCFI2 + .byte 0x2e / DW_CFA_GNU_args_size + .byte 0x0 / ULEB128 0x0 + .align 4 +.LEFDE1: + .set .LLFDE1,.LEFDE1-.LSFDE1 / FDE Length Symbol + + The immediate issue we can address in the assembler is the + DW_CFA_advance_loc4 followed by a four byte value. The value is + the difference of two addresses in the function. Since gcc does + not know this value, it always uses four bytes. We will know the + value at the end of assembly, so we can do better. */ + +struct cie_info +{ + unsigned code_alignment; + int z_augmentation; +}; + +static int get_cie_info (struct cie_info *); + +/* Extract information from the CIE. */ + +static int +get_cie_info (struct cie_info *info) +{ + fragS *f; + fixS *fix; + int offset; + char CIE_id; + char augmentation[10]; + int iaug; + int code_alignment = 0; + + /* We should find the CIE at the start of the section. */ + + f = seg_info (now_seg)->frchainP->frch_root; + fix = seg_info (now_seg)->frchainP->fix_root; + + /* Look through the frags of the section to find the code alignment. */ + + /* First make sure that the CIE Identifier Tag is 0/-1. */ + + if (strncmp (segment_name (now_seg), ".debug_frame", 12) == 0) + CIE_id = (char)0xff; + else + CIE_id = 0; + + offset = 4; + while (f != NULL && offset >= f->fr_fix) + { + offset -= f->fr_fix; + f = f->fr_next; + } + if (f == NULL + || f->fr_fix - offset < 4 + || f->fr_literal[offset] != CIE_id + || f->fr_literal[offset + 1] != CIE_id + || f->fr_literal[offset + 2] != CIE_id + || f->fr_literal[offset + 3] != CIE_id) + return 0; + + /* Next make sure the CIE version number is 1. */ + + offset += 4; + while (f != NULL && offset >= f->fr_fix) + { + offset -= f->fr_fix; + f = f->fr_next; + } + if (f == NULL + || f->fr_fix - offset < 1 + || f->fr_literal[offset] != 1) + return 0; + + /* Skip the augmentation (a null terminated string). */ + + iaug = 0; + ++offset; + while (1) + { + while (f != NULL && offset >= f->fr_fix) + { + offset -= f->fr_fix; + f = f->fr_next; + } + if (f == NULL) + return 0; + + while (offset < f->fr_fix && f->fr_literal[offset] != '\0') + { + if ((size_t) iaug < (sizeof augmentation) - 1) + { + augmentation[iaug] = f->fr_literal[offset]; + ++iaug; + } + ++offset; + } + if (offset < f->fr_fix) + break; + } + ++offset; + while (f != NULL && offset >= f->fr_fix) + { + offset -= f->fr_fix; + f = f->fr_next; + } + if (f == NULL) + return 0; + + augmentation[iaug] = '\0'; + if (augmentation[0] == '\0') + { + /* No augmentation. */ + } + else if (strcmp (augmentation, "eh") == 0) + { + /* We have to skip a pointer. Unfortunately, we don't know how + large it is. We find out by looking for a matching fixup. */ + while (fix != NULL + && (fix->fx_frag != f || fix->fx_where != offset)) + fix = fix->fx_next; + if (fix == NULL) + offset += 4; + else + offset += fix->fx_size; + while (f != NULL && offset >= f->fr_fix) + { + offset -= f->fr_fix; + f = f->fr_next; + } + if (f == NULL) + return 0; + } + else if (augmentation[0] != 'z') + return 0; + + /* We're now at the code alignment factor, which is a ULEB128. If + it isn't a single byte, forget it. */ + + code_alignment = f->fr_literal[offset] & 0xff; + if ((code_alignment & 0x80) != 0) + code_alignment = 0; + + info->code_alignment = code_alignment; + info->z_augmentation = (augmentation[0] == 'z'); + + return 1; +} + +enum frame_state +{ + state_idle, + state_saw_size, + state_saw_cie_offset, + state_saw_pc_begin, + state_seeing_aug_size, + state_skipping_aug, + state_wait_loc4, + state_saw_loc4, + state_error, +}; + +/* This function is called from emit_expr. It looks for cases which + we can optimize. + + Rather than try to parse all this information as we read it, we + look for a single byte DW_CFA_advance_loc4 followed by a 4 byte + difference. We turn that into a rs_cfa_advance frag, and handle + those frags at the end of the assembly. If the gcc output changes + somewhat, this optimization may stop working. + + This function returns non-zero if it handled the expression and + emit_expr should not do anything, or zero otherwise. It can also + change *EXP and *PNBYTES. */ + +int +check_eh_frame (expressionS *exp, unsigned int *pnbytes) +{ + struct frame_data + { + enum frame_state state; + + int cie_info_ok; + struct cie_info cie_info; + + symbolS *size_end_sym; + fragS *loc4_frag; + int loc4_fix; + + int aug_size; + int aug_shift; + }; + + static struct frame_data eh_frame_data; + static struct frame_data debug_frame_data; + struct frame_data *d; + + /* Don't optimize. */ + if (flag_traditional_format) + return 0; + +#ifdef md_allow_eh_opt + if (! md_allow_eh_opt) + return 0; +#endif + + /* Select the proper section data. */ + if (strncmp (segment_name (now_seg), ".eh_frame", 9) == 0 + && segment_name (now_seg)[9] != '_') + d = &eh_frame_data; + else if (strncmp (segment_name (now_seg), ".debug_frame", 12) == 0) + d = &debug_frame_data; + else + return 0; + + if (d->state >= state_saw_size && S_IS_DEFINED (d->size_end_sym)) + { + /* We have come to the end of the CIE or FDE. See below where + we set saw_size. We must check this first because we may now + be looking at the next size. */ + d->state = state_idle; + } + + switch (d->state) + { + case state_idle: + if (*pnbytes == 4) + { + /* This might be the size of the CIE or FDE. We want to know + the size so that we don't accidentally optimize across an FDE + boundary. We recognize the size in one of two forms: a + symbol which will later be defined as a difference, or a + subtraction of two symbols. Either way, we can tell when we + are at the end of the FDE because the symbol becomes defined + (in the case of a subtraction, the end symbol, from which the + start symbol is being subtracted). Other ways of describing + the size will not be optimized. */ + if ((exp->X_op == O_symbol || exp->X_op == O_subtract) + && ! S_IS_DEFINED (exp->X_add_symbol)) + { + d->state = state_saw_size; + d->size_end_sym = exp->X_add_symbol; + } + } + break; + + case state_saw_size: + case state_saw_cie_offset: + /* Assume whatever form it appears in, it appears atomically. */ + d->state = (enum frame_state) (d->state + 1); + break; + + case state_saw_pc_begin: + /* Decide whether we should see an augmentation. */ + if (! d->cie_info_ok + && ! (d->cie_info_ok = get_cie_info (&d->cie_info))) + d->state = state_error; + else if (d->cie_info.z_augmentation) + { + d->state = state_seeing_aug_size; + d->aug_size = 0; + d->aug_shift = 0; + } + else + d->state = state_wait_loc4; + break; + + case state_seeing_aug_size: + /* Bytes == -1 means this comes from an leb128 directive. */ + if ((int)*pnbytes == -1 && exp->X_op == O_constant) + { + d->aug_size = exp->X_add_number; + d->state = state_skipping_aug; + } + else if (*pnbytes == 1 && exp->X_op == O_constant) + { + unsigned char byte = exp->X_add_number; + d->aug_size |= (byte & 0x7f) << d->aug_shift; + d->aug_shift += 7; + if ((byte & 0x80) == 0) + d->state = state_skipping_aug; + } + else + d->state = state_error; + if (d->state == state_skipping_aug && d->aug_size == 0) + d->state = state_wait_loc4; + break; + + case state_skipping_aug: + if ((int)*pnbytes < 0) + d->state = state_error; + else + { + int left = (d->aug_size -= *pnbytes); + if (left == 0) + d->state = state_wait_loc4; + else if (left < 0) + d->state = state_error; + } + break; + + case state_wait_loc4: + if (*pnbytes == 1 + && exp->X_op == O_constant + && exp->X_add_number == DW_CFA_advance_loc4) + { + /* This might be a DW_CFA_advance_loc4. Record the frag and the + position within the frag, so that we can change it later. */ + frag_grow (1); + d->state = state_saw_loc4; + d->loc4_frag = frag_now; + d->loc4_fix = frag_now_fix (); + } + break; + + case state_saw_loc4: + d->state = state_wait_loc4; + if (*pnbytes != 4) + break; + if (exp->X_op == O_constant) + { + /* This is a case which we can optimize. The two symbols being + subtracted were in the same frag and the expression was + reduced to a constant. We can do the optimization entirely + in this function. */ + if (exp->X_add_number < 0x40) + { + d->loc4_frag->fr_literal[d->loc4_fix] + = DW_CFA_advance_loc | exp->X_add_number; + /* No more bytes needed. */ + return 1; + } + else if (exp->X_add_number < 0x100) + { + d->loc4_frag->fr_literal[d->loc4_fix] = DW_CFA_advance_loc1; + *pnbytes = 1; + } + else if (exp->X_add_number < 0x10000) + { + d->loc4_frag->fr_literal[d->loc4_fix] = DW_CFA_advance_loc2; + *pnbytes = 2; + } + } + else if (exp->X_op == O_subtract && d->cie_info.code_alignment == 1) + { + /* This is a case we can optimize. The expression was not + reduced, so we can not finish the optimization until the end + of the assembly. We set up a variant frag which we handle + later. */ + frag_var (rs_cfa, 4, 0, 1 << 3, make_expr_symbol (exp), + d->loc4_fix, (char *) d->loc4_frag); + return 1; + } + else if ((exp->X_op == O_divide + || exp->X_op == O_right_shift) + && d->cie_info.code_alignment > 1) + { + if (exp->X_add_symbol->bsym + && exp->X_op_symbol->bsym + && exp->X_add_symbol->sy_value.X_op == O_subtract + && exp->X_op_symbol->sy_value.X_op == O_constant + && ((exp->X_op == O_divide + ? exp->X_op_symbol->sy_value.X_add_number + : (offsetT) 1 << exp->X_op_symbol->sy_value.X_add_number) + == (offsetT) d->cie_info.code_alignment)) + { + /* This is a case we can optimize as well. The expression was + not reduced, so we can not finish the optimization until the + end of the assembly. We set up a variant frag which we + handle later. */ + frag_var (rs_cfa, 4, 0, d->cie_info.code_alignment << 3, + make_expr_symbol (&exp->X_add_symbol->sy_value), + d->loc4_fix, (char *) d->loc4_frag); + return 1; + } + } + break; + + case state_error: + /* Just skipping everything. */ + break; + } + + return 0; +} + +/* The function estimates the size of a rs_cfa variant frag based on + the current values of the symbols. It is called before the + relaxation loop. We set fr_subtype{0:2} to the expected length. */ + +int +eh_frame_estimate_size_before_relax (fragS *frag) +{ + offsetT diff; + int ca = frag->fr_subtype >> 3; + int ret; + + diff = resolve_symbol_value (frag->fr_symbol); + + gas_assert (ca > 0); + diff /= ca; + if (diff < 0x40) + ret = 0; + else if (diff < 0x100) + ret = 1; + else if (diff < 0x10000) + ret = 2; + else + ret = 4; + + frag->fr_subtype = (frag->fr_subtype & ~7) | ret; + + return ret; +} + +/* This function relaxes a rs_cfa variant frag based on the current + values of the symbols. fr_subtype{0:2} is the current length of + the frag. This returns the change in frag length. */ + +int +eh_frame_relax_frag (fragS *frag) +{ + int oldsize, newsize; + + oldsize = frag->fr_subtype & 7; + newsize = eh_frame_estimate_size_before_relax (frag); + return newsize - oldsize; +} + +/* This function converts a rs_cfa variant frag into a normal fill + frag. This is called after all relaxation has been done. + fr_subtype{0:2} will be the desired length of the frag. */ + +void +eh_frame_convert_frag (fragS *frag) +{ + offsetT diff; + fragS *loc4_frag; + int loc4_fix, ca; + + loc4_frag = (fragS *) frag->fr_opcode; + loc4_fix = (int) frag->fr_offset; + + diff = resolve_symbol_value (frag->fr_symbol); + + ca = frag->fr_subtype >> 3; + gas_assert (ca > 0); + diff /= ca; + switch (frag->fr_subtype & 7) + { + case 0: + gas_assert (diff < 0x40); + loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc | diff; + break; + + case 1: + gas_assert (diff < 0x100); + loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc1; + frag->fr_literal[frag->fr_fix] = diff; + break; + + case 2: + gas_assert (diff < 0x10000); + loc4_frag->fr_literal[loc4_fix] = DW_CFA_advance_loc2; + md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 2); + break; + + default: + md_number_to_chars (frag->fr_literal + frag->fr_fix, diff, 4); + break; + } + + frag->fr_fix += frag->fr_subtype & 7; + frag->fr_type = rs_fill; + frag->fr_subtype = 0; + frag->fr_offset = 0; +} diff --git a/contrib/toolchain/binutils/gas/expr.c b/contrib/toolchain/binutils/gas/expr.c new file mode 100644 index 0000000000..c4b2b756ba --- /dev/null +++ b/contrib/toolchain/binutils/gas/expr.c @@ -0,0 +1,2367 @@ +/* expr.c -operands, expressions- + Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2011, + 2012 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* This is really a branch office of as-read.c. I split it out to clearly + distinguish the world of expressions from the world of statements. + (It also gives smaller files to re-compile.) + Here, "operand"s are of expressions, not instructions. */ + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +#include "as.h" +#include "safe-ctype.h" +#include "obstack.h" + +#ifdef HAVE_LIMITS_H +#include +#endif +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +static void floating_constant (expressionS * expressionP); +static valueT generic_bignum_to_int32 (void); +#ifdef BFD64 +static valueT generic_bignum_to_int64 (void); +#endif +static void integer_constant (int radix, expressionS * expressionP); +static void mri_char_constant (expressionS *); +static void clean_up_expression (expressionS * expressionP); +static segT operand (expressionS *, enum expr_mode); +static operatorT operatorf (int *); + +extern const char EXP_CHARS[], FLT_CHARS[]; + +/* We keep a mapping of expression symbols to file positions, so that + we can provide better error messages. */ + +struct expr_symbol_line { + struct expr_symbol_line *next; + symbolS *sym; + char *file; + unsigned int line; +}; + +static struct expr_symbol_line *expr_symbol_lines; + +/* Build a dummy symbol to hold a complex expression. This is how we + build expressions up out of other expressions. The symbol is put + into the fake section expr_section. */ + +symbolS * +make_expr_symbol (expressionS *expressionP) +{ + expressionS zero; + symbolS *symbolP; + struct expr_symbol_line *n; + + if (expressionP->X_op == O_symbol + && expressionP->X_add_number == 0) + return expressionP->X_add_symbol; + + if (expressionP->X_op == O_big) + { + /* This won't work, because the actual value is stored in + generic_floating_point_number or generic_bignum, and we are + going to lose it if we haven't already. */ + if (expressionP->X_add_number > 0) + as_bad (_("bignum invalid")); + else + as_bad (_("floating point number invalid")); + zero.X_op = O_constant; + zero.X_add_number = 0; + zero.X_unsigned = 0; + zero.X_extrabit = 0; + clean_up_expression (&zero); + expressionP = &zero; + } + + /* Putting constant symbols in absolute_section rather than + expr_section is convenient for the old a.out code, for which + S_GET_SEGMENT does not always retrieve the value put in by + S_SET_SEGMENT. */ + symbolP = symbol_create (FAKE_LABEL_NAME, + (expressionP->X_op == O_constant + ? absolute_section + : expressionP->X_op == O_register + ? reg_section + : expr_section), + 0, &zero_address_frag); + symbol_set_value_expression (symbolP, expressionP); + + if (expressionP->X_op == O_constant) + resolve_symbol_value (symbolP); + + n = (struct expr_symbol_line *) xmalloc (sizeof *n); + n->sym = symbolP; + as_where (&n->file, &n->line); + n->next = expr_symbol_lines; + expr_symbol_lines = n; + + return symbolP; +} + +/* Return the file and line number for an expr symbol. Return + non-zero if something was found, 0 if no information is known for + the symbol. */ + +int +expr_symbol_where (symbolS *sym, char **pfile, unsigned int *pline) +{ + register struct expr_symbol_line *l; + + for (l = expr_symbol_lines; l != NULL; l = l->next) + { + if (l->sym == sym) + { + *pfile = l->file; + *pline = l->line; + return 1; + } + } + + return 0; +} + +/* Utilities for building expressions. + Since complex expressions are recorded as symbols for use in other + expressions these return a symbolS * and not an expressionS *. + These explicitly do not take an "add_number" argument. */ +/* ??? For completeness' sake one might want expr_build_symbol. + It would just return its argument. */ + +/* Build an expression for an unsigned constant. + The corresponding one for signed constants is missing because + there's currently no need for it. One could add an unsigned_p flag + but that seems more clumsy. */ + +symbolS * +expr_build_uconstant (offsetT value) +{ + expressionS e; + + e.X_op = O_constant; + e.X_add_number = value; + e.X_unsigned = 1; + e.X_extrabit = 0; + return make_expr_symbol (&e); +} + +/* Build an expression for the current location ('.'). */ + +symbolS * +expr_build_dot (void) +{ + expressionS e; + + current_location (&e); + return symbol_clone_if_forward_ref (make_expr_symbol (&e)); +} + +/* Build any floating-point literal here. + Also build any bignum literal here. */ + +/* Seems atof_machine can backscan through generic_bignum and hit whatever + happens to be loaded before it in memory. And its way too complicated + for me to fix right. Thus a hack. JF: Just make generic_bignum bigger, + and never write into the early words, thus they'll always be zero. + I hate Dean's floating-point code. Bleh. */ +LITTLENUM_TYPE generic_bignum[SIZE_OF_LARGE_NUMBER + 6]; + +FLONUM_TYPE generic_floating_point_number = { + &generic_bignum[6], /* low. (JF: Was 0) */ + &generic_bignum[SIZE_OF_LARGE_NUMBER + 6 - 1], /* high. JF: (added +6) */ + 0, /* leader. */ + 0, /* exponent. */ + 0 /* sign. */ +}; + + +static void +floating_constant (expressionS *expressionP) +{ + /* input_line_pointer -> floating-point constant. */ + int error_code; + + error_code = atof_generic (&input_line_pointer, ".", EXP_CHARS, + &generic_floating_point_number); + + if (error_code) + { + if (error_code == ERROR_EXPONENT_OVERFLOW) + { + as_bad (_("bad floating-point constant: exponent overflow")); + } + else + { + as_bad (_("bad floating-point constant: unknown error code=%d"), + error_code); + } + } + expressionP->X_op = O_big; + /* input_line_pointer -> just after constant, which may point to + whitespace. */ + expressionP->X_add_number = -1; +} + +static valueT +generic_bignum_to_int32 (void) +{ + valueT number = + ((generic_bignum[1] & LITTLENUM_MASK) << LITTLENUM_NUMBER_OF_BITS) + | (generic_bignum[0] & LITTLENUM_MASK); + number &= 0xffffffff; + return number; +} + +#ifdef BFD64 +static valueT +generic_bignum_to_int64 (void) +{ + valueT number = + ((((((((valueT) generic_bignum[3] & LITTLENUM_MASK) + << LITTLENUM_NUMBER_OF_BITS) + | ((valueT) generic_bignum[2] & LITTLENUM_MASK)) + << LITTLENUM_NUMBER_OF_BITS) + | ((valueT) generic_bignum[1] & LITTLENUM_MASK)) + << LITTLENUM_NUMBER_OF_BITS) + | ((valueT) generic_bignum[0] & LITTLENUM_MASK)); + return number; +} +#endif + +static void +integer_constant (int radix, expressionS *expressionP) +{ + char *start; /* Start of number. */ + char *suffix = NULL; + char c; + valueT number; /* Offset or (absolute) value. */ + short int digit; /* Value of next digit in current radix. */ + short int maxdig = 0; /* Highest permitted digit value. */ + int too_many_digits = 0; /* If we see >= this number of. */ + char *name; /* Points to name of symbol. */ + symbolS *symbolP; /* Points to symbol. */ + + int small; /* True if fits in 32 bits. */ + + /* May be bignum, or may fit in 32 bits. */ + /* Most numbers fit into 32 bits, and we want this case to be fast. + so we pretend it will fit into 32 bits. If, after making up a 32 + bit number, we realise that we have scanned more digits than + comfortably fit into 32 bits, we re-scan the digits coding them + into a bignum. For decimal and octal numbers we are + conservative: Some numbers may be assumed bignums when in fact + they do fit into 32 bits. Numbers of any radix can have excess + leading zeros: We strive to recognise this and cast them back + into 32 bits. We must check that the bignum really is more than + 32 bits, and change it back to a 32-bit number if it fits. The + number we are looking for is expected to be positive, but if it + fits into 32 bits as an unsigned number, we let it be a 32-bit + number. The cavalier approach is for speed in ordinary cases. */ + /* This has been extended for 64 bits. We blindly assume that if + you're compiling in 64-bit mode, the target is a 64-bit machine. + This should be cleaned up. */ + +#ifdef BFD64 +#define valuesize 64 +#else /* includes non-bfd case, mostly */ +#define valuesize 32 +#endif + + if ((NUMBERS_WITH_SUFFIX || flag_m68k_mri) && radix == 0) + { + int flt = 0; + + /* In MRI mode, the number may have a suffix indicating the + radix. For that matter, it might actually be a floating + point constant. */ + for (suffix = input_line_pointer; ISALNUM (*suffix); suffix++) + { + if (*suffix == 'e' || *suffix == 'E') + flt = 1; + } + + if (suffix == input_line_pointer) + { + radix = 10; + suffix = NULL; + } + else + { + c = *--suffix; + c = TOUPPER (c); + /* If we have both NUMBERS_WITH_SUFFIX and LOCAL_LABELS_FB, + we distinguish between 'B' and 'b'. This is the case for + Z80. */ + if ((NUMBERS_WITH_SUFFIX && LOCAL_LABELS_FB ? *suffix : c) == 'B') + radix = 2; + else if (c == 'D') + radix = 10; + else if (c == 'O' || c == 'Q') + radix = 8; + else if (c == 'H') + radix = 16; + else if (suffix[1] == '.' || c == 'E' || flt) + { + floating_constant (expressionP); + return; + } + else + { + radix = 10; + suffix = NULL; + } + } + } + + switch (radix) + { + case 2: + maxdig = 2; + too_many_digits = valuesize + 1; + break; + case 8: + maxdig = radix = 8; + too_many_digits = (valuesize + 2) / 3 + 1; + break; + case 16: + maxdig = radix = 16; + too_many_digits = (valuesize + 3) / 4 + 1; + break; + case 10: + maxdig = radix = 10; + too_many_digits = (valuesize + 11) / 4; /* Very rough. */ + } +#undef valuesize + start = input_line_pointer; + c = *input_line_pointer++; + for (number = 0; + (digit = hex_value (c)) < maxdig; + c = *input_line_pointer++) + { + number = number * radix + digit; + } + /* c contains character after number. */ + /* input_line_pointer->char after c. */ + small = (input_line_pointer - start - 1) < too_many_digits; + + if (radix == 16 && c == '_') + { + /* This is literal of the form 0x333_0_12345678_1. + This example is equivalent to 0x00000333000000001234567800000001. */ + + int num_little_digits = 0; + int i; + input_line_pointer = start; /* -> 1st digit. */ + + know (LITTLENUM_NUMBER_OF_BITS == 16); + + for (c = '_'; c == '_'; num_little_digits += 2) + { + + /* Convert one 64-bit word. */ + int ndigit = 0; + number = 0; + for (c = *input_line_pointer++; + (digit = hex_value (c)) < maxdig; + c = *(input_line_pointer++)) + { + number = number * radix + digit; + ndigit++; + } + + /* Check for 8 digit per word max. */ + if (ndigit > 8) + as_bad (_("a bignum with underscores may not have more than 8 hex digits in any word")); + + /* Add this chunk to the bignum. + Shift things down 2 little digits. */ + know (LITTLENUM_NUMBER_OF_BITS == 16); + for (i = min (num_little_digits + 1, SIZE_OF_LARGE_NUMBER - 1); + i >= 2; + i--) + generic_bignum[i] = generic_bignum[i - 2]; + + /* Add the new digits as the least significant new ones. */ + generic_bignum[0] = number & 0xffffffff; + generic_bignum[1] = number >> 16; + } + + /* Again, c is char after number, input_line_pointer->after c. */ + + if (num_little_digits > SIZE_OF_LARGE_NUMBER - 1) + num_little_digits = SIZE_OF_LARGE_NUMBER - 1; + + gas_assert (num_little_digits >= 4); + + if (num_little_digits != 8) + as_bad (_("a bignum with underscores must have exactly 4 words")); + + /* We might have some leading zeros. These can be trimmed to give + us a change to fit this constant into a small number. */ + while (generic_bignum[num_little_digits - 1] == 0 + && num_little_digits > 1) + num_little_digits--; + + if (num_little_digits <= 2) + { + /* will fit into 32 bits. */ + number = generic_bignum_to_int32 (); + small = 1; + } +#ifdef BFD64 + else if (num_little_digits <= 4) + { + /* Will fit into 64 bits. */ + number = generic_bignum_to_int64 (); + small = 1; + } +#endif + else + { + small = 0; + + /* Number of littlenums in the bignum. */ + number = num_little_digits; + } + } + else if (!small) + { + /* We saw a lot of digits. manufacture a bignum the hard way. */ + LITTLENUM_TYPE *leader; /* -> high order littlenum of the bignum. */ + LITTLENUM_TYPE *pointer; /* -> littlenum we are frobbing now. */ + long carry; + + leader = generic_bignum; + generic_bignum[0] = 0; + generic_bignum[1] = 0; + generic_bignum[2] = 0; + generic_bignum[3] = 0; + input_line_pointer = start; /* -> 1st digit. */ + c = *input_line_pointer++; + for (; (carry = hex_value (c)) < maxdig; c = *input_line_pointer++) + { + for (pointer = generic_bignum; pointer <= leader; pointer++) + { + long work; + + work = carry + radix * *pointer; + *pointer = work & LITTLENUM_MASK; + carry = work >> LITTLENUM_NUMBER_OF_BITS; + } + if (carry) + { + if (leader < generic_bignum + SIZE_OF_LARGE_NUMBER - 1) + { + /* Room to grow a longer bignum. */ + *++leader = carry; + } + } + } + /* Again, c is char after number. */ + /* input_line_pointer -> after c. */ + know (LITTLENUM_NUMBER_OF_BITS == 16); + if (leader < generic_bignum + 2) + { + /* Will fit into 32 bits. */ + number = generic_bignum_to_int32 (); + small = 1; + } +#ifdef BFD64 + else if (leader < generic_bignum + 4) + { + /* Will fit into 64 bits. */ + number = generic_bignum_to_int64 (); + small = 1; + } +#endif + else + { + /* Number of littlenums in the bignum. */ + number = leader - generic_bignum + 1; + } + } + + if ((NUMBERS_WITH_SUFFIX || flag_m68k_mri) + && suffix != NULL + && input_line_pointer - 1 == suffix) + c = *input_line_pointer++; + + if (small) + { + /* Here with number, in correct radix. c is the next char. + Note that unlike un*x, we allow "011f" "0x9f" to both mean + the same as the (conventional) "9f". + This is simply easier than checking for strict canonical + form. Syntax sux! */ + + if (LOCAL_LABELS_FB && c == 'b') + { + /* Backward ref to local label. + Because it is backward, expect it to be defined. */ + /* Construct a local label. */ + name = fb_label_name ((int) number, 0); + + /* Seen before, or symbol is defined: OK. */ + symbolP = symbol_find (name); + if ((symbolP != NULL) && (S_IS_DEFINED (symbolP))) + { + /* Local labels are never absolute. Don't waste time + checking absoluteness. */ + know (SEG_NORMAL (S_GET_SEGMENT (symbolP))); + + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + } + else + { + /* Either not seen or not defined. */ + /* @@ Should print out the original string instead of + the parsed number. */ + as_bad (_("backward ref to unknown label \"%d:\""), + (int) number); + expressionP->X_op = O_constant; + } + + expressionP->X_add_number = 0; + } /* case 'b' */ + else if (LOCAL_LABELS_FB && c == 'f') + { + /* Forward reference. Expect symbol to be undefined or + unknown. undefined: seen it before. unknown: never seen + it before. + + Construct a local label name, then an undefined symbol. + Don't create a xseg frag for it: caller may do that. + Just return it as never seen before. */ + name = fb_label_name ((int) number, 1); + symbolP = symbol_find_or_make (name); + /* We have no need to check symbol properties. */ +#ifndef many_segments + /* Since "know" puts its arg into a "string", we + can't have newlines in the argument. */ + know (S_GET_SEGMENT (symbolP) == undefined_section || S_GET_SEGMENT (symbolP) == text_section || S_GET_SEGMENT (symbolP) == data_section); +#endif + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; + } /* case 'f' */ + else if (LOCAL_LABELS_DOLLAR && c == '$') + { + /* If the dollar label is *currently* defined, then this is just + another reference to it. If it is not *currently* defined, + then this is a fresh instantiation of that number, so create + it. */ + + if (dollar_label_defined ((long) number)) + { + name = dollar_label_name ((long) number, 0); + symbolP = symbol_find (name); + know (symbolP != NULL); + } + else + { + name = dollar_label_name ((long) number, 1); + symbolP = symbol_find_or_make (name); + } + + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; + } /* case '$' */ + else + { + expressionP->X_op = O_constant; + expressionP->X_add_number = number; + input_line_pointer--; /* Restore following character. */ + } /* Really just a number. */ + } + else + { + /* Not a small number. */ + expressionP->X_op = O_big; + expressionP->X_add_number = number; /* Number of littlenums. */ + input_line_pointer--; /* -> char following number. */ + } +} + +/* Parse an MRI multi character constant. */ + +static void +mri_char_constant (expressionS *expressionP) +{ + int i; + + if (*input_line_pointer == '\'' + && input_line_pointer[1] != '\'') + { + expressionP->X_op = O_constant; + expressionP->X_add_number = 0; + return; + } + + /* In order to get the correct byte ordering, we must build the + number in reverse. */ + for (i = SIZE_OF_LARGE_NUMBER - 1; i >= 0; i--) + { + int j; + + generic_bignum[i] = 0; + for (j = 0; j < CHARS_PER_LITTLENUM; j++) + { + if (*input_line_pointer == '\'') + { + if (input_line_pointer[1] != '\'') + break; + ++input_line_pointer; + } + generic_bignum[i] <<= 8; + generic_bignum[i] += *input_line_pointer; + ++input_line_pointer; + } + + if (i < SIZE_OF_LARGE_NUMBER - 1) + { + /* If there is more than one littlenum, left justify the + last one to make it match the earlier ones. If there is + only one, we can just use the value directly. */ + for (; j < CHARS_PER_LITTLENUM; j++) + generic_bignum[i] <<= 8; + } + + if (*input_line_pointer == '\'' + && input_line_pointer[1] != '\'') + break; + } + + if (i < 0) + { + as_bad (_("character constant too large")); + i = 0; + } + + if (i > 0) + { + int c; + int j; + + c = SIZE_OF_LARGE_NUMBER - i; + for (j = 0; j < c; j++) + generic_bignum[j] = generic_bignum[i + j]; + i = c; + } + + know (LITTLENUM_NUMBER_OF_BITS == 16); + if (i > 2) + { + expressionP->X_op = O_big; + expressionP->X_add_number = i; + } + else + { + expressionP->X_op = O_constant; + if (i < 2) + expressionP->X_add_number = generic_bignum[0] & LITTLENUM_MASK; + else + expressionP->X_add_number = + (((generic_bignum[1] & LITTLENUM_MASK) + << LITTLENUM_NUMBER_OF_BITS) + | (generic_bignum[0] & LITTLENUM_MASK)); + } + + /* Skip the final closing quote. */ + ++input_line_pointer; +} + +/* Return an expression representing the current location. This + handles the magic symbol `.'. */ + +void +current_location (expressionS *expressionp) +{ + if (now_seg == absolute_section) + { + expressionp->X_op = O_constant; + expressionp->X_add_number = abs_section_offset; + } + else + { + expressionp->X_op = O_symbol; + expressionp->X_add_symbol = &dot_symbol; + expressionp->X_add_number = 0; + } +} + +/* In: Input_line_pointer points to 1st char of operand, which may + be a space. + + Out: An expressionS. + The operand may have been empty: in this case X_op == O_absent. + Input_line_pointer->(next non-blank) char after operand. */ + +static segT +operand (expressionS *expressionP, enum expr_mode mode) +{ + char c; + symbolS *symbolP; /* Points to symbol. */ + char *name; /* Points to name of symbol. */ + segT segment; + + /* All integers are regarded as unsigned unless they are negated. + This is because the only thing which cares whether a number is + unsigned is the code in emit_expr which extends constants into + bignums. It should only sign extend negative numbers, so that + something like ``.quad 0x80000000'' is not sign extended even + though it appears negative if valueT is 32 bits. */ + expressionP->X_unsigned = 1; + expressionP->X_extrabit = 0; + + /* Digits, assume it is a bignum. */ + + SKIP_WHITESPACE (); /* Leading whitespace is part of operand. */ + c = *input_line_pointer++; /* input_line_pointer -> past char in c. */ + + if (is_end_of_line[(unsigned char) c]) + goto eol; + + switch (c) + { + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + input_line_pointer--; + + integer_constant ((NUMBERS_WITH_SUFFIX || flag_m68k_mri) + ? 0 : 10, + expressionP); + break; + +#ifdef LITERAL_PREFIXDOLLAR_HEX + case '$': + /* $L is the start of a local label, not a hex constant. */ + if (* input_line_pointer == 'L') + goto isname; + integer_constant (16, expressionP); + break; +#endif + +#ifdef LITERAL_PREFIXPERCENT_BIN + case '%': + integer_constant (2, expressionP); + break; +#endif + + case '0': + /* Non-decimal radix. */ + + if (NUMBERS_WITH_SUFFIX || flag_m68k_mri) + { + char *s; + + /* Check for a hex or float constant. */ + for (s = input_line_pointer; hex_p (*s); s++) + ; + if (*s == 'h' || *s == 'H' || *input_line_pointer == '.') + { + --input_line_pointer; + integer_constant (0, expressionP); + break; + } + } + c = *input_line_pointer; + switch (c) + { + case 'o': + case 'O': + case 'q': + case 'Q': + case '8': + case '9': + if (NUMBERS_WITH_SUFFIX || flag_m68k_mri) + { + integer_constant (0, expressionP); + break; + } + /* Fall through. */ + default: + default_case: + if (c && strchr (FLT_CHARS, c)) + { + input_line_pointer++; + floating_constant (expressionP); + expressionP->X_add_number = - TOLOWER (c); + } + else + { + /* The string was only zero. */ + expressionP->X_op = O_constant; + expressionP->X_add_number = 0; + } + + break; + + case 'x': + case 'X': + if (flag_m68k_mri) + goto default_case; + input_line_pointer++; + integer_constant (16, expressionP); + break; + + case 'b': + if (LOCAL_LABELS_FB && ! (flag_m68k_mri || NUMBERS_WITH_SUFFIX)) + { + /* This code used to check for '+' and '-' here, and, in + some conditions, fall through to call + integer_constant. However, that didn't make sense, + as integer_constant only accepts digits. */ + /* Some of our code elsewhere does permit digits greater + than the expected base; for consistency, do the same + here. */ + if (input_line_pointer[1] < '0' + || input_line_pointer[1] > '9') + { + /* Parse this as a back reference to label 0. */ + input_line_pointer--; + integer_constant (10, expressionP); + break; + } + /* Otherwise, parse this as a binary number. */ + } + /* Fall through. */ + case 'B': + input_line_pointer++; + if (flag_m68k_mri || NUMBERS_WITH_SUFFIX) + goto default_case; + integer_constant (2, expressionP); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + integer_constant ((flag_m68k_mri || NUMBERS_WITH_SUFFIX) + ? 0 : 8, + expressionP); + break; + + case 'f': + if (LOCAL_LABELS_FB) + { + /* If it says "0f" and it could possibly be a floating point + number, make it one. Otherwise, make it a local label, + and try to deal with parsing the rest later. */ + if (!input_line_pointer[1] + || (is_end_of_line[0xff & input_line_pointer[1]]) + || strchr (FLT_CHARS, 'f') == NULL) + goto is_0f_label; + { + char *cp = input_line_pointer + 1; + int r = atof_generic (&cp, ".", EXP_CHARS, + &generic_floating_point_number); + switch (r) + { + case 0: + case ERROR_EXPONENT_OVERFLOW: + if (*cp == 'f' || *cp == 'b') + /* Looks like a difference expression. */ + goto is_0f_label; + else if (cp == input_line_pointer + 1) + /* No characters has been accepted -- looks like + end of operand. */ + goto is_0f_label; + else + goto is_0f_float; + default: + as_fatal (_("expr.c(operand): bad atof_generic return val %d"), + r); + } + } + + /* Okay, now we've sorted it out. We resume at one of these + two labels, depending on what we've decided we're probably + looking at. */ + is_0f_label: + input_line_pointer--; + integer_constant (10, expressionP); + break; + + is_0f_float: + /* Fall through. */ + ; + } + + case 'd': + case 'D': + if (flag_m68k_mri || NUMBERS_WITH_SUFFIX) + { + integer_constant (0, expressionP); + break; + } + /* Fall through. */ + case 'F': + case 'r': + case 'e': + case 'E': + case 'g': + case 'G': + input_line_pointer++; + floating_constant (expressionP); + expressionP->X_add_number = - TOLOWER (c); + break; + + case '$': + if (LOCAL_LABELS_DOLLAR) + { + integer_constant (10, expressionP); + break; + } + else + goto default_case; + } + + break; + +#ifndef NEED_INDEX_OPERATOR + case '[': +# ifdef md_need_index_operator + if (md_need_index_operator()) + goto de_fault; +# endif + /* FALLTHROUGH */ +#endif + case '(': + /* Didn't begin with digit & not a name. */ + segment = expr (0, expressionP, mode); + /* expression () will pass trailing whitespace. */ + if ((c == '(' && *input_line_pointer != ')') + || (c == '[' && *input_line_pointer != ']')) + as_bad (_("missing '%c'"), c == '(' ? ')' : ']'); + else + input_line_pointer++; + SKIP_WHITESPACE (); + /* Here with input_line_pointer -> char after "(...)". */ + return segment; + +#ifdef TC_M68K + case 'E': + if (! flag_m68k_mri || *input_line_pointer != '\'') + goto de_fault; + as_bad (_("EBCDIC constants are not supported")); + /* Fall through. */ + case 'A': + if (! flag_m68k_mri || *input_line_pointer != '\'') + goto de_fault; + ++input_line_pointer; + /* Fall through. */ +#endif + case '\'': + if (! flag_m68k_mri) + { + /* Warning: to conform to other people's assemblers NO + ESCAPEMENT is permitted for a single quote. The next + character, parity errors and all, is taken as the value + of the operand. VERY KINKY. */ + expressionP->X_op = O_constant; + expressionP->X_add_number = *input_line_pointer++; + break; + } + + mri_char_constant (expressionP); + break; + +#ifdef TC_M68K + case '"': + /* Double quote is the bitwise not operator in MRI mode. */ + if (! flag_m68k_mri) + goto de_fault; + /* Fall through. */ +#endif + case '~': + /* '~' is permitted to start a label on the Delta. */ + if (is_name_beginner (c)) + goto isname; + case '!': + case '-': + case '+': + { +#ifdef md_operator + unary: +#endif + operand (expressionP, mode); + if (expressionP->X_op == O_constant) + { + /* input_line_pointer -> char after operand. */ + if (c == '-') + { + expressionP->X_add_number = - expressionP->X_add_number; + /* Notice: '-' may overflow: no warning is given. + This is compatible with other people's + assemblers. Sigh. */ + expressionP->X_unsigned = 0; + if (expressionP->X_add_number) + expressionP->X_extrabit ^= 1; + } + else if (c == '~' || c == '"') + expressionP->X_add_number = ~ expressionP->X_add_number; + else if (c == '!') + expressionP->X_add_number = ! expressionP->X_add_number; + } + else if (expressionP->X_op == O_big + && expressionP->X_add_number <= 0 + && c == '-' + && (generic_floating_point_number.sign == '+' + || generic_floating_point_number.sign == 'P')) + { + /* Negative flonum (eg, -1.000e0). */ + if (generic_floating_point_number.sign == '+') + generic_floating_point_number.sign = '-'; + else + generic_floating_point_number.sign = 'N'; + } + else if (expressionP->X_op == O_big + && expressionP->X_add_number > 0) + { + int i; + + if (c == '~' || c == '-') + { + for (i = 0; i < expressionP->X_add_number; ++i) + generic_bignum[i] = ~generic_bignum[i]; + + /* Extend the bignum to at least the size of .octa. */ + if (expressionP->X_add_number < SIZE_OF_LARGE_NUMBER) + { + expressionP->X_add_number = SIZE_OF_LARGE_NUMBER; + for (; i < expressionP->X_add_number; ++i) + generic_bignum[i] = ~(LITTLENUM_TYPE) 0; + } + + if (c == '-') + for (i = 0; i < expressionP->X_add_number; ++i) + { + generic_bignum[i] += 1; + if (generic_bignum[i]) + break; + } + } + else if (c == '!') + { + for (i = 0; i < expressionP->X_add_number; ++i) + if (generic_bignum[i] != 0) + break; + expressionP->X_add_number = i >= expressionP->X_add_number; + expressionP->X_op = O_constant; + expressionP->X_unsigned = 1; + expressionP->X_extrabit = 0; + } + } + else if (expressionP->X_op != O_illegal + && expressionP->X_op != O_absent) + { + if (c != '+') + { + expressionP->X_add_symbol = make_expr_symbol (expressionP); + if (c == '-') + expressionP->X_op = O_uminus; + else if (c == '~' || c == '"') + expressionP->X_op = O_bit_not; + else + expressionP->X_op = O_logical_not; + expressionP->X_add_number = 0; + } + } + else + as_warn (_("Unary operator %c ignored because bad operand follows"), + c); + } + break; + +#if defined (DOLLAR_DOT) || defined (TC_M68K) + case '$': + /* '$' is the program counter when in MRI mode, or when + DOLLAR_DOT is defined. */ +#ifndef DOLLAR_DOT + if (! flag_m68k_mri) + goto de_fault; +#endif + if (DOLLAR_AMBIGU && hex_p (*input_line_pointer)) + { + /* In MRI mode and on Z80, '$' is also used as the prefix + for a hexadecimal constant. */ + integer_constant (16, expressionP); + break; + } + + if (is_part_of_name (*input_line_pointer)) + goto isname; + + current_location (expressionP); + break; +#endif + + case '.': + if (!is_part_of_name (*input_line_pointer)) + { + current_location (expressionP); + break; + } + else if ((strncasecmp (input_line_pointer, "startof.", 8) == 0 + && ! is_part_of_name (input_line_pointer[8])) + || (strncasecmp (input_line_pointer, "sizeof.", 7) == 0 + && ! is_part_of_name (input_line_pointer[7]))) + { + int start; + + start = (input_line_pointer[1] == 't' + || input_line_pointer[1] == 'T'); + input_line_pointer += start ? 8 : 7; + SKIP_WHITESPACE (); + if (*input_line_pointer != '(') + as_bad (_("syntax error in .startof. or .sizeof.")); + else + { + char *buf; + + ++input_line_pointer; + SKIP_WHITESPACE (); + name = input_line_pointer; + c = get_symbol_end (); + + buf = (char *) xmalloc (strlen (name) + 10); + if (start) + sprintf (buf, ".startof.%s", name); + else + sprintf (buf, ".sizeof.%s", name); + symbolP = symbol_make (buf); + free (buf); + + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; + + *input_line_pointer = c; + SKIP_WHITESPACE (); + if (*input_line_pointer != ')') + as_bad (_("syntax error in .startof. or .sizeof.")); + else + ++input_line_pointer; + } + break; + } + else + { + goto isname; + } + + case ',': + eol: + /* Can't imagine any other kind of operand. */ + expressionP->X_op = O_absent; + input_line_pointer--; + break; + +#ifdef TC_M68K + case '%': + if (! flag_m68k_mri) + goto de_fault; + integer_constant (2, expressionP); + break; + + case '@': + if (! flag_m68k_mri) + goto de_fault; + integer_constant (8, expressionP); + break; + + case ':': + if (! flag_m68k_mri) + goto de_fault; + + /* In MRI mode, this is a floating point constant represented + using hexadecimal digits. */ + + ++input_line_pointer; + integer_constant (16, expressionP); + break; + + case '*': + if (! flag_m68k_mri || is_part_of_name (*input_line_pointer)) + goto de_fault; + + current_location (expressionP); + break; +#endif + + default: +#if defined(md_need_index_operator) || defined(TC_M68K) + de_fault: +#endif + if (is_name_beginner (c)) /* Here if did not begin with a digit. */ + { + /* Identifier begins here. + This is kludged for speed, so code is repeated. */ + isname: + name = --input_line_pointer; + c = get_symbol_end (); + +#ifdef md_operator + { + operatorT op = md_operator (name, 1, &c); + + switch (op) + { + case O_uminus: + *input_line_pointer = c; + c = '-'; + goto unary; + case O_bit_not: + *input_line_pointer = c; + c = '~'; + goto unary; + case O_logical_not: + *input_line_pointer = c; + c = '!'; + goto unary; + case O_illegal: + as_bad (_("invalid use of operator \"%s\""), name); + break; + default: + break; + } + if (op != O_absent && op != O_illegal) + { + *input_line_pointer = c; + expr (9, expressionP, mode); + expressionP->X_add_symbol = make_expr_symbol (expressionP); + expressionP->X_op_symbol = NULL; + expressionP->X_add_number = 0; + expressionP->X_op = op; + break; + } + } +#endif + +#ifdef md_parse_name + /* This is a hook for the backend to parse certain names + specially in certain contexts. If a name always has a + specific value, it can often be handled by simply + entering it in the symbol table. */ + if (md_parse_name (name, expressionP, mode, &c)) + { + *input_line_pointer = c; + break; + } +#endif + +#ifdef TC_I960 + /* The MRI i960 assembler permits + lda sizeof code,g13 + FIXME: This should use md_parse_name. */ + if (flag_mri + && (strcasecmp (name, "sizeof") == 0 + || strcasecmp (name, "startof") == 0)) + { + int start; + char *buf; + + start = (name[1] == 't' + || name[1] == 'T'); + + *input_line_pointer = c; + SKIP_WHITESPACE (); + + name = input_line_pointer; + c = get_symbol_end (); + + buf = (char *) xmalloc (strlen (name) + 10); + if (start) + sprintf (buf, ".startof.%s", name); + else + sprintf (buf, ".sizeof.%s", name); + symbolP = symbol_make (buf); + free (buf); + + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; + + *input_line_pointer = c; + SKIP_WHITESPACE (); + + break; + } +#endif + + symbolP = symbol_find_or_make (name); + + /* If we have an absolute symbol or a reg, then we know its + value now. */ + segment = S_GET_SEGMENT (symbolP); + if (mode != expr_defer + && segment == absolute_section + && !S_FORCE_RELOC (symbolP, 0)) + { + expressionP->X_op = O_constant; + expressionP->X_add_number = S_GET_VALUE (symbolP); + } + else if (mode != expr_defer && segment == reg_section) + { + expressionP->X_op = O_register; + expressionP->X_add_number = S_GET_VALUE (symbolP); + } + else + { + expressionP->X_op = O_symbol; + expressionP->X_add_symbol = symbolP; + expressionP->X_add_number = 0; + } + *input_line_pointer = c; + } + else + { + /* Let the target try to parse it. Success is indicated by changing + the X_op field to something other than O_absent and pointing + input_line_pointer past the expression. If it can't parse the + expression, X_op and input_line_pointer should be unchanged. */ + expressionP->X_op = O_absent; + --input_line_pointer; + md_operand (expressionP); + if (expressionP->X_op == O_absent) + { + ++input_line_pointer; + as_bad (_("bad expression")); + expressionP->X_op = O_constant; + expressionP->X_add_number = 0; + } + } + break; + } + + /* It is more 'efficient' to clean up the expressionS when they are + created. Doing it here saves lines of code. */ + clean_up_expression (expressionP); + SKIP_WHITESPACE (); /* -> 1st char after operand. */ + know (*input_line_pointer != ' '); + + /* The PA port needs this information. */ + if (expressionP->X_add_symbol) + symbol_mark_used (expressionP->X_add_symbol); + + if (mode != expr_defer) + { + expressionP->X_add_symbol + = symbol_clone_if_forward_ref (expressionP->X_add_symbol); + expressionP->X_op_symbol + = symbol_clone_if_forward_ref (expressionP->X_op_symbol); + } + + switch (expressionP->X_op) + { + default: + return absolute_section; + case O_symbol: + return S_GET_SEGMENT (expressionP->X_add_symbol); + case O_register: + return reg_section; + } +} + +/* Internal. Simplify a struct expression for use by expr (). */ + +/* In: address of an expressionS. + The X_op field of the expressionS may only take certain values. + Elsewise we waste time special-case testing. Sigh. Ditto SEG_ABSENT. + + Out: expressionS may have been modified: + Unused fields zeroed to help expr (). */ + +static void +clean_up_expression (expressionS *expressionP) +{ + switch (expressionP->X_op) + { + case O_illegal: + case O_absent: + expressionP->X_add_number = 0; + /* Fall through. */ + case O_big: + case O_constant: + case O_register: + expressionP->X_add_symbol = NULL; + /* Fall through. */ + case O_symbol: + case O_uminus: + case O_bit_not: + expressionP->X_op_symbol = NULL; + break; + default: + break; + } +} + +/* Expression parser. */ + +/* We allow an empty expression, and just assume (absolute,0) silently. + Unary operators and parenthetical expressions are treated as operands. + As usual, Q==quantity==operand, O==operator, X==expression mnemonics. + + We used to do an aho/ullman shift-reduce parser, but the logic got so + warped that I flushed it and wrote a recursive-descent parser instead. + Now things are stable, would anybody like to write a fast parser? + Most expressions are either register (which does not even reach here) + or 1 symbol. Then "symbol+constant" and "symbol-symbol" are common. + So I guess it doesn't really matter how inefficient more complex expressions + are parsed. + + After expr(RANK,resultP) input_line_pointer->operator of rank <= RANK. + Also, we have consumed any leading or trailing spaces (operand does that) + and done all intervening operators. + + This returns the segment of the result, which will be + absolute_section or the segment of a symbol. */ + +#undef __ +#define __ O_illegal +#ifndef O_SINGLE_EQ +#define O_SINGLE_EQ O_illegal +#endif + +/* Maps ASCII -> operators. */ +static const operatorT op_encoding[256] = { + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + + __, O_bit_or_not, __, __, __, O_modulus, O_bit_and, __, + __, __, O_multiply, O_add, __, O_subtract, __, O_divide, + __, __, __, __, __, __, __, __, + __, __, __, __, O_lt, O_SINGLE_EQ, O_gt, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, + __, __, __, +#ifdef NEED_INDEX_OPERATOR + O_index, +#else + __, +#endif + __, __, O_bit_exclusive_or, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, + __, __, __, __, O_bit_inclusive_or, __, __, __, + + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __ +}; + +/* Rank Examples + 0 operand, (expression) + 1 || + 2 && + 3 == <> < <= >= > + 4 + - + 5 used for * / % in MRI mode + 6 & ^ ! | + 7 * / % << >> + 8 unary - unary ~ +*/ +static operator_rankT op_rank[O_max] = { + 0, /* O_illegal */ + 0, /* O_absent */ + 0, /* O_constant */ + 0, /* O_symbol */ + 0, /* O_symbol_rva */ + 0, /* O_register */ + 0, /* O_big */ + 9, /* O_uminus */ + 9, /* O_bit_not */ + 9, /* O_logical_not */ + 8, /* O_multiply */ + 8, /* O_divide */ + 8, /* O_modulus */ + 8, /* O_left_shift */ + 8, /* O_right_shift */ + 7, /* O_bit_inclusive_or */ + 7, /* O_bit_or_not */ + 7, /* O_bit_exclusive_or */ + 7, /* O_bit_and */ + 5, /* O_add */ + 5, /* O_subtract */ + 4, /* O_eq */ + 4, /* O_ne */ + 4, /* O_lt */ + 4, /* O_le */ + 4, /* O_ge */ + 4, /* O_gt */ + 3, /* O_logical_and */ + 2, /* O_logical_or */ + 1, /* O_index */ +}; + +/* Unfortunately, in MRI mode for the m68k, multiplication and + division have lower precedence than the bit wise operators. This + function sets the operator precedences correctly for the current + mode. Also, MRI uses a different bit_not operator, and this fixes + that as well. */ + +#define STANDARD_MUL_PRECEDENCE 8 +#define MRI_MUL_PRECEDENCE 6 + +void +expr_set_precedence (void) +{ + if (flag_m68k_mri) + { + op_rank[O_multiply] = MRI_MUL_PRECEDENCE; + op_rank[O_divide] = MRI_MUL_PRECEDENCE; + op_rank[O_modulus] = MRI_MUL_PRECEDENCE; + } + else + { + op_rank[O_multiply] = STANDARD_MUL_PRECEDENCE; + op_rank[O_divide] = STANDARD_MUL_PRECEDENCE; + op_rank[O_modulus] = STANDARD_MUL_PRECEDENCE; + } +} + +void +expr_set_rank (operatorT op, operator_rankT rank) +{ + gas_assert (op >= O_md1 && op < ARRAY_SIZE (op_rank)); + op_rank[op] = rank; +} + +/* Initialize the expression parser. */ + +void +expr_begin (void) +{ + expr_set_precedence (); + + /* Verify that X_op field is wide enough. */ + { + expressionS e; + e.X_op = O_max; + gas_assert (e.X_op == O_max); + } +} + +/* Return the encoding for the operator at INPUT_LINE_POINTER, and + sets NUM_CHARS to the number of characters in the operator. + Does not advance INPUT_LINE_POINTER. */ + +static inline operatorT +operatorf (int *num_chars) +{ + int c; + operatorT ret; + + c = *input_line_pointer & 0xff; + *num_chars = 1; + + if (is_end_of_line[c]) + return O_illegal; + +#ifdef md_operator + if (is_name_beginner (c)) + { + char *name = input_line_pointer; + char ec = get_symbol_end (); + + ret = md_operator (name, 2, &ec); + switch (ret) + { + case O_absent: + *input_line_pointer = ec; + input_line_pointer = name; + break; + case O_uminus: + case O_bit_not: + case O_logical_not: + as_bad (_("invalid use of operator \"%s\""), name); + ret = O_illegal; + /* FALLTHROUGH */ + default: + *input_line_pointer = ec; + *num_chars = input_line_pointer - name; + input_line_pointer = name; + return ret; + } + } +#endif + + switch (c) + { + default: + ret = op_encoding[c]; +#ifdef md_operator + if (ret == O_illegal) + { + char *start = input_line_pointer; + + ret = md_operator (NULL, 2, NULL); + if (ret != O_illegal) + *num_chars = input_line_pointer - start; + input_line_pointer = start; + } +#endif + return ret; + + case '+': + case '-': + return op_encoding[c]; + + case '<': + switch (input_line_pointer[1]) + { + default: + return op_encoding[c]; + case '<': + ret = O_left_shift; + break; + case '>': + ret = O_ne; + break; + case '=': + ret = O_le; + break; + } + *num_chars = 2; + return ret; + + case '=': + if (input_line_pointer[1] != '=') + return op_encoding[c]; + + *num_chars = 2; + return O_eq; + + case '>': + switch (input_line_pointer[1]) + { + default: + return op_encoding[c]; + case '>': + ret = O_right_shift; + break; + case '=': + ret = O_ge; + break; + } + *num_chars = 2; + return ret; + + case '!': + switch (input_line_pointer[1]) + { + case '!': + /* We accept !! as equivalent to ^ for MRI compatibility. */ + *num_chars = 2; + return O_bit_exclusive_or; + case '=': + /* We accept != as equivalent to <>. */ + *num_chars = 2; + return O_ne; + default: + if (flag_m68k_mri) + return O_bit_inclusive_or; + return op_encoding[c]; + } + + case '|': + if (input_line_pointer[1] != '|') + return op_encoding[c]; + + *num_chars = 2; + return O_logical_or; + + case '&': + if (input_line_pointer[1] != '&') + return op_encoding[c]; + + *num_chars = 2; + return O_logical_and; + } + + /* NOTREACHED */ +} + +/* Implement "word-size + 1 bit" addition for + {resultP->X_extrabit:resultP->X_add_number} + {rhs_highbit:amount}. This + is used so that the full range of unsigned word values and the full range of + signed word values can be represented in an O_constant expression, which is + useful e.g. for .sleb128 directives. */ + +void +add_to_result (expressionS *resultP, offsetT amount, int rhs_highbit) +{ + valueT ures = resultP->X_add_number; + valueT uamount = amount; + + resultP->X_add_number += amount; + + resultP->X_extrabit ^= rhs_highbit; + + if (ures + uamount < ures) + resultP->X_extrabit ^= 1; +} + +/* Similarly, for subtraction. */ + +void +subtract_from_result (expressionS *resultP, offsetT amount, int rhs_highbit) +{ + valueT ures = resultP->X_add_number; + valueT uamount = amount; + + resultP->X_add_number -= amount; + + resultP->X_extrabit ^= rhs_highbit; + + if (ures < uamount) + resultP->X_extrabit ^= 1; +} + +/* Parse an expression. */ + +segT +expr (int rankarg, /* Larger # is higher rank. */ + expressionS *resultP, /* Deliver result here. */ + enum expr_mode mode /* Controls behavior. */) +{ + operator_rankT rank = (operator_rankT) rankarg; + segT retval; + expressionS right; + operatorT op_left; + operatorT op_right; + int op_chars; + + know (rankarg >= 0); + + /* Save the value of dot for the fixup code. */ + if (rank == 0) + { + dot_value = frag_now_fix (); + dot_frag = frag_now; + } + + retval = operand (resultP, mode); + + /* operand () gobbles spaces. */ + know (*input_line_pointer != ' '); + + op_left = operatorf (&op_chars); + while (op_left != O_illegal && op_rank[(int) op_left] > rank) + { + segT rightseg; + offsetT frag_off; + + input_line_pointer += op_chars; /* -> after operator. */ + + right.X_md = 0; + rightseg = expr (op_rank[(int) op_left], &right, mode); + if (right.X_op == O_absent) + { + as_warn (_("missing operand; zero assumed")); + right.X_op = O_constant; + right.X_add_number = 0; + right.X_add_symbol = NULL; + right.X_op_symbol = NULL; + } + + know (*input_line_pointer != ' '); + + if (op_left == O_index) + { + if (*input_line_pointer != ']') + as_bad ("missing right bracket"); + else + { + ++input_line_pointer; + SKIP_WHITESPACE (); + } + } + + op_right = operatorf (&op_chars); + + know (op_right == O_illegal || op_left == O_index + || op_rank[(int) op_right] <= op_rank[(int) op_left]); + know ((int) op_left >= (int) O_multiply); +#ifndef md_operator + know ((int) op_left <= (int) O_index); +#else + know ((int) op_left < (int) O_max); +#endif + + /* input_line_pointer->after right-hand quantity. */ + /* left-hand quantity in resultP. */ + /* right-hand quantity in right. */ + /* operator in op_left. */ + + if (resultP->X_op == O_big) + { + if (resultP->X_add_number > 0) + as_warn (_("left operand is a bignum; integer 0 assumed")); + else + as_warn (_("left operand is a float; integer 0 assumed")); + resultP->X_op = O_constant; + resultP->X_add_number = 0; + resultP->X_add_symbol = NULL; + resultP->X_op_symbol = NULL; + } + if (right.X_op == O_big) + { + if (right.X_add_number > 0) + as_warn (_("right operand is a bignum; integer 0 assumed")); + else + as_warn (_("right operand is a float; integer 0 assumed")); + right.X_op = O_constant; + right.X_add_number = 0; + right.X_add_symbol = NULL; + right.X_op_symbol = NULL; + } + + /* Optimize common cases. */ +#ifdef md_optimize_expr + if (md_optimize_expr (resultP, op_left, &right)) + { + /* Skip. */ + ; + } + else +#endif +#ifndef md_register_arithmetic +# define md_register_arithmetic 1 +#endif + if (op_left == O_add && right.X_op == O_constant + && (md_register_arithmetic || resultP->X_op != O_register)) + { + /* X + constant. */ + add_to_result (resultP, right.X_add_number, right.X_extrabit); + } + /* This case comes up in PIC code. */ + else if (op_left == O_subtract + && right.X_op == O_symbol + && resultP->X_op == O_symbol + && retval == rightseg +#ifdef md_allow_local_subtract + && md_allow_local_subtract (resultP, & right, rightseg) +#endif + && ((SEG_NORMAL (rightseg) + && !S_FORCE_RELOC (resultP->X_add_symbol, 0) + && !S_FORCE_RELOC (right.X_add_symbol, 0)) + || right.X_add_symbol == resultP->X_add_symbol) + && frag_offset_fixed_p (symbol_get_frag (resultP->X_add_symbol), + symbol_get_frag (right.X_add_symbol), + &frag_off)) + { + offsetT symval_diff = S_GET_VALUE (resultP->X_add_symbol) + - S_GET_VALUE (right.X_add_symbol); + subtract_from_result (resultP, right.X_add_number, right.X_extrabit); + subtract_from_result (resultP, frag_off / OCTETS_PER_BYTE, 0); + add_to_result (resultP, symval_diff, symval_diff < 0); + resultP->X_op = O_constant; + resultP->X_add_symbol = 0; + } + else if (op_left == O_subtract && right.X_op == O_constant + && (md_register_arithmetic || resultP->X_op != O_register)) + { + /* X - constant. */ + subtract_from_result (resultP, right.X_add_number, right.X_extrabit); + } + else if (op_left == O_add && resultP->X_op == O_constant + && (md_register_arithmetic || right.X_op != O_register)) + { + /* Constant + X. */ + resultP->X_op = right.X_op; + resultP->X_add_symbol = right.X_add_symbol; + resultP->X_op_symbol = right.X_op_symbol; + add_to_result (resultP, right.X_add_number, right.X_extrabit); + retval = rightseg; + } + else if (resultP->X_op == O_constant && right.X_op == O_constant) + { + /* Constant OP constant. */ + offsetT v = right.X_add_number; + if (v == 0 && (op_left == O_divide || op_left == O_modulus)) + { + as_warn (_("division by zero")); + v = 1; + } + if ((valueT) v >= sizeof(valueT) * CHAR_BIT + && (op_left == O_left_shift || op_left == O_right_shift)) + { + as_warn_value_out_of_range (_("shift count"), v, 0, + sizeof(valueT) * CHAR_BIT - 1, + NULL, 0); + resultP->X_add_number = v = 0; + } + switch (op_left) + { + default: goto general; + case O_multiply: resultP->X_add_number *= v; break; + case O_divide: resultP->X_add_number /= v; break; + case O_modulus: resultP->X_add_number %= v; break; + case O_left_shift: resultP->X_add_number <<= v; break; + case O_right_shift: + /* We always use unsigned shifts, to avoid relying on + characteristics of the compiler used to compile gas. */ + resultP->X_add_number = + (offsetT) ((valueT) resultP->X_add_number >> (valueT) v); + break; + case O_bit_inclusive_or: resultP->X_add_number |= v; break; + case O_bit_or_not: resultP->X_add_number |= ~v; break; + case O_bit_exclusive_or: resultP->X_add_number ^= v; break; + case O_bit_and: resultP->X_add_number &= v; break; + /* Constant + constant (O_add) is handled by the + previous if statement for constant + X, so is omitted + here. */ + case O_subtract: + subtract_from_result (resultP, v, 0); + break; + case O_eq: + resultP->X_add_number = + resultP->X_add_number == v ? ~ (offsetT) 0 : 0; + break; + case O_ne: + resultP->X_add_number = + resultP->X_add_number != v ? ~ (offsetT) 0 : 0; + break; + case O_lt: + resultP->X_add_number = + resultP->X_add_number < v ? ~ (offsetT) 0 : 0; + break; + case O_le: + resultP->X_add_number = + resultP->X_add_number <= v ? ~ (offsetT) 0 : 0; + break; + case O_ge: + resultP->X_add_number = + resultP->X_add_number >= v ? ~ (offsetT) 0 : 0; + break; + case O_gt: + resultP->X_add_number = + resultP->X_add_number > v ? ~ (offsetT) 0 : 0; + break; + case O_logical_and: + resultP->X_add_number = resultP->X_add_number && v; + break; + case O_logical_or: + resultP->X_add_number = resultP->X_add_number || v; + break; + } + } + else if (resultP->X_op == O_symbol + && right.X_op == O_symbol + && (op_left == O_add + || op_left == O_subtract + || (resultP->X_add_number == 0 + && right.X_add_number == 0))) + { + /* Symbol OP symbol. */ + resultP->X_op = op_left; + resultP->X_op_symbol = right.X_add_symbol; + if (op_left == O_add) + add_to_result (resultP, right.X_add_number, right.X_extrabit); + else if (op_left == O_subtract) + { + subtract_from_result (resultP, right.X_add_number, + right.X_extrabit); + if (retval == rightseg + && SEG_NORMAL (retval) + && !S_FORCE_RELOC (resultP->X_add_symbol, 0) + && !S_FORCE_RELOC (right.X_add_symbol, 0)) + { + retval = absolute_section; + rightseg = absolute_section; + } + } + } + else + { + general: + /* The general case. */ + resultP->X_add_symbol = make_expr_symbol (resultP); + resultP->X_op_symbol = make_expr_symbol (&right); + resultP->X_op = op_left; + resultP->X_add_number = 0; + resultP->X_unsigned = 1; + resultP->X_extrabit = 0; + } + + if (retval != rightseg) + { + if (retval == undefined_section) + ; + else if (rightseg == undefined_section) + retval = rightseg; + else if (retval == expr_section) + ; + else if (rightseg == expr_section) + retval = rightseg; + else if (retval == reg_section) + ; + else if (rightseg == reg_section) + retval = rightseg; + else if (rightseg == absolute_section) + ; + else if (retval == absolute_section) + retval = rightseg; +#ifdef DIFF_EXPR_OK + else if (op_left == O_subtract) + ; +#endif + else + as_bad (_("operation combines symbols in different segments")); + } + + op_left = op_right; + } /* While next operator is >= this rank. */ + + /* The PA port needs this information. */ + if (resultP->X_add_symbol) + symbol_mark_used (resultP->X_add_symbol); + + if (rank == 0 && mode == expr_evaluate) + resolve_expression (resultP); + + return resultP->X_op == O_constant ? absolute_section : retval; +} + +/* Resolve an expression without changing any symbols/sub-expressions + used. */ + +int +resolve_expression (expressionS *expressionP) +{ + /* Help out with CSE. */ + valueT final_val = expressionP->X_add_number; + symbolS *add_symbol = expressionP->X_add_symbol; + symbolS *orig_add_symbol = add_symbol; + symbolS *op_symbol = expressionP->X_op_symbol; + operatorT op = expressionP->X_op; + valueT left, right; + segT seg_left, seg_right; + fragS *frag_left, *frag_right; + offsetT frag_off; + + switch (op) + { + default: + return 0; + + case O_constant: + case O_register: + left = 0; + break; + + case O_symbol: + case O_symbol_rva: + if (!snapshot_symbol (&add_symbol, &left, &seg_left, &frag_left)) + return 0; + + break; + + case O_uminus: + case O_bit_not: + case O_logical_not: + if (!snapshot_symbol (&add_symbol, &left, &seg_left, &frag_left)) + return 0; + + if (seg_left != absolute_section) + return 0; + + if (op == O_logical_not) + left = !left; + else if (op == O_uminus) + left = -left; + else + left = ~left; + op = O_constant; + break; + + case O_multiply: + case O_divide: + case O_modulus: + case O_left_shift: + case O_right_shift: + case O_bit_inclusive_or: + case O_bit_or_not: + case O_bit_exclusive_or: + case O_bit_and: + case O_add: + case O_subtract: + case O_eq: + case O_ne: + case O_lt: + case O_le: + case O_ge: + case O_gt: + case O_logical_and: + case O_logical_or: + if (!snapshot_symbol (&add_symbol, &left, &seg_left, &frag_left) + || !snapshot_symbol (&op_symbol, &right, &seg_right, &frag_right)) + return 0; + + /* Simplify addition or subtraction of a constant by folding the + constant into X_add_number. */ + if (op == O_add) + { + if (seg_right == absolute_section) + { + final_val += right; + op = O_symbol; + break; + } + else if (seg_left == absolute_section) + { + final_val += left; + left = right; + seg_left = seg_right; + add_symbol = op_symbol; + orig_add_symbol = expressionP->X_op_symbol; + op = O_symbol; + break; + } + } + else if (op == O_subtract) + { + if (seg_right == absolute_section) + { + final_val -= right; + op = O_symbol; + break; + } + } + + /* Equality and non-equality tests are permitted on anything. + Subtraction, and other comparison operators are permitted if + both operands are in the same section. + Shifts by constant zero are permitted on anything. + Multiplies, bit-ors, and bit-ands with constant zero are + permitted on anything. + Multiplies and divides by constant one are permitted on + anything. + Binary operations with both operands being the same register + or undefined symbol are permitted if the result doesn't depend + on the input value. + Otherwise, both operands must be absolute. We already handled + the case of addition or subtraction of a constant above. */ + frag_off = 0; + if (!(seg_left == absolute_section + && seg_right == absolute_section) + && !(op == O_eq || op == O_ne) + && !((op == O_subtract + || op == O_lt || op == O_le || op == O_ge || op == O_gt) + && seg_left == seg_right + && (finalize_syms + || frag_offset_fixed_p (frag_left, frag_right, &frag_off)) + && (seg_left != reg_section || left == right) + && (seg_left != undefined_section || add_symbol == op_symbol))) + { + if ((seg_left == absolute_section && left == 0) + || (seg_right == absolute_section && right == 0)) + { + if (op == O_bit_exclusive_or || op == O_bit_inclusive_or) + { + if (!(seg_right == absolute_section && right == 0)) + { + seg_left = seg_right; + left = right; + add_symbol = op_symbol; + orig_add_symbol = expressionP->X_op_symbol; + } + op = O_symbol; + break; + } + else if (op == O_left_shift || op == O_right_shift) + { + if (!(seg_left == absolute_section && left == 0)) + { + op = O_symbol; + break; + } + } + else if (op != O_multiply + && op != O_bit_or_not && op != O_bit_and) + return 0; + } + else if (op == O_multiply + && seg_left == absolute_section && left == 1) + { + seg_left = seg_right; + left = right; + add_symbol = op_symbol; + orig_add_symbol = expressionP->X_op_symbol; + op = O_symbol; + break; + } + else if ((op == O_multiply || op == O_divide) + && seg_right == absolute_section && right == 1) + { + op = O_symbol; + break; + } + else if (!(left == right + && ((seg_left == reg_section && seg_right == reg_section) + || (seg_left == undefined_section + && seg_right == undefined_section + && add_symbol == op_symbol)))) + return 0; + else if (op == O_bit_and || op == O_bit_inclusive_or) + { + op = O_symbol; + break; + } + else if (op != O_bit_exclusive_or && op != O_bit_or_not) + return 0; + } + + right += frag_off / OCTETS_PER_BYTE; + switch (op) + { + case O_add: left += right; break; + case O_subtract: left -= right; break; + case O_multiply: left *= right; break; + case O_divide: + if (right == 0) + return 0; + left = (offsetT) left / (offsetT) right; + break; + case O_modulus: + if (right == 0) + return 0; + left = (offsetT) left % (offsetT) right; + break; + case O_left_shift: left <<= right; break; + case O_right_shift: left >>= right; break; + case O_bit_inclusive_or: left |= right; break; + case O_bit_or_not: left |= ~right; break; + case O_bit_exclusive_or: left ^= right; break; + case O_bit_and: left &= right; break; + case O_eq: + case O_ne: + left = (left == right + && seg_left == seg_right + && (finalize_syms || frag_left == frag_right) + && (seg_left != undefined_section + || add_symbol == op_symbol) + ? ~ (valueT) 0 : 0); + if (op == O_ne) + left = ~left; + break; + case O_lt: + left = (offsetT) left < (offsetT) right ? ~ (valueT) 0 : 0; + break; + case O_le: + left = (offsetT) left <= (offsetT) right ? ~ (valueT) 0 : 0; + break; + case O_ge: + left = (offsetT) left >= (offsetT) right ? ~ (valueT) 0 : 0; + break; + case O_gt: + left = (offsetT) left > (offsetT) right ? ~ (valueT) 0 : 0; + break; + case O_logical_and: left = left && right; break; + case O_logical_or: left = left || right; break; + default: abort (); + } + + op = O_constant; + break; + } + + if (op == O_symbol) + { + if (seg_left == absolute_section) + op = O_constant; + else if (seg_left == reg_section && final_val == 0) + op = O_register; + else if (!symbol_same_p (add_symbol, orig_add_symbol)) + final_val += left; + expressionP->X_add_symbol = add_symbol; + } + expressionP->X_op = op; + + if (op == O_constant || op == O_register) + final_val += left; + expressionP->X_add_number = final_val; + + return 1; +} + +/* This lives here because it belongs equally in expr.c & read.c. + expr.c is just a branch office read.c anyway, and putting it + here lessens the crowd at read.c. + + Assume input_line_pointer is at start of symbol name. + Advance input_line_pointer past symbol name. + Turn that character into a '\0', returning its former value. + This allows a string compare (RMS wants symbol names to be strings) + of the symbol name. + There will always be a char following symbol name, because all good + lines end in end-of-line. */ + +char +get_symbol_end (void) +{ + char c; + + /* We accept \001 in a name in case this is being called with a + constructed string. */ + if (is_name_beginner (c = *input_line_pointer++) || c == '\001') + { + while (is_part_of_name (c = *input_line_pointer++) + || c == '\001') + ; + if (is_name_ender (c)) + c = *input_line_pointer++; + } + *--input_line_pointer = 0; + return (c); +} + +unsigned int +get_single_number (void) +{ + expressionS exp; + operand (&exp, expr_normal); + return exp.X_add_number; +} diff --git a/contrib/toolchain/binutils/gas/expr.h b/contrib/toolchain/binutils/gas/expr.h new file mode 100644 index 0000000000..438ac0dcc1 --- /dev/null +++ b/contrib/toolchain/binutils/gas/expr.h @@ -0,0 +1,189 @@ +/* expr.h -> header file for expr.c + Copyright 1987, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2002, 2003, 2005, 2007, 2009 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* + * By popular demand, we define a struct to represent an expression. + * This will no doubt mutate as expressions become baroque. + * + * Currently, we support expressions like "foo OP bar + 42". In other + * words we permit a (possibly undefined) symbol, a (possibly + * undefined) symbol and the operation used to combine the symbols, + * and an (absolute) augend. RMS says this is so we can have 1-pass + * assembly for any compiler emissions, and a 'case' statement might + * emit 'undefined1 - undefined2'. + * + * The type of an expression used to be stored as a segment. That got + * confusing because it overloaded the concept of a segment. I added + * an operator field, instead. + */ + +/* This is the type of an expression. The operator types are also + used while parsing an expression. + + NOTE: This enumeration must match the op_rank array in expr.c. */ + +typedef enum { + /* An illegal expression. */ + O_illegal, + /* A nonexistent expression. */ + O_absent, + /* X_add_number (a constant expression). */ + O_constant, + /* X_add_symbol + X_add_number. */ + O_symbol, + /* X_add_symbol + X_add_number - the base address of the image. */ + O_symbol_rva, + /* A register (X_add_number is register number). */ + O_register, + /* A big value. If X_add_number is negative or 0, the value is in + generic_floating_point_number. Otherwise the value is in + generic_bignum, and X_add_number is the number of LITTLENUMs in + the value. */ + O_big, + /* (- X_add_symbol) + X_add_number. */ + O_uminus, + /* (~ X_add_symbol) + X_add_number. */ + O_bit_not, + /* (! X_add_symbol) + X_add_number. */ + O_logical_not, + /* (X_add_symbol * X_op_symbol) + X_add_number. */ + O_multiply, + /* (X_add_symbol / X_op_symbol) + X_add_number. */ + O_divide, + /* (X_add_symbol % X_op_symbol) + X_add_number. */ + O_modulus, + /* (X_add_symbol << X_op_symbol) + X_add_number. */ + O_left_shift, + /* (X_add_symbol >> X_op_symbol) + X_add_number. */ + O_right_shift, + /* (X_add_symbol | X_op_symbol) + X_add_number. */ + O_bit_inclusive_or, + /* (X_add_symbol |~ X_op_symbol) + X_add_number. */ + O_bit_or_not, + /* (X_add_symbol ^ X_op_symbol) + X_add_number. */ + O_bit_exclusive_or, + /* (X_add_symbol & X_op_symbol) + X_add_number. */ + O_bit_and, + /* (X_add_symbol + X_op_symbol) + X_add_number. */ + O_add, + /* (X_add_symbol - X_op_symbol) + X_add_number. */ + O_subtract, + /* (X_add_symbol == X_op_symbol) + X_add_number. */ + O_eq, + /* (X_add_symbol != X_op_symbol) + X_add_number. */ + O_ne, + /* (X_add_symbol < X_op_symbol) + X_add_number. */ + O_lt, + /* (X_add_symbol <= X_op_symbol) + X_add_number. */ + O_le, + /* (X_add_symbol >= X_op_symbol) + X_add_number. */ + O_ge, + /* (X_add_symbol > X_op_symbol) + X_add_number. */ + O_gt, + /* (X_add_symbol && X_op_symbol) + X_add_number. */ + O_logical_and, + /* (X_add_symbol || X_op_symbol) + X_add_number. */ + O_logical_or, + /* X_op_symbol [ X_add_symbol ] */ + O_index, + /* machine dependent operators */ + O_md1, O_md2, O_md3, O_md4, O_md5, O_md6, O_md7, O_md8, + O_md9, O_md10, O_md11, O_md12, O_md13, O_md14, O_md15, O_md16, + O_md17, O_md18, O_md19, O_md20, O_md21, O_md22, O_md23, O_md24, + O_md25, O_md26, O_md27, O_md28, O_md29, O_md30, O_md31, O_md32, + /* this must be the largest value */ + O_max +} operatorT; + +typedef struct expressionS { + /* The main symbol. */ + symbolS *X_add_symbol; + /* The second symbol, if needed. */ + symbolS *X_op_symbol; + /* A number to add. */ + offsetT X_add_number; + + /* The type of the expression. We can't assume that an arbitrary + compiler can handle a bitfield of enum type. FIXME: We could + check this using autoconf. */ +#ifdef __GNUC__ + operatorT X_op : 8; +#else + unsigned char X_op; +#endif + + /* Non-zero if X_add_number should be regarded as unsigned. This is + only valid for O_constant expressions. It is only used when an + O_constant must be extended into a bignum (i.e., it is not used + when performing arithmetic on these values). + FIXME: This field is not set very reliably. */ + unsigned int X_unsigned : 1; + /* This is used to implement "word size + 1 bit" arithmetic, so that e.g. + expressions used with .sleb128 directives can use the full range available + for an unsigned word, but can also properly represent all values of a + signed word. */ + unsigned int X_extrabit : 1; + + /* 7 additional bits can be defined if needed. */ + + /* Machine dependent field */ + unsigned short X_md; +} expressionS; + +enum expr_mode +{ + expr_evaluate, + expr_normal, + expr_defer +}; + +/* "result" should be type (expressionS *). */ +#define expression(result) expr (0, result, expr_normal) +#define expression_and_evaluate(result) expr (0, result, expr_evaluate) +#define deferred_expression(result) expr (0, result, expr_defer) + +/* If an expression is O_big, look here for its value. These common + data may be clobbered whenever expr() is called. */ +/* Flonums returned here. Big enough to hold most precise flonum. */ +extern FLONUM_TYPE generic_floating_point_number; +/* Bignums returned here. */ +extern LITTLENUM_TYPE generic_bignum[]; +/* Number of littlenums in above. */ +#define SIZE_OF_LARGE_NUMBER (20) + +typedef char operator_rankT; + +extern char get_symbol_end (void); +extern void expr_begin (void); +extern void expr_set_precedence (void); +extern void expr_set_rank (operatorT, operator_rankT); +extern void add_to_result (expressionS *, offsetT, int); +extern void subtract_from_result (expressionS *, offsetT, int); +extern segT expr (int, expressionS *, enum expr_mode); +extern unsigned int get_single_number (void); +extern symbolS *make_expr_symbol (expressionS * expressionP); +extern int expr_symbol_where (symbolS *, char **, unsigned int *); +extern void current_location (expressionS *); + +extern symbolS *expr_build_uconstant (offsetT); +extern symbolS *expr_build_dot (void); + +int resolve_expression (expressionS *); diff --git a/contrib/toolchain/binutils/gas/flonum-copy.c b/contrib/toolchain/binutils/gas/flonum-copy.c new file mode 100644 index 0000000000..c1131b1250 --- /dev/null +++ b/contrib/toolchain/binutils/gas/flonum-copy.c @@ -0,0 +1,71 @@ +/* flonum_copy.c - copy a flonum + Copyright 1987, 1990, 1991, 1992, 1993, 2000, 2003, 2005, 2007 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" + +void +flonum_copy (FLONUM_TYPE *in, FLONUM_TYPE *out) +{ + unsigned int in_length; /* 0 origin */ + unsigned int out_length; /* 0 origin */ + + out->sign = in->sign; + in_length = in->leader - in->low; + + if (in->leader < in->low) + { + out->leader = out->low - 1; /* 0.0 case */ + } + else + { + out_length = out->high - out->low; + /* Assume no GAPS in packing of littlenums. + I.e. sizeof(array) == sizeof(element) * number_of_elements. */ + if (in_length <= out_length) + { + { + /* For defensive programming, zero any high-order + littlenums we don't need. This is destroying evidence + and wasting time, so why bother??? */ + if (in_length < out_length) + { + memset ((char *) (out->low + in_length + 1), '\0', + out_length - in_length); + } + } + memcpy ((void *) (out->low), (void *) (in->low), + ((in_length + 1) * sizeof (LITTLENUM_TYPE))); + out->exponent = in->exponent; + out->leader = in->leader - in->low + out->low; + } + else + { + int shorten; /* 1-origin. Number of littlenums we drop. */ + + shorten = in_length - out_length; + /* Assume out_length >= 0 ! */ + memcpy ((void *) (out->low), (void *) (in->low + shorten), + ((out_length + 1) * sizeof (LITTLENUM_TYPE))); + out->leader = out->high; + out->exponent = in->exponent + shorten; + } + } /* if any significant bits */ +} diff --git a/contrib/toolchain/binutils/gas/flonum-konst.c b/contrib/toolchain/binutils/gas/flonum-konst.c new file mode 100644 index 0000000000..cb60beaa66 --- /dev/null +++ b/contrib/toolchain/binutils/gas/flonum-konst.c @@ -0,0 +1,228 @@ +/* flonum_const.c - Useful Flonum constants + Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 2000, 2002, + 2005, 2007 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "ansidecl.h" +#include "flonum.h" +/* JF: I added the last entry to this table, and I'm not + sure if its right or not. Could go either way. I wish + I really understood this stuff. */ + +const int table_size_of_flonum_powers_of_ten = 13; + +static const LITTLENUM_TYPE zero[] = { + 1 +}; + +/***********************************************************************\ + * * + * Warning: the low order bits may be WRONG here. * + * I took this from a suspect bc(1) script. * + * "minus_X"[] is supposed to be 10^(2^-X) expressed in base 2^16. * + * The radix point is just AFTER the highest element of the [] * + * * + * Because bc rounds DOWN for printing (I think), the lowest * + * significance littlenums should probably have 1 added to them. * + * * + \***********************************************************************/ + +/* JF: If this equals 6553/(2^16)+39321/(2^32)+... it approaches .1 */ +static const LITTLENUM_TYPE minus_1[] = { + 39322, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, + 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 39321, 6553 +}; + +static const LITTLENUM_TYPE plus_1[] = { + 10 +}; + +/* JF: If this equals 655/(2^16) + 23592/(2^32) + ... it approaches .01 */ +static const LITTLENUM_TYPE minus_2[] = { + 10486, 36700, 62914, 23592, 49807, 10485, 36700, 62914, 23592, 49807, + 10485, 36700, 62914, 23592, 49807, 10485, 36700, 62914, 23592, 655 +}; + +static const LITTLENUM_TYPE plus_2[] = { + 100 +}; + +/* This approaches .0001 */ +static const LITTLENUM_TYPE minus_3[] = { + 52534, 20027, 37329, 65116, 64067, 60397, 14784, 18979, 33659, 19503, + 2726, 9542, 629, 2202, 40475, 10590, 4299, 47815, 36280, 6 +}; + +static const LITTLENUM_TYPE plus_3[] = { + 10000 +}; + +/* JF: this approaches 1e-8 */ +static const LITTLENUM_TYPE minus_4[] = { + 22517, 49501, 54293, 19424, 60699, 6716, 24348, 22618, 23904, 21327, + 3919, 44703, 19149, 28803, 48959, 6259, 50273, 62237, 42 +}; + +/* This equals 1525 * 2^16 + 57600 */ +static const LITTLENUM_TYPE plus_4[] = { + 57600, 1525 +}; + +/* This approaches 1e-16 */ +static const LITTLENUM_TYPE minus_5[] = { + 22199, 45957, 17005, 26266, 10526, 16260, 55017, 35680, 40443, 19789, + 17356, 30195, 55905, 28426, 63010, 44197, 1844 +}; + +static const LITTLENUM_TYPE plus_5[] = { + 28609, 34546, 35 +}; + +static const LITTLENUM_TYPE minus_6[] = { + 30926, 26518, 13110, 43018, 54982, 48258, 24658, 15209, 63366, 11929, + 20069, 43857, 60487, 51 +}; + +static const LITTLENUM_TYPE plus_6[] = { + 61313, 34220, 16731, 11629, 1262 +}; + +static const LITTLENUM_TYPE minus_7[] = { + 29819, 14733, 21490, 40602, 31315, 65186, 2695 +}; + +static const LITTLENUM_TYPE plus_7[] = { + 7937, 49002, 60772, 28216, 38893, 55975, 63988, 59711, 20227, 24 +}; + +static const LITTLENUM_TYPE minus_8[] = { + 27579, 64807, 12543, 794, 13907, 61297, 12013, 64360, 15961, 20566, + 24178, 15922, 59427, 110 +}; + +static const LITTLENUM_TYPE plus_8[] = { + 15873, 11925, 39177, 991, 14589, 3861, 58415, 9076, 62956, 54223, + 56328, 50180, 45274, 48333, 32537, 42547, 9731, 59679, 590 +}; + +static const LITTLENUM_TYPE minus_9[] = { + 11042, 8464, 58971, 63429, 6022, 63485, 5500, 53464, 47545, 50068, + 56988, 22819, 49708, 54493, 9920, 47667, 40409, 35764, 10383, 54466, + 32702, 17493, 32420, 34382, 22750, 20681, 12300 +}; + +static const LITTLENUM_TYPE plus_9[] = { + 20678, 27614, 28272, 53066, 55311, 54677, 29038, 9906, 26288, 44486, + 13860, 7445, 54106, 15426, 21518, 25599, 29632, 52309, 61207, 26105, + 10482, 21948, 51191, 32988, 60892, 62574, 61390, 24540, 21495, 5 +}; + +static const LITTLENUM_TYPE minus_10[] = { + 6214, 48771, 23471, 30163, 31763, 38013, 57001, 11770, 18263, 36366, + 20742, 45086, 56969, 53231, 37856, 55814, 38057, 15692, 46761, 8713, + 6102, 20083, 8269, 11839, 11571, 50963, 15649, 11698, 40675, 2308 +}; + +static const LITTLENUM_TYPE plus_10[] = { + 63839, 36576, 45712, 44516, 37803, 29482, 4966, 30556, 37961, 23310, + 27070, 44972, 29507, 48257, 45209, 7494, 17831, 38728, 41577, 29443, + 36016, 7955, 35339, 35479, 36011, 14553, 49618, 5588, 25396, 28 +}; + +static const LITTLENUM_TYPE minus_11[] = { + 16663, 56882, 61983, 7804, 36555, 32060, 34502, 1000, 14356, 21681, + 6605, 34767, 51411, 59048, 53614, 39850, 30079, 6496, 6846, 26841, + 40778, 19578, 59899, 44085, 54016, 24259, 11232, 21229, 21313, 81 +}; + +static const LITTLENUM_TYPE plus_11[] = { + 92, 9054, 62707, 17993, 7821, 56838, 13992, 21321, 29637, 48426, + 42982, 38668, 49574, 28820, 18200, 18927, 53979, 16219, 37484, 2516, + 44642, 14665, 11587, 41926, 13556, 23956, 54320, 6661, 55766, 805 +}; + +static const LITTLENUM_TYPE minus_12[] = { + 33202, 45969, 58804, 56734, 16482, 26007, 44984, 49334, 31007, 32944, + 44517, 63329, 47131, 15291, 59465, 2264, 23218, 11829, 59771, 38798, + 31051, 28748, 23129, 40541, 41562, 35108, 50620, 59014, 51817, 6613 +}; + +static const LITTLENUM_TYPE plus_12[] = { + 10098, 37922, 58070, 7432, 10470, 63465, 23718, 62190, 47420, 7009, + 38443, 4587, 45596, 38472, 52129, 52779, 29012, 13559, 48688, 31678, + 41753, 58662, 10668, 36067, 29906, 56906, 21461, 46556, 59571, 9 +}; + +static const LITTLENUM_TYPE minus_13[] = { + 45309, 27592, 37144, 34637, 34328, 41671, 34620, 24135, 53401, 22112, + 21576, 45147, 39310, 44051, 48572, 3676, 46544, 59768, 33350, 2323, + 49524, 61568, 3903, 36487, 36356, 30903, 14975, 9035, 29715, 667 +}; + +static const LITTLENUM_TYPE plus_13[] = { + 18788, 16960, 6318, 45685, 55400, 46230, 35794, 25588, 7253, 55541, + 49716, 59760, 63592, 8191, 63765, 58530, 44667, 13294, 10001, 55586, + 47887, 18738, 9509, 40896, 42506, 52580, 4171, 325, 12329, 98 +}; + +/* Shut up complaints about differing pointer types. They only differ + in the const attribute, but there isn't any easy way to do this + */ +#define X (LITTLENUM_TYPE *) + +const FLONUM_TYPE flonum_negative_powers_of_ten[] = { + {X zero, X zero, X zero, 0, '+'}, + {X minus_1, X minus_1 + 19, X minus_1 + 19, -20, '+'}, + {X minus_2, X minus_2 + 19, X minus_2 + 19, -20, '+'}, + {X minus_3, X minus_3 + 19, X minus_3 + 19, -20, '+'}, + {X minus_4, X minus_4 + 18, X minus_4 + 18, -20, '+'}, + {X minus_5, X minus_5 + 16, X minus_5 + 16, -20, '+'}, + {X minus_6, X minus_6 + 13, X minus_6 + 13, -20, '+'}, + {X minus_7, X minus_7 + 6, X minus_7 + 6, -20, '+'}, + {X minus_8, X minus_8 + 13, X minus_8 + 13, -40, '+'}, + {X minus_9, X minus_9 + 26, X minus_9 + 26, -80, '+'}, + {X minus_10, X minus_10 + 29, X minus_10 + 29, -136, '+'}, + {X minus_11, X minus_11 + 29, X minus_11 + 29, -242, '+'}, + {X minus_12, X minus_12 + 29, X minus_12 + 29, -455, '+'}, + {X minus_13, X minus_13 + 29, X minus_13 + 29, -880, '+'}, +}; + +const FLONUM_TYPE flonum_positive_powers_of_ten[] = { + {X zero, X zero, X zero, 0, '+'}, + {X plus_1, X plus_1 + 0, X plus_1 + 0, 0, '+'}, + {X plus_2, X plus_2 + 0, X plus_2 + 0, 0, '+'}, + {X plus_3, X plus_3 + 0, X plus_3 + 0, 0, '+'}, + {X plus_4, X plus_4 + 1, X plus_4 + 1, 0, '+'}, + {X plus_5, X plus_5 + 2, X plus_5 + 2, 1, '+'}, + {X plus_6, X plus_6 + 4, X plus_6 + 4, 2, '+'}, + {X plus_7, X plus_7 + 9, X plus_7 + 9, 4, '+'}, + {X plus_8, X plus_8 + 18, X plus_8 + 18, 8, '+'}, + {X plus_9, X plus_9 + 29, X plus_9 + 29, 24, '+'}, + {X plus_10, X plus_10 + 29, X plus_10 + 29, 77, '+'}, + {X plus_11, X plus_11 + 29, X plus_11 + 29, 183, '+'}, + {X plus_12, X plus_12 + 29, X plus_12 + 29, 396, '+'}, + {X plus_13, X plus_13 + 29, X plus_13 + 29, 821, '+'}, +}; + +#ifdef VMS +void +dummy1 () +{ +} +#endif diff --git a/contrib/toolchain/binutils/gas/flonum-mult.c b/contrib/toolchain/binutils/gas/flonum-mult.c new file mode 100644 index 0000000000..e5f32fca2e --- /dev/null +++ b/contrib/toolchain/binutils/gas/flonum-mult.c @@ -0,0 +1,188 @@ +/* flonum_mult.c - multiply two flonums + Copyright 1987, 1990, 1991, 1992, 1995, 2000, 2002, 2003, 2007 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "ansidecl.h" +#include "flonum.h" + +/* plan for a . b => p(roduct) + + +-------+-------+-/ /-+-------+-------+ + | a | a | ... | a | a | + | A | A-1 | | 1 | 0 | + +-------+-------+-/ /-+-------+-------+ + + +-------+-------+-/ /-+-------+-------+ + | b | b | ... | b | b | + | B | B-1 | | 1 | 0 | + +-------+-------+-/ /-+-------+-------+ + + +-------+-------+-/ /-+-------+-/ /-+-------+-------+ + | p | p | ... | p | ... | p | p | + | A+B+1| A+B | | N | | 1 | 0 | + +-------+-------+-/ /-+-------+-/ /-+-------+-------+ + + /^\ + (carry) a .b ... | ... a .b a .b + A B | 0 1 0 0 + | + ... | ... a .b + | 1 0 + | + | ... + | + | + | + | ___ + | \ + +----- P = > a .b + N /__ i j + + N = 0 ... A+B + + for all i,j where i+j=N + [i,j integers > 0] + + a[], b[], p[] may not intersect. + Zero length factors signify 0 significant bits: treat as 0.0. + 0.0 factors do the right thing. + Zero length product OK. + + I chose the ForTran accent "foo[bar]" instead of the C accent "*garply" + because I felt the ForTran way was more intuitive. The C way would + probably yield better code on most C compilers. Dean Elsner. + (C style also gives deeper insight [to me] ... oh well ...) */ + +void +flonum_multip (const FLONUM_TYPE *a, const FLONUM_TYPE *b, + FLONUM_TYPE *product) +{ + int size_of_a; /* 0 origin */ + int size_of_b; /* 0 origin */ + int size_of_product; /* 0 origin */ + int size_of_sum; /* 0 origin */ + int extra_product_positions; /* 1 origin */ + unsigned long work; + unsigned long carry; + long exponent; + LITTLENUM_TYPE *q; + long significant; /* TRUE when we emit a non-0 littlenum */ + /* ForTran accent follows. */ + int P; /* Scan product low-order -> high. */ + int N; /* As in sum above. */ + int A; /* Which [] of a? */ + int B; /* Which [] of b? */ + + if ((a->sign != '-' && a->sign != '+') + || (b->sign != '-' && b->sign != '+')) + { + /* Got to fail somehow. Any suggestions? */ + product->sign = 0; + return; + } + product->sign = (a->sign == b->sign) ? '+' : '-'; + size_of_a = a->leader - a->low; + size_of_b = b->leader - b->low; + exponent = a->exponent + b->exponent; + size_of_product = product->high - product->low; + size_of_sum = size_of_a + size_of_b; + extra_product_positions = size_of_product - size_of_sum; + if (extra_product_positions < 0) + { + P = extra_product_positions; /* P < 0 */ + exponent -= extra_product_positions; /* Increases exponent. */ + } + else + { + P = 0; + } + carry = 0; + significant = 0; + for (N = 0; N <= size_of_sum; N++) + { + work = carry; + carry = 0; + for (A = 0; A <= N; A++) + { + B = N - A; + if (A <= size_of_a && B <= size_of_b && B >= 0) + { +#ifdef TRACE + printf ("a:low[%d.]=%04x b:low[%d.]=%04x work_before=%08x\n", + A, a->low[A], B, b->low[B], work); +#endif + /* Watch out for sign extension! Without the casts, on + the DEC Alpha, the multiplication result is *signed* + int, which gets sign-extended to convert to the + unsigned long! */ + work += (unsigned long) a->low[A] * (unsigned long) b->low[B]; + carry += work >> LITTLENUM_NUMBER_OF_BITS; + work &= LITTLENUM_MASK; +#ifdef TRACE + printf ("work=%08x carry=%04x\n", work, carry); +#endif + } + } + significant |= work; + if (significant || P < 0) + { + if (P >= 0) + { + product->low[P] = work; +#ifdef TRACE + printf ("P=%d. work[p]:=%04x\n", P, work); +#endif + } + P++; + } + else + { + extra_product_positions++; + exponent++; + } + } + /* [P]-> position # size_of_sum + 1. + This is where 'carry' should go. */ +#ifdef TRACE + printf ("final carry =%04x\n", carry); +#endif + if (carry) + { + if (extra_product_positions > 0) + product->low[P] = carry; + else + { + /* No room at high order for carry littlenum. */ + /* Shift right 1 to make room for most significant littlenum. */ + exponent++; + P--; + for (q = product->low + P; q >= product->low; q--) + { + work = *q; + *q = carry; + carry = work; + } + } + } + else + P--; + product->leader = product->low + P; + product->exponent = exponent; +} diff --git a/contrib/toolchain/binutils/gas/flonum.h b/contrib/toolchain/binutils/gas/flonum.h new file mode 100644 index 0000000000..c27dd22235 --- /dev/null +++ b/contrib/toolchain/binutils/gas/flonum.h @@ -0,0 +1,102 @@ +/* flonum.h - Floating point package + Copyright 1987, 1990, 1991, 1992, 1994, 1996, 2000, 2003, 2005, 2007 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/***********************************************************************\ + * * + * Arbitrary-precision floating point arithmetic. * + * * + * * + * Notation: a floating point number is expressed as * + * MANTISSA * (2 ** EXPONENT). * + * * + * If this offends more traditional mathematicians, then * + * please tell me your nomenclature for flonums! * + * * + \***********************************************************************/ + +#include "bignum.h" + +/***********************************************************************\ + * * + * Variable precision floating point numbers. * + * * + * Exponent is the place value of the low littlenum. E.g.: * + * If 0: low points to the units littlenum. * + * If 1: low points to the LITTLENUM_RADIX littlenum. * + * If -1: low points to the 1/LITTLENUM_RADIX littlenum. * + * * + \***********************************************************************/ + +/* JF: A sign value of 0 means we have been asked to assemble NaN + A sign value of 'P' means we've been asked to assemble +Inf + A sign value of 'N' means we've been asked to assemble -Inf + */ +struct FLONUM_STRUCT { + LITTLENUM_TYPE *low; /* low order littlenum of a bignum */ + LITTLENUM_TYPE *high; /* high order littlenum of a bignum */ + LITTLENUM_TYPE *leader; /* -> 1st non-zero littlenum */ + /* If flonum is 0.0, leader==low-1 */ + long exponent; /* base LITTLENUM_RADIX */ + char sign; /* '+' or '-' */ +}; + +typedef struct FLONUM_STRUCT FLONUM_TYPE; + +/***********************************************************************\ + * * + * Since we can (& do) meet with exponents like 10^5000, it * + * is silly to make a table of ~ 10,000 entries, one for each * + * power of 10. We keep a table where item [n] is a struct * + * FLONUM_FLOATING_POINT representing 10^(2^n). We then * + * multiply appropriate entries from this table to get any * + * particular power of 10. For the example of 10^5000, a table * + * of just 25 entries suffices: 10^(2^-12)...10^(2^+12). * + * * + \***********************************************************************/ + +extern const FLONUM_TYPE flonum_positive_powers_of_ten[]; +extern const FLONUM_TYPE flonum_negative_powers_of_ten[]; +extern const int table_size_of_flonum_powers_of_ten; +/* Flonum_XXX_powers_of_ten[] table has legal indices from 0 to + + this number inclusive. */ + +/***********************************************************************\ + * * + * Declare worker functions. * + * * + \***********************************************************************/ + +int atof_generic (char **address_of_string_pointer, + const char *string_of_decimal_marks, + const char *string_of_decimal_exponent_marks, + FLONUM_TYPE * address_of_generic_floating_point_number); + +void flonum_copy (FLONUM_TYPE * in, FLONUM_TYPE * out); +void flonum_multip (const FLONUM_TYPE * a, const FLONUM_TYPE * b, + FLONUM_TYPE * product); + +/***********************************************************************\ + * * + * Declare error codes. * + * * + \***********************************************************************/ + +#define ERROR_EXPONENT_OVERFLOW (2) diff --git a/contrib/toolchain/binutils/gas/frags.c b/contrib/toolchain/binutils/gas/frags.c new file mode 100644 index 0000000000..beb251bb01 --- /dev/null +++ b/contrib/toolchain/binutils/gas/frags.c @@ -0,0 +1,445 @@ +/* frags.c - manage frags - + Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, 2012 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" +#include "subsegs.h" +#include "obstack.h" + +extern fragS zero_address_frag; +extern fragS predefined_address_frag; + +/* Initialization for frag routines. */ + +void +frag_init (void) +{ + zero_address_frag.fr_type = rs_fill; + predefined_address_frag.fr_type = rs_fill; +} + +/* Check that we're not trying to assemble into a section that can't + allocate frags (currently, this is only possible in the absolute + section), or into an mri common. */ + +static void +frag_alloc_check (const struct obstack *ob) +{ + if (ob->chunk_size == 0) + { + as_bad (_("attempt to allocate data in absolute section")); + subseg_set (text_section, 0); + } + + if (mri_common_symbol != NULL) + { + as_bad (_("attempt to allocate data in common section")); + mri_common_symbol = NULL; + } +} + +/* Allocate a frag on the specified obstack. + Call this routine from everywhere else, so that all the weird alignment + hackery can be done in just one place. */ + +fragS * +frag_alloc (struct obstack *ob) +{ + fragS *ptr; + int oalign; + + (void) obstack_alloc (ob, 0); + oalign = obstack_alignment_mask (ob); + obstack_alignment_mask (ob) = 0; + ptr = (fragS *) obstack_alloc (ob, SIZEOF_STRUCT_FRAG); + obstack_alignment_mask (ob) = oalign; + memset (ptr, 0, SIZEOF_STRUCT_FRAG); + return ptr; +} + +/* Try to augment current frag by nchars chars. + If there is no room, close of the current frag with a ".fill 0" + and begin a new frag. Unless the new frag has nchars chars available + do not return. Do not set up any fields of *now_frag. */ + +void +frag_grow (unsigned int nchars) +{ + if (obstack_room (&frchain_now->frch_obstack) < nchars) + { + long oldc; + long newc; + + /* Try to allocate a bit more than needed right now. But don't do + this if we would waste too much memory. Especially necessary + for extremely big (like 2GB initialized) frags. */ + if (nchars < 0x10000) + newc = 2 * nchars; + else + newc = nchars + 0x10000; + newc += SIZEOF_STRUCT_FRAG; + + /* Check for possible overflow. */ + if (newc < 0) + as_fatal (_("can't extend frag %u chars"), nchars); + + /* Force to allocate at least NEWC bytes, but not less than the + default. */ + oldc = obstack_chunk_size (&frchain_now->frch_obstack); + if (newc > oldc) + obstack_chunk_size (&frchain_now->frch_obstack) = newc; + + while (obstack_room (&frchain_now->frch_obstack) < nchars) + { + /* Not enough room in this frag. Close it and start a new one. + This must be done in a loop because the created frag may not + be big enough if the current obstack chunk is used. */ + frag_wane (frag_now); + frag_new (0); + } + + /* Restore the old chunk size. */ + obstack_chunk_size (&frchain_now->frch_obstack) = oldc; + } +} + +/* Call this to close off a completed frag, and start up a new (empty) + frag, in the same subsegment as the old frag. + [frchain_now remains the same but frag_now is updated.] + Because this calculates the correct value of fr_fix by + looking at the obstack 'frags', it needs to know how many + characters at the end of the old frag belong to the maximal + variable part; The rest must belong to fr_fix. + It doesn't actually set up the old frag's fr_var. You may have + set fr_var == 1, but allocated 10 chars to the end of the frag; + In this case you pass old_frags_var_max_size == 10. + In fact, you may use fr_var for something totally unrelated to the + size of the variable part of the frag; None of the generic frag + handling code makes use of fr_var. + + Make a new frag, initialising some components. Link new frag at end + of frchain_now. */ + +void +frag_new (int old_frags_var_max_size + /* Number of chars (already allocated on obstack frags) in + variable_length part of frag. */) +{ + fragS *former_last_fragP; + frchainS *frchP; + + gas_assert (frchain_now->frch_last == frag_now); + + /* Fix up old frag's fr_fix. */ + frag_now->fr_fix = frag_now_fix_octets () - old_frags_var_max_size; + /* Make sure its type is valid. */ + gas_assert (frag_now->fr_type != 0); + + /* This will align the obstack so the next struct we allocate on it + will begin at a correct boundary. */ + obstack_finish (&frchain_now->frch_obstack); + frchP = frchain_now; + know (frchP); + former_last_fragP = frchP->frch_last; + gas_assert (former_last_fragP != 0); + gas_assert (former_last_fragP == frag_now); + frag_now = frag_alloc (&frchP->frch_obstack); + + as_where (&frag_now->fr_file, &frag_now->fr_line); + + /* Generally, frag_now->points to an address rounded up to next + alignment. However, characters will add to obstack frags + IMMEDIATELY after the struct frag, even if they are not starting + at an alignment address. */ + former_last_fragP->fr_next = frag_now; + frchP->frch_last = frag_now; + +#ifndef NO_LISTING + { + extern struct list_info_struct *listing_tail; + frag_now->line = listing_tail; + } +#endif + + gas_assert (frchain_now->frch_last == frag_now); + + frag_now->fr_next = NULL; +} + +/* Start a new frag unless we have n more chars of room in the current frag. + Close off the old frag with a .fill 0. + + Return the address of the 1st char to write into. Advance + frag_now_growth past the new chars. */ + +char * +frag_more (int nchars) +{ + register char *retval; + + frag_alloc_check (&frchain_now->frch_obstack); + frag_grow (nchars); + retval = obstack_next_free (&frchain_now->frch_obstack); + obstack_blank_fast (&frchain_now->frch_obstack, nchars); + return (retval); +} + +/* Close the current frag, setting its fields for a relaxable frag. Start a + new frag. */ + +static void +frag_var_init (relax_stateT type, int max_chars, int var, + relax_substateT subtype, symbolS *symbol, offsetT offset, + char *opcode) +{ + frag_now->fr_var = var; + frag_now->fr_type = type; + frag_now->fr_subtype = subtype; + frag_now->fr_symbol = symbol; + frag_now->fr_offset = offset; + frag_now->fr_opcode = opcode; +#ifdef USING_CGEN + frag_now->fr_cgen.insn = 0; + frag_now->fr_cgen.opindex = 0; + frag_now->fr_cgen.opinfo = 0; +#endif +#ifdef TC_FRAG_INIT + TC_FRAG_INIT (frag_now); +#endif + as_where (&frag_now->fr_file, &frag_now->fr_line); + + frag_new (max_chars); +} + +/* Start a new frag unless we have max_chars more chars of room in the + current frag. Close off the old frag with a .fill 0. + + Set up a machine_dependent relaxable frag, then start a new frag. + Return the address of the 1st char of the var part of the old frag + to write into. */ + +char * +frag_var (relax_stateT type, int max_chars, int var, relax_substateT subtype, + symbolS *symbol, offsetT offset, char *opcode) +{ + register char *retval; + + frag_grow (max_chars); + retval = obstack_next_free (&frchain_now->frch_obstack); + obstack_blank_fast (&frchain_now->frch_obstack, max_chars); + frag_var_init (type, max_chars, var, subtype, symbol, offset, opcode); + return retval; +} + +/* OVE: This variant of frag_var assumes that space for the tail has been + allocated by caller. + No call to frag_grow is done. */ + +char * +frag_variant (relax_stateT type, int max_chars, int var, + relax_substateT subtype, symbolS *symbol, offsetT offset, + char *opcode) +{ + register char *retval; + + retval = obstack_next_free (&frchain_now->frch_obstack); + frag_var_init (type, max_chars, var, subtype, symbol, offset, opcode); + + return retval; +} + +/* Reduce the variable end of a frag to a harmless state. */ + +void +frag_wane (register fragS *fragP) +{ + fragP->fr_type = rs_fill; + fragP->fr_offset = 0; + fragP->fr_var = 0; +} + +/* Return the number of bytes by which the current frag can be grown. */ + +int +frag_room (void) +{ + return obstack_room (&frchain_now->frch_obstack); +} + +/* Make an alignment frag. The size of this frag will be adjusted to + force the next frag to have the appropriate alignment. ALIGNMENT + is the power of two to which to align. FILL_CHARACTER is the + character to use to fill in any bytes which are skipped. MAX is + the maximum number of characters to skip when doing the alignment, + or 0 if there is no maximum. */ + +void +frag_align (int alignment, int fill_character, int max) +{ + if (now_seg == absolute_section) + { + addressT new_off; + addressT mask; + + mask = (~(addressT) 0) << alignment; + new_off = (abs_section_offset + ~mask) & mask; + if (max == 0 || new_off - abs_section_offset <= (addressT) max) + abs_section_offset = new_off; + } + else + { + char *p; + + p = frag_var (rs_align, 1, 1, (relax_substateT) max, + (symbolS *) 0, (offsetT) alignment, (char *) 0); + *p = fill_character; + } +} + +/* Make an alignment frag like frag_align, but fill with a repeating + pattern rather than a single byte. ALIGNMENT is the power of two + to which to align. FILL_PATTERN is the fill pattern to repeat in + the bytes which are skipped. N_FILL is the number of bytes in + FILL_PATTERN. MAX is the maximum number of characters to skip when + doing the alignment, or 0 if there is no maximum. */ + +void +frag_align_pattern (int alignment, const char *fill_pattern, + int n_fill, int max) +{ + char *p; + + p = frag_var (rs_align, n_fill, n_fill, (relax_substateT) max, + (symbolS *) 0, (offsetT) alignment, (char *) 0); + memcpy (p, fill_pattern, n_fill); +} + +/* The NOP_OPCODE is for the alignment fill value. Fill it with a nop + instruction so that the disassembler does not choke on it. */ +#ifndef NOP_OPCODE +#define NOP_OPCODE 0x00 +#endif + +/* Use this to restrict the amount of memory allocated for representing + the alignment code. Needs to be large enough to hold any fixed sized + prologue plus the replicating portion. */ +#ifndef MAX_MEM_FOR_RS_ALIGN_CODE + /* Assume that if HANDLE_ALIGN is not defined then no special action + is required to code fill, which means that we get just repeat the + one NOP_OPCODE byte. */ +# ifndef HANDLE_ALIGN +# define MAX_MEM_FOR_RS_ALIGN_CODE 1 +# else +# define MAX_MEM_FOR_RS_ALIGN_CODE ((1 << alignment) - 1) +# endif +#endif + +void +frag_align_code (int alignment, int max) +{ + char *p; + + p = frag_var (rs_align_code, MAX_MEM_FOR_RS_ALIGN_CODE, 1, + (relax_substateT) max, (symbolS *) 0, + (offsetT) alignment, (char *) 0); + *p = NOP_OPCODE; +} + +addressT +frag_now_fix_octets (void) +{ + if (now_seg == absolute_section) + return abs_section_offset; + + return ((char *) obstack_next_free (&frchain_now->frch_obstack) + - frag_now->fr_literal); +} + +addressT +frag_now_fix (void) +{ + return frag_now_fix_octets () / OCTETS_PER_BYTE; +} + +void +frag_append_1_char (int datum) +{ + frag_alloc_check (&frchain_now->frch_obstack); + if (obstack_room (&frchain_now->frch_obstack) <= 1) + { + frag_wane (frag_now); + frag_new (0); + } + obstack_1grow (&frchain_now->frch_obstack, datum); +} + +/* Return TRUE if FRAG1 and FRAG2 have a fixed relationship between + their start addresses. Set OFFSET to the difference in address + not already accounted for in the frag FR_ADDRESS. */ + +bfd_boolean +frag_offset_fixed_p (const fragS *frag1, const fragS *frag2, offsetT *offset) +{ + const fragS *frag; + offsetT off; + + /* Start with offset initialised to difference between the two frags. + Prior to assigning frag addresses this will be zero. */ + off = frag1->fr_address - frag2->fr_address; + if (frag1 == frag2) + { + *offset = off; + return TRUE; + } + + /* Maybe frag2 is after frag1. */ + frag = frag1; + while (frag->fr_type == rs_fill) + { + off += frag->fr_fix + frag->fr_offset * frag->fr_var; + frag = frag->fr_next; + if (frag == NULL) + break; + if (frag == frag2) + { + *offset = off; + return TRUE; + } + } + + /* Maybe frag1 is after frag2. */ + off = frag1->fr_address - frag2->fr_address; + frag = frag2; + while (frag->fr_type == rs_fill) + { + off -= frag->fr_fix + frag->fr_offset * frag->fr_var; + frag = frag->fr_next; + if (frag == NULL) + break; + if (frag == frag1) + { + *offset = off; + return TRUE; + } + } + + return FALSE; +} diff --git a/contrib/toolchain/binutils/gas/frags.h b/contrib/toolchain/binutils/gas/frags.h new file mode 100644 index 0000000000..f5846be5a5 --- /dev/null +++ b/contrib/toolchain/binutils/gas/frags.h @@ -0,0 +1,160 @@ +/* frags.h - Header file for the frag concept. + Copyright 1987, 1992, 1993, 1994, 1995, 1997, 1998, 1999, 2000, 2001, + 2002, 2003, 2004, 2005, 2006, 2007, 2010, 2011, 2012 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef FRAGS_H +#define FRAGS_H + +struct obstack; + +/* A code fragment (frag) is some known number of chars, followed by some + unknown number of chars. Typically the unknown number of chars is an + instruction address whose size is yet unknown. We always know the greatest + possible size the unknown number of chars may become, and reserve that + much room at the end of the frag. + Once created, frags do not change address during assembly. + We chain the frags in (a) forward-linked list(s). The object-file address + of the 1st char of a frag is generally not known until after relax(). + Many things at assembly time describe an address by {object-file-address + of a particular frag}+offset. + + BUG: it may be smarter to have a single pointer off to various different + notes for different frag kinds. See how code pans. */ + +struct frag { + /* Object file address (as an octet offset). */ + addressT fr_address; + /* When relaxing multiple times, remember the address the frag had + in the last relax pass. */ + addressT last_fr_address; + + /* (Fixed) number of octets we know we have. May be 0. */ + offsetT fr_fix; + /* May be used for (Variable) number of octets after above. + The generic frag handling code no longer makes any use of fr_var. */ + offsetT fr_var; + /* For variable-length tail. */ + offsetT fr_offset; + /* For variable-length tail. */ + symbolS *fr_symbol; + /* Points to opcode low addr byte, for relaxation. */ + char *fr_opcode; + + /* Chain forward; ascending address order. Rooted in frch_root. */ + struct frag *fr_next; + + /* Where the frag was created, or where it became a variant frag. */ + char *fr_file; + unsigned int fr_line; + +#ifndef NO_LISTING + struct list_info_struct *line; +#endif + + /* A serial number for a sequence of frags having at most one alignment + or org frag, and that at the tail of the sequence. */ + unsigned int region:16; + + /* Flipped each relax pass so we can easily determine whether + fr_address has been adjusted. */ + unsigned int relax_marker:1; + + /* Used to ensure that all insns are emitted on proper address + boundaries. */ + unsigned int has_code:1; + unsigned int insn_addr:6; + + /* What state is my tail in? */ + relax_stateT fr_type; + relax_substateT fr_subtype; + +#ifdef USING_CGEN + /* Don't include this unless using CGEN to keep frag size down. */ + struct { + /* CGEN_INSN entry for this instruction. */ + const struct cgen_insn *insn; + /* Index into operand table. */ + int opindex; + /* Target specific data, usually reloc number. */ + int opinfo; + } fr_cgen; +#endif + +#ifdef TC_FRAG_TYPE + TC_FRAG_TYPE tc_frag_data; +#endif +#ifdef OBJ_FRAG_TYPE + OBJ_FRAG_TYPE obj_frag_data; +#endif + + /* Data begins here. */ + char fr_literal[1]; +}; + +#define SIZEOF_STRUCT_FRAG \ +((char *) zero_address_frag.fr_literal - (char *) &zero_address_frag) +/* We want to say fr_literal[0] above. */ + +/* Current frag we are building. This frag is incomplete. It is, + however, included in frchain_now. The fr_fix field is bogus; + instead, use frag_now_fix (). */ +COMMON fragS *frag_now; +extern addressT frag_now_fix (void); +extern addressT frag_now_fix_octets (void); + +/* For foreign-segment symbol fixups. */ +COMMON fragS zero_address_frag; +COMMON fragS predefined_address_frag; + +extern void frag_append_1_char (int); +#define FRAG_APPEND_1_CHAR(X) frag_append_1_char (X) + +void frag_init (void); +fragS *frag_alloc (struct obstack *); +void frag_grow (unsigned int nchars); +char *frag_more (int nchars); +void frag_align (int alignment, int fill_character, int max); +void frag_align_pattern (int alignment, const char *fill_pattern, + int n_fill, int max); +void frag_align_code (int alignment, int max); +void frag_new (int old_frags_var_max_size); +void frag_wane (fragS * fragP); +int frag_room (void); + +char *frag_variant (relax_stateT type, + int max_chars, + int var, + relax_substateT subtype, + symbolS * symbol, + offsetT offset, + char *opcode); + +char *frag_var (relax_stateT type, + int max_chars, + int var, + relax_substateT subtype, + symbolS * symbol, + offsetT offset, + char *opcode); + +bfd_boolean frag_offset_fixed_p (const fragS *, const fragS *, offsetT *); + +#endif /* FRAGS_H */ diff --git a/contrib/toolchain/binutils/gas/hash.c b/contrib/toolchain/binutils/gas/hash.c new file mode 100644 index 0000000000..bae83867d5 --- /dev/null +++ b/contrib/toolchain/binutils/gas/hash.c @@ -0,0 +1,597 @@ +/* hash.c -- gas hash table code + Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 1999, + 2000, 2001, 2002, 2003, 2005, 2007, 2008, 2009, 2011, 2013 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* This version of the hash table code is a wholescale replacement of + the old hash table code, which was fairly bad. This is based on + the hash table code in BFD, but optimized slightly for the + assembler. The assembler does not need to derive structures that + are stored in the hash table. Instead, it always stores a pointer. + The assembler uses the hash table mostly to store symbols, and we + don't need to confuse the symbol structure with a hash table + structure. */ + +#include "as.h" +#include "safe-ctype.h" +#include "obstack.h" + +/* An entry in a hash table. */ + +struct hash_entry { + /* Next entry for this hash code. */ + struct hash_entry *next; + /* String being hashed. */ + const char *string; + /* Hash code. This is the full hash code, not the index into the + table. */ + unsigned long hash; + /* Pointer being stored in the hash table. */ + void *data; +}; + +/* A hash table. */ + +struct hash_control { + /* The hash array. */ + struct hash_entry **table; + /* The number of slots in the hash table. */ + unsigned int size; + /* An obstack for this hash table. */ + struct obstack memory; + +#ifdef HASH_STATISTICS + /* Statistics. */ + unsigned long lookups; + unsigned long hash_compares; + unsigned long string_compares; + unsigned long insertions; + unsigned long replacements; + unsigned long deletions; +#endif /* HASH_STATISTICS */ +}; + +/* The default number of entries to use when creating a hash table. + Note this value can be reduced to 4051 by using the command line + switch --reduce-memory-overheads, or set to other values by using + the --hash-size= switch. */ + +static unsigned long gas_hash_table_size = 65537; + +void +set_gas_hash_table_size (unsigned long size) +{ + gas_hash_table_size = bfd_hash_set_default_size (size); +} + +/* Create a hash table. This return a control block. */ + +struct hash_control * +hash_new_sized (unsigned long size) +{ + unsigned long alloc; + struct hash_control *ret; + + ret = (struct hash_control *) xmalloc (sizeof *ret); + obstack_begin (&ret->memory, chunksize); + alloc = size * sizeof (struct hash_entry *); + ret->table = (struct hash_entry **) obstack_alloc (&ret->memory, alloc); + memset (ret->table, 0, alloc); + ret->size = size; + +#ifdef HASH_STATISTICS + ret->lookups = 0; + ret->hash_compares = 0; + ret->string_compares = 0; + ret->insertions = 0; + ret->replacements = 0; + ret->deletions = 0; +#endif + + return ret; +} + +struct hash_control * +hash_new (void) +{ + return hash_new_sized (gas_hash_table_size); +} + +/* Delete a hash table, freeing all allocated memory. */ + +void +hash_die (struct hash_control *table) +{ + obstack_free (&table->memory, 0); + free (table); +} + +/* Look up a string in a hash table. This returns a pointer to the + hash_entry, or NULL if the string is not in the table. If PLIST is + not NULL, this sets *PLIST to point to the start of the list which + would hold this hash entry. If PHASH is not NULL, this sets *PHASH + to the hash code for KEY. + + Each time we look up a string, we move it to the start of the list + for its hash code, to take advantage of referential locality. */ + +static struct hash_entry * +hash_lookup (struct hash_control *table, const char *key, size_t len, + struct hash_entry ***plist, unsigned long *phash) +{ + unsigned long hash; + size_t n; + unsigned int c; + unsigned int hindex; + struct hash_entry **list; + struct hash_entry *p; + struct hash_entry *prev; + +#ifdef HASH_STATISTICS + ++table->lookups; +#endif + + hash = 0; + for (n = 0; n < len; n++) + { + c = key[n]; + hash += c + (c << 17); + hash ^= hash >> 2; + } + hash += len + (len << 17); + hash ^= hash >> 2; + + if (phash != NULL) + *phash = hash; + + hindex = hash % table->size; + list = table->table + hindex; + + if (plist != NULL) + *plist = list; + + prev = NULL; + for (p = *list; p != NULL; p = p->next) + { +#ifdef HASH_STATISTICS + ++table->hash_compares; +#endif + + if (p->hash == hash) + { +#ifdef HASH_STATISTICS + ++table->string_compares; +#endif + + if (strncmp (p->string, key, len) == 0 && p->string[len] == '\0') + { + if (prev != NULL) + { + prev->next = p->next; + p->next = *list; + *list = p; + } + + return p; + } + } + + prev = p; + } + + return NULL; +} + +/* Insert an entry into a hash table. This returns NULL on success. + On error, it returns a printable string indicating the error. It + is considered to be an error if the entry already exists in the + hash table. */ + +const char * +hash_insert (struct hash_control *table, const char *key, void *val) +{ + struct hash_entry *p; + struct hash_entry **list; + unsigned long hash; + + p = hash_lookup (table, key, strlen (key), &list, &hash); + if (p != NULL) + return "exists"; + +#ifdef HASH_STATISTICS + ++table->insertions; +#endif + + p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p)); + p->string = key; + p->hash = hash; + p->data = val; + + p->next = *list; + *list = p; + + return NULL; +} + +/* Insert or replace an entry in a hash table. This returns NULL on + success. On error, it returns a printable string indicating the + error. If an entry already exists, its value is replaced. */ + +const char * +hash_jam (struct hash_control *table, const char *key, void *val) +{ + struct hash_entry *p; + struct hash_entry **list; + unsigned long hash; + + p = hash_lookup (table, key, strlen (key), &list, &hash); + if (p != NULL) + { +#ifdef HASH_STATISTICS + ++table->replacements; +#endif + + p->data = val; + } + else + { +#ifdef HASH_STATISTICS + ++table->insertions; +#endif + + p = (struct hash_entry *) obstack_alloc (&table->memory, sizeof (*p)); + p->string = key; + p->hash = hash; + p->data = val; + + p->next = *list; + *list = p; + } + + return NULL; +} + +/* Replace an existing entry in a hash table. This returns the old + value stored for the entry. If the entry is not found in the hash + table, this does nothing and returns NULL. */ + +void * +hash_replace (struct hash_control *table, const char *key, void *value) +{ + struct hash_entry *p; + void *ret; + + p = hash_lookup (table, key, strlen (key), NULL, NULL); + if (p == NULL) + return NULL; + +#ifdef HASH_STATISTICS + ++table->replacements; +#endif + + ret = p->data; + + p->data = value; + + return ret; +} + +/* Find an entry in a hash table, returning its value. Returns NULL + if the entry is not found. */ + +void * +hash_find (struct hash_control *table, const char *key) +{ + struct hash_entry *p; + + p = hash_lookup (table, key, strlen (key), NULL, NULL); + if (p == NULL) + return NULL; + + return p->data; +} + +/* As hash_find, but KEY is of length LEN and is not guaranteed to be + NUL-terminated. */ + +void * +hash_find_n (struct hash_control *table, const char *key, size_t len) +{ + struct hash_entry *p; + + p = hash_lookup (table, key, len, NULL, NULL); + if (p == NULL) + return NULL; + + return p->data; +} + +/* Delete an entry from a hash table. This returns the value stored + for that entry, or NULL if there is no such entry. */ + +void * +hash_delete (struct hash_control *table, const char *key, int freeme) +{ + struct hash_entry *p; + struct hash_entry **list; + + p = hash_lookup (table, key, strlen (key), &list, NULL); + if (p == NULL) + return NULL; + + if (p != *list) + abort (); + +#ifdef HASH_STATISTICS + ++table->deletions; +#endif + + *list = p->next; + + if (freeme) + obstack_free (&table->memory, p); + + return p->data; +} + +/* Traverse a hash table. Call the function on every entry in the + hash table. */ + +void +hash_traverse (struct hash_control *table, + void (*pfn) (const char *key, void *value)) +{ + unsigned int i; + + for (i = 0; i < table->size; ++i) + { + struct hash_entry *p; + + for (p = table->table[i]; p != NULL; p = p->next) + (*pfn) (p->string, p->data); + } +} + +/* Print hash table statistics on the specified file. NAME is the + name of the hash table, used for printing a header. */ + +void +hash_print_statistics (FILE *f ATTRIBUTE_UNUSED, + const char *name ATTRIBUTE_UNUSED, + struct hash_control *table ATTRIBUTE_UNUSED) +{ +#ifdef HASH_STATISTICS + unsigned int i; + unsigned long total; + unsigned long empty; + + fprintf (f, "%s hash statistics:\n", name); + fprintf (f, "\t%lu lookups\n", table->lookups); + fprintf (f, "\t%lu hash comparisons\n", table->hash_compares); + fprintf (f, "\t%lu string comparisons\n", table->string_compares); + fprintf (f, "\t%lu insertions\n", table->insertions); + fprintf (f, "\t%lu replacements\n", table->replacements); + fprintf (f, "\t%lu deletions\n", table->deletions); + + total = 0; + empty = 0; + for (i = 0; i < table->size; ++i) + { + struct hash_entry *p; + + if (table->table[i] == NULL) + ++empty; + else + { + for (p = table->table[i]; p != NULL; p = p->next) + ++total; + } + } + + fprintf (f, "\t%g average chain length\n", (double) total / table->size); + fprintf (f, "\t%lu empty slots\n", empty); +#endif +} + +#ifdef TEST + +/* This test program is left over from the old hash table code. */ + +/* Number of hash tables to maintain (at once) in any testing. */ +#define TABLES (6) + +/* We can have 12 statistics. */ +#define STATBUFSIZE (12) + +/* Display statistics here. */ +int statbuf[STATBUFSIZE]; + +/* Human farts here. */ +char answer[100]; + +/* We test many hash tables at once. */ +char *hashtable[TABLES]; + +/* Points to current hash_control. */ +char *h; +char **pp; +char *p; +char *name; +char *value; +int size; +int used; +char command; + +/* Number 0:TABLES-1 of current hashed symbol table. */ +int number; + +int +main () +{ + void applicatee (); + void destroy (); + char *what (); + int *ip; + + number = 0; + h = 0; + printf ("type h for help\n"); + for (;;) + { + printf ("hash_test command: "); + gets (answer); + command = answer[0]; + command = TOLOWER (command); /* Ecch! */ + switch (command) + { + case '#': + printf ("old hash table #=%d.\n", number); + whattable (); + break; + case '?': + for (pp = hashtable; pp < hashtable + TABLES; pp++) + { + printf ("address of hash table #%d control block is %xx\n", + pp - hashtable, *pp); + } + break; + case 'a': + hash_traverse (h, applicatee); + break; + case 'd': + hash_traverse (h, destroy); + hash_die (h); + break; + case 'f': + p = hash_find (h, name = what ("symbol")); + printf ("value of \"%s\" is \"%s\"\n", name, p ? p : "NOT-PRESENT"); + break; + case 'h': + printf ("# show old, select new default hash table number\n"); + printf ("? display all hashtable control block addresses\n"); + printf ("a apply a simple display-er to each symbol in table\n"); + printf ("d die: destroy hashtable\n"); + printf ("f find value of nominated symbol\n"); + printf ("h this help\n"); + printf ("i insert value into symbol\n"); + printf ("j jam value into symbol\n"); + printf ("n new hashtable\n"); + printf ("r replace a value with another\n"); + printf ("s say what %% of table is used\n"); + printf ("q exit this program\n"); + printf ("x delete a symbol from table, report its value\n"); + break; + case 'i': + p = hash_insert (h, name = what ("symbol"), value = what ("value")); + if (p) + { + printf ("symbol=\"%s\" value=\"%s\" error=%s\n", name, value, + p); + } + break; + case 'j': + p = hash_jam (h, name = what ("symbol"), value = what ("value")); + if (p) + { + printf ("symbol=\"%s\" value=\"%s\" error=%s\n", name, value, p); + } + break; + case 'n': + h = hashtable[number] = (char *) hash_new (); + break; + case 'q': + exit (EXIT_SUCCESS); + case 'r': + p = hash_replace (h, name = what ("symbol"), value = what ("value")); + printf ("old value was \"%s\"\n", p ? p : "{}"); + break; + case 's': + hash_say (h, statbuf, STATBUFSIZE); + for (ip = statbuf; ip < statbuf + STATBUFSIZE; ip++) + { + printf ("%d ", *ip); + } + printf ("\n"); + break; + case 'x': + p = hash_delete (h, name = what ("symbol")); + printf ("old value was \"%s\"\n", p ? p : "{}"); + break; + default: + printf ("I can't understand command \"%c\"\n", command); + break; + } + } +} + +char * +what (description) + char *description; +{ + printf (" %s : ", description); + gets (answer); + return xstrdup (answer); +} + +void +destroy (string, value) + char *string; + char *value; +{ + free (string); + free (value); +} + +void +applicatee (string, value) + char *string; + char *value; +{ + printf ("%.20s-%.20s\n", string, value); +} + +/* Determine number: what hash table to use. + Also determine h: points to hash_control. */ + +void +whattable () +{ + for (;;) + { + printf (" what hash table (%d:%d) ? ", 0, TABLES - 1); + gets (answer); + sscanf (answer, "%d", &number); + if (number >= 0 && number < TABLES) + { + h = hashtable[number]; + if (!h) + { + printf ("warning: current hash-table-#%d. has no hash-control\n", number); + } + return; + } + else + { + printf ("invalid hash table number: %d\n", number); + } + } +} + +#endif /* TEST */ diff --git a/contrib/toolchain/binutils/gas/hash.h b/contrib/toolchain/binutils/gas/hash.h new file mode 100644 index 0000000000..c7ea7b57c7 --- /dev/null +++ b/contrib/toolchain/binutils/gas/hash.h @@ -0,0 +1,89 @@ +/* hash.h -- header file for gas hash table routines + Copyright 1987, 1992, 1993, 1995, 1999, 2003, 2005, 2007, 2008, 2013 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef HASH_H +#define HASH_H + +struct hash_control; + +/* Set the size of the hash table used. */ + +void set_gas_hash_table_size (unsigned long); + +/* Create a hash table. This return a control block. */ + +extern struct hash_control *hash_new (void); +extern struct hash_control *hash_new_sized (unsigned long); + +/* Delete a hash table, freeing all allocated memory. */ + +extern void hash_die (struct hash_control *); + +/* Insert an entry into a hash table. This returns NULL on success. + On error, it returns a printable string indicating the error. It + is considered to be an error if the entry already exists in the + hash table. */ + +extern const char *hash_insert (struct hash_control *, + const char *key, void *value); + +/* Insert or replace an entry in a hash table. This returns NULL on + success. On error, it returns a printable string indicating the + error. If an entry already exists, its value is replaced. */ + +extern const char *hash_jam (struct hash_control *, + const char *key, void *value); + +/* Replace an existing entry in a hash table. This returns the old + value stored for the entry. If the entry is not found in the hash + table, this does nothing and returns NULL. */ + +extern void *hash_replace (struct hash_control *, const char *key, + void *value); + +/* Find an entry in a hash table, returning its value. Returns NULL + if the entry is not found. */ + +extern void *hash_find (struct hash_control *, const char *key); + +/* As hash_find, but KEY is of length LEN and is not guaranteed to be + NUL-terminated. */ + +extern void *hash_find_n (struct hash_control *, const char *key, size_t len); + +/* Delete an entry from a hash table. This returns the value stored + for that entry, or NULL if there is no such entry. */ + +extern void *hash_delete (struct hash_control *, const char *key, int); + +/* Traverse a hash table. Call the function on every entry in the + hash table. */ + +extern void hash_traverse (struct hash_control *, + void (*pfn) (const char *key, void *value)); + +/* Print hash table statistics on the specified file. NAME is the + name of the hash table, used for printing a header. */ + +extern void hash_print_statistics (FILE *, const char *name, + struct hash_control *); + +#endif /* HASH_H */ diff --git a/contrib/toolchain/binutils/gas/input-file.c b/contrib/toolchain/binutils/gas/input-file.c new file mode 100644 index 0000000000..ecf1b44be9 --- /dev/null +++ b/contrib/toolchain/binutils/gas/input-file.c @@ -0,0 +1,259 @@ +/* input_file.c - Deal with Input Files - + Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1999, 2000, 2001, + 2002, 2003, 2005, 2006, 2007, 2009, 2012 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* Confines all details of reading source bytes to this module. + All O/S specific crocks should live here. + What we lose in "efficiency" we gain in modularity. + Note we don't need to #include the "as.h" file. No common coupling! */ + +#include "as.h" +#include "input-file.h" +#include "safe-ctype.h" + +/* This variable is non-zero if the file currently being read should be + preprocessed by app. It is zero if the file can be read straight in. */ +int preprocess = 0; + +/* This code opens a file, then delivers BUFFER_SIZE character + chunks of the file on demand. + BUFFER_SIZE is supposed to be a number chosen for speed. + The caller only asks once what BUFFER_SIZE is, and asks before + the nature of the input files (if any) is known. */ + +#define BUFFER_SIZE (32 * 1024) + +/* We use static data: the data area is not sharable. */ + +static FILE *f_in; +static char *file_name; + +/* Struct for saving the state of this module for file includes. */ +struct saved_file + { + FILE * f_in; + char * file_name; + int preprocess; + char * app_save; + }; + +/* These hooks accommodate most operating systems. */ + +void +input_file_begin (void) +{ + f_in = (FILE *) 0; +} + +void +input_file_end (void) +{ +} + +/* Return BUFFER_SIZE. */ +size_t +input_file_buffer_size (void) +{ + return (BUFFER_SIZE); +} + +/* Push the state of our input, returning a pointer to saved info that + can be restored with input_file_pop (). */ + +char * +input_file_push (void) +{ + register struct saved_file *saved; + + saved = (struct saved_file *) xmalloc (sizeof *saved); + + saved->f_in = f_in; + saved->file_name = file_name; + saved->preprocess = preprocess; + if (preprocess) + saved->app_save = app_push (); + + /* Initialize for new file. */ + input_file_begin (); + + return (char *) saved; +} + +void +input_file_pop (char *arg) +{ + register struct saved_file *saved = (struct saved_file *) arg; + + input_file_end (); /* Close out old file. */ + + f_in = saved->f_in; + file_name = saved->file_name; + preprocess = saved->preprocess; + if (preprocess) + app_pop (saved->app_save); + + free (arg); +} + +void +input_file_open (char *filename, /* "" means use stdin. Must not be 0. */ + int pre) +{ + int c; + char buf[80]; + + preprocess = pre; + + gas_assert (filename != 0); /* Filename may not be NULL. */ + if (filename[0]) + { + f_in = fopen (filename, FOPEN_RT); + file_name = filename; + } + else + { + /* Use stdin for the input file. */ + f_in = stdin; + /* For error messages. */ + file_name = _("{standard input}"); + } + + if (f_in == NULL) + { + as_bad (_("can't open %s for reading: %s"), + file_name, xstrerror (errno)); + return; + } + + c = getc (f_in); + + if (ferror (f_in)) + { + as_bad (_("can't read from %s: %s"), + file_name, xstrerror (errno)); + + fclose (f_in); + f_in = NULL; + return; + } + + /* Check for an empty input file. */ + if (feof (f_in)) + { + fclose (f_in); + f_in = NULL; + return; + } + gas_assert (c != EOF); + + if (c == '#') + { + /* Begins with comment, may not want to preprocess. */ + c = getc (f_in); + if (c == 'N') + { + if (fgets (buf, sizeof (buf), f_in) + && !strncmp (buf, "O_APP", 5) && ISSPACE (buf[5])) + preprocess = 0; + if (!strchr (buf, '\n')) + ungetc ('#', f_in); /* It was longer. */ + else + ungetc ('\n', f_in); + } + else if (c == 'A') + { + if (fgets (buf, sizeof (buf), f_in) + && !strncmp (buf, "PP", 2) && ISSPACE (buf[2])) + preprocess = 1; + if (!strchr (buf, '\n')) + ungetc ('#', f_in); + else + ungetc ('\n', f_in); + } + else if (c == '\n') + ungetc ('\n', f_in); + else + ungetc ('#', f_in); + } + else + ungetc (c, f_in); +} + +/* Close input file. */ + +void +input_file_close (void) +{ + /* Don't close a null file pointer. */ + if (f_in != NULL) + fclose (f_in); + + f_in = 0; +} + +/* This function is passed to do_scrub_chars. */ + +static size_t +input_file_get (char *buf, size_t buflen) +{ + size_t size; + + if (feof (f_in)) + return 0; + + size = fread (buf, sizeof (char), buflen, f_in); + if (ferror (f_in)) + as_bad (_("can't read from %s: %s"), file_name, xstrerror (errno)); + return size; +} + +/* Read a buffer from the input file. */ + +char * +input_file_give_next_buffer (char *where /* Where to place 1st character of new buffer. */) +{ + char *return_value; /* -> Last char of what we read, + 1. */ + size_t size; + + if (f_in == (FILE *) 0) + return 0; + /* fflush (stdin); could be done here if you want to synchronise + stdin and stdout, for the case where our input file is stdin. + Since the assembler shouldn't do any output to stdout, we + don't bother to synch output and input. */ + if (preprocess) + size = do_scrub_chars (input_file_get, where, BUFFER_SIZE); + else + size = input_file_get (where, BUFFER_SIZE); + + if (size) + return_value = where + size; + else + { + if (fclose (f_in)) + as_warn (_("can't close %s: %s"), file_name, xstrerror (errno)); + + f_in = (FILE *) 0; + return_value = 0; + } + + return return_value; +} diff --git a/contrib/toolchain/binutils/gas/input-file.h b/contrib/toolchain/binutils/gas/input-file.h new file mode 100644 index 0000000000..7148cc548b --- /dev/null +++ b/contrib/toolchain/binutils/gas/input-file.h @@ -0,0 +1,66 @@ +/* input_file.h header for input-file.c + Copyright 1987, 1992, 1993, 2000, 2003, 2005, 2006, 2007, 2012 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/*"input_file.c":Operating-system dependant functions to read source files.*/ + +/* + * No matter what the operating system, this module must provide the + * following services to its callers. + * + * input_file_begin() Call once before anything else. + * + * input_file_end() Call once after everything else. + * + * input_file_buffer_size() Call anytime. Returns largest possible + * delivery from + * input_file_give_next_buffer(). + * + * input_file_open(name) Call once for each input file. + * + * input_file_give_next_buffer(where) Call once to get each new buffer. + * Return 0: no more chars left in file, + * the file has already been closed. + * Otherwise: return a pointer to just + * after the last character we read + * into the buffer. + * If we can only read 0 characters, then + * end-of-file is faked. + * + * input_file_push() Push state, which can be restored + * later. Does implicit input_file_begin. + * Returns char * to saved state. + * + * input_file_pop (arg) Pops previously saved state. + * + * input_file_close () Closes opened file. + * + * All errors are reported so caller doesn't have to think + * about I/O errors. + */ + +char *input_file_give_next_buffer (char *where); +char *input_file_push (void); +size_t input_file_buffer_size (void); +void input_file_begin (void); +void input_file_close (void); +void input_file_end (void); +void input_file_open (char *filename, int pre); +void input_file_pop (char *arg); diff --git a/contrib/toolchain/binutils/gas/input-scrub.c b/contrib/toolchain/binutils/gas/input-scrub.c new file mode 100644 index 0000000000..adae4d4dcd --- /dev/null +++ b/contrib/toolchain/binutils/gas/input-scrub.c @@ -0,0 +1,523 @@ +/* input_scrub.c - Break up input buffers into whole numbers of lines. + Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 2000, 2001, 2003, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" +#include "filenames.h" +#include "input-file.h" +#include "sb.h" +#include "listing.h" + +/* + * O/S independent module to supply buffers of sanitised source code + * to rest of assembler. We get sanitised input data of arbitrary length. + * We break these buffers on line boundaries, recombine pieces that + * were broken across buffers, and return a buffer of full lines to + * the caller. + * The last partial line begins the next buffer we build and return to caller. + * The buffer returned to caller is preceded by BEFORE_STRING and followed + * by AFTER_STRING, as sentinels. The last character before AFTER_STRING + * is a newline. + * Also looks after line numbers, for e.g. error messages. + */ + +/* + * We don't care how filthy our buffers are, but our callers assume + * that the following sanitation has already been done. + * + * No comments, reduce a comment to a space. + * Reduce a tab to a space unless it is 1st char of line. + * All multiple tabs and spaces collapsed into 1 char. Tab only + * legal if 1st char of line. + * # line file statements converted to .line x;.file y; statements. + * Escaped newlines at end of line: remove them but add as many newlines + * to end of statement as you removed in the middle, to synch line numbers. + */ + +#define BEFORE_STRING ("\n") +#define AFTER_STRING ("\0") /* memcpy of 0 chars might choke. */ +#define BEFORE_SIZE (1) +#define AFTER_SIZE (1) + +#ifndef TC_EOL_IN_INSN +#define TC_EOL_IN_INSN(P) 0 +#endif + +static char *buffer_start; /*->1st char of full buffer area. */ +static char *partial_where; /*->after last full line in buffer. */ +static int partial_size; /* >=0. Number of chars in partial line in buffer. */ + +/* Because we need AFTER_STRING just after last full line, it clobbers + 1st part of partial line. So we preserve 1st part of partial line + here. */ +static char save_source[AFTER_SIZE]; + +/* What is the largest size buffer that input_file_give_next_buffer() + could return to us? */ +static unsigned int buffer_length; + +/* The index into an sb structure we are reading from. -1 if none. */ +static size_t sb_index = -1; + +/* If we are reading from an sb structure, this is it. */ +static sb from_sb; + +/* Should we do a conditional check on from_sb? */ +static int from_sb_is_expansion = 1; + +/* The number of nested sb structures we have included. */ +int macro_nest; + +/* We can have more than one source file open at once, though the info for all + but the latest one are saved off in a struct input_save. These files remain + open, so we are limited by the number of open files allowed by the + underlying OS. We may also sequentially read more than one source file in an + assembly. */ + +/* We must track the physical file and line number for error messages. We also + track a "logical" file and line number corresponding to (C?) compiler + source line numbers. Whenever we open a file we must fill in + physical_input_file. So if it is NULL we have not opened any files yet. */ + +static char *physical_input_file; +static char *logical_input_file; + +/* 1-origin line number in a source file. */ +/* A line ends in '\n' or eof. */ +static unsigned int physical_input_line; +static int logical_input_line; + +/* Struct used to save the state of the input handler during include files */ +struct input_save { + char * buffer_start; + char * partial_where; + int partial_size; + char save_source[AFTER_SIZE]; + size_t buffer_length; + char * physical_input_file; + char * logical_input_file; + unsigned int physical_input_line; + int logical_input_line; + size_t sb_index; + sb from_sb; + int from_sb_is_expansion; /* Should we do a conditional check? */ + struct input_save * next_saved_file; /* Chain of input_saves. */ + char * input_file_save; /* Saved state of input routines. */ + char * saved_position; /* Caller's saved position in buf. */ +}; + +static struct input_save *input_scrub_push (char *saved_position); +static char *input_scrub_pop (struct input_save *arg); + +/* Saved information about the file that .include'd this one. When we hit EOF, + we automatically pop to that file. */ + +static struct input_save *next_saved_file; + +/* Push the state of input reading and scrubbing so that we can #include. + The return value is a 'void *' (fudged for old compilers) to a save + area, which can be restored by passing it to input_scrub_pop(). */ + +static struct input_save * +input_scrub_push (char *saved_position) +{ + register struct input_save *saved; + + saved = (struct input_save *) xmalloc (sizeof *saved); + + saved->saved_position = saved_position; + saved->buffer_start = buffer_start; + saved->partial_where = partial_where; + saved->partial_size = partial_size; + saved->buffer_length = buffer_length; + saved->physical_input_file = physical_input_file; + saved->logical_input_file = logical_input_file; + saved->physical_input_line = physical_input_line; + saved->logical_input_line = logical_input_line; + saved->sb_index = sb_index; + saved->from_sb = from_sb; + saved->from_sb_is_expansion = from_sb_is_expansion; + memcpy (saved->save_source, save_source, sizeof (save_source)); + saved->next_saved_file = next_saved_file; + saved->input_file_save = input_file_push (); + + input_file_begin (); /* Reinitialize! */ + logical_input_line = -1; + logical_input_file = (char *) NULL; + buffer_length = input_file_buffer_size (); + sb_index = -1; + + buffer_start = (char *) xmalloc ((BEFORE_SIZE + buffer_length + + buffer_length + AFTER_SIZE)); + memcpy (buffer_start, BEFORE_STRING, (int) BEFORE_SIZE); + + return saved; +} + +static char * +input_scrub_pop (struct input_save *saved) +{ + char *saved_position; + + input_scrub_end (); /* Finish off old buffer */ + + input_file_pop (saved->input_file_save); + saved_position = saved->saved_position; + buffer_start = saved->buffer_start; + buffer_length = saved->buffer_length; + physical_input_file = saved->physical_input_file; + logical_input_file = saved->logical_input_file; + physical_input_line = saved->physical_input_line; + logical_input_line = saved->logical_input_line; + sb_index = saved->sb_index; + from_sb = saved->from_sb; + from_sb_is_expansion = saved->from_sb_is_expansion; + partial_where = saved->partial_where; + partial_size = saved->partial_size; + next_saved_file = saved->next_saved_file; + memcpy (save_source, saved->save_source, sizeof (save_source)); + + free (saved); + return saved_position; +} + +void +input_scrub_begin (void) +{ + know (strlen (BEFORE_STRING) == BEFORE_SIZE); + know (strlen (AFTER_STRING) == AFTER_SIZE + || (AFTER_STRING[0] == '\0' && AFTER_SIZE == 1)); + + input_file_begin (); + + buffer_length = input_file_buffer_size (); + + buffer_start = (char *) xmalloc ((BEFORE_SIZE + buffer_length + + buffer_length + AFTER_SIZE)); + memcpy (buffer_start, BEFORE_STRING, (int) BEFORE_SIZE); + + /* Line number things. */ + logical_input_line = -1; + logical_input_file = (char *) NULL; + physical_input_file = NULL; /* No file read yet. */ + next_saved_file = NULL; /* At EOF, don't pop to any other file */ + do_scrub_begin (flag_m68k_mri); +} + +void +input_scrub_end (void) +{ + if (buffer_start) + { + free (buffer_start); + buffer_start = 0; + input_file_end (); + } +} + +/* Start reading input from a new file. + Return start of caller's part of buffer. */ + +char * +input_scrub_new_file (char *filename) +{ + input_file_open (filename, !flag_no_comments); + physical_input_file = filename[0] ? filename : _("{standard input}"); + physical_input_line = 0; + + partial_size = 0; + return (buffer_start + BEFORE_SIZE); +} + +/* Include a file from the current file. Save our state, cause it to + be restored on EOF, and begin handling a new file. Same result as + input_scrub_new_file. */ + +char * +input_scrub_include_file (char *filename, char *position) +{ + next_saved_file = input_scrub_push (position); + return input_scrub_new_file (filename); +} + +/* Start getting input from an sb structure. This is used when + expanding a macro. */ + +void +input_scrub_include_sb (sb *from, char *position, int is_expansion) +{ + int newline; + + if (macro_nest > max_macro_nest) + as_fatal (_("macros nested too deeply")); + ++macro_nest; + +#ifdef md_macro_start + if (is_expansion) + { + md_macro_start (); + } +#endif + + next_saved_file = input_scrub_push (position); + + /* Allocate sufficient space: from->len + optional newline. */ + newline = from->len >= 1 && from->ptr[0] != '\n'; + sb_build (&from_sb, from->len + newline); + from_sb_is_expansion = is_expansion; + if (newline) + { + /* Add the sentinel required by read.c. */ + sb_add_char (&from_sb, '\n'); + } + sb_scrub_and_add_sb (&from_sb, from); + + /* Make sure the parser looks at defined contents when it scans for + e.g. end-of-line at the end of a macro. */ + sb_terminate (&from_sb); + + sb_index = 1; + + /* These variables are reset by input_scrub_push. Restore them + since we are, after all, still at the same point in the file. */ + logical_input_line = next_saved_file->logical_input_line; + logical_input_file = next_saved_file->logical_input_file; +} + +void +input_scrub_close (void) +{ + input_file_close (); + physical_input_line = 0; + logical_input_line = -1; +} + +char * +input_scrub_next_buffer (char **bufp) +{ + register char *limit; /*->just after last char of buffer. */ + + if (sb_index != (size_t) -1) + { + if (sb_index >= from_sb.len) + { + sb_kill (&from_sb); + if (from_sb_is_expansion) + { + cond_finish_check (macro_nest); +#ifdef md_macro_end + /* Allow the target to clean up per-macro expansion + data. */ + md_macro_end (); +#endif + } + --macro_nest; + partial_where = NULL; + if (next_saved_file != NULL) + *bufp = input_scrub_pop (next_saved_file); + return partial_where; + } + + partial_where = from_sb.ptr + from_sb.len; + partial_size = 0; + *bufp = from_sb.ptr + sb_index; + sb_index = from_sb.len; + return partial_where; + } + + *bufp = buffer_start + BEFORE_SIZE; + + if (partial_size) + { + memmove (buffer_start + BEFORE_SIZE, partial_where, + (unsigned int) partial_size); + memcpy (buffer_start + BEFORE_SIZE, save_source, AFTER_SIZE); + } + limit = input_file_give_next_buffer (buffer_start + + BEFORE_SIZE + + partial_size); + if (limit) + { + register char *p; /* Find last newline. */ + /* Terminate the buffer to avoid confusing TC_EOL_IN_INSN. */ + *limit = '\0'; + for (p = limit - 1; *p != '\n' || TC_EOL_IN_INSN (p); --p) + ; + ++p; + + while (p <= buffer_start + BEFORE_SIZE) + { + int limoff; + + limoff = limit - buffer_start; + buffer_length += input_file_buffer_size (); + buffer_start = (char *) xrealloc (buffer_start, + (BEFORE_SIZE + + 2 * buffer_length + + AFTER_SIZE)); + *bufp = buffer_start + BEFORE_SIZE; + limit = input_file_give_next_buffer (buffer_start + limoff); + + if (limit == NULL) + { + as_warn (_("partial line at end of file ignored")); + partial_where = NULL; + if (next_saved_file) + *bufp = input_scrub_pop (next_saved_file); + return NULL; + } + + /* Terminate the buffer to avoid confusing TC_EOL_IN_INSN. */ + *limit = '\0'; + for (p = limit - 1; *p != '\n' || TC_EOL_IN_INSN (p); --p) + ; + ++p; + } + + partial_where = p; + partial_size = limit - p; + memcpy (save_source, partial_where, (int) AFTER_SIZE); + memcpy (partial_where, AFTER_STRING, (int) AFTER_SIZE); + } + else + { + partial_where = 0; + if (partial_size > 0) + { + as_warn (_("partial line at end of file ignored")); + } + + /* Tell the listing we've finished the file. */ + LISTING_EOF (); + + /* If we should pop to another file at EOF, do it. */ + if (next_saved_file) + { + *bufp = input_scrub_pop (next_saved_file); /* Pop state */ + /* partial_where is now correct to return, since we popped it. */ + } + } + return (partial_where); +} + +/* The remaining part of this file deals with line numbers, error + messages and so on. Return TRUE if we opened any file. */ + +int +seen_at_least_1_file (void) +{ + return (physical_input_file != NULL); +} + +void +bump_line_counters (void) +{ + if (sb_index == (size_t) -1) + { + ++physical_input_line; + if (logical_input_line >= 0) + ++logical_input_line; + } +} + +/* Tells us what the new logical line number and file are. + If the line_number is -1, we don't change the current logical line + number. If it is -2, we decrement the logical line number (this is + to support the .appfile pseudo-op inserted into the stream by + do_scrub_chars). + If the fname is NULL, we don't change the current logical file name. + Returns nonzero if the filename actually changes. */ + +int +new_logical_line_flags (char *fname, /* DON'T destroy it! We point to it! */ + int line_number, + int flags) +{ + switch (flags) + { + case 0: + break; + case 1: + if (line_number != -1) + abort (); + break; + case 1 << 1: + case 1 << 2: + /* FIXME: we could check that include nesting is correct. */ + break; + default: + abort (); + } + + if (line_number >= 0) + logical_input_line = line_number; + else if (line_number == -1 && fname && !*fname && (flags & (1 << 2))) + { + logical_input_file = physical_input_file; + logical_input_line = physical_input_line; + fname = NULL; + } + + if (fname + && (logical_input_file == NULL + || filename_cmp (logical_input_file, fname))) + { + logical_input_file = fname; + return 1; + } + else + return 0; +} + +int +new_logical_line (char *fname, int line_number) +{ + return new_logical_line_flags (fname, line_number, 0); +} + + +/* Return the current file name and line number. + namep should be char * const *, but there are compilers which screw + up declarations like that, and it's easier to avoid it. */ + +void +as_where (char **namep, unsigned int *linep) +{ + if (logical_input_file != NULL + && (linep == NULL || logical_input_line >= 0)) + { + *namep = logical_input_file; + if (linep != NULL) + *linep = logical_input_line; + } + else if (physical_input_file != NULL) + { + *namep = physical_input_file; + if (linep != NULL) + *linep = physical_input_line; + } + else + { + *namep = 0; + if (linep != NULL) + *linep = 0; + } +} diff --git a/contrib/toolchain/binutils/gas/itbl-cpu.h b/contrib/toolchain/binutils/gas/itbl-cpu.h new file mode 100644 index 0000000000..54880214c7 --- /dev/null +++ b/contrib/toolchain/binutils/gas/itbl-cpu.h @@ -0,0 +1 @@ +#include "itbl-i386.h" diff --git a/contrib/toolchain/binutils/gas/listing.c b/contrib/toolchain/binutils/gas/listing.c new file mode 100644 index 0000000000..182d504691 --- /dev/null +++ b/contrib/toolchain/binutils/gas/listing.c @@ -0,0 +1,1660 @@ +/* listing.c - maintain assembly listings + Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, + 2001, 2002, 2003, 2005, 2006, 2007, 2008, 2009, 2010 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* Contributed by Steve Chamberlain + + A listing page looks like: + + LISTING_HEADER sourcefilename pagenumber + TITLE LINE + SUBTITLE LINE + linenumber address data source + linenumber address data source + linenumber address data source + linenumber address data source + + If not overridden, the listing commands are: + + .title "stuff" + Put "stuff" onto the title line + .sbttl "stuff" + Put stuff onto the subtitle line + + If these commands come within 10 lines of the top of the page, they + will affect the page they are on, as well as any subsequent page + + .eject + Thow a page + .list + Increment the enable listing counter + .nolist + Decrement the enable listing counter + + .psize Y[,X] + Set the paper size to X wide and Y high. Setting a psize Y of + zero will suppress form feeds except where demanded by .eject + + If the counter goes below zero, listing is suppressed. + + Listings are a maintained by read calling various listing_ + functions. What happens most is that the macro NO_LISTING is not + defined (from the Makefile), then the macro LISTING_NEWLINE expands + into a call to listing_newline. The call is done from read.c, every + time it sees a newline, and -l is on the command line. + + The function listing_newline remembers the frag associated with the + newline, and creates a new frag - note that this is wasteful, but not + a big deal, since listing slows things down a lot anyway. The + function also remembers when the filename changes. + + When all the input has finished, and gas has had a chance to settle + down, the listing is output. This is done by running down the list of + frag/source file records, and opening the files as needed and printing + out the bytes and chars associated with them. + + The only things which the architecture can change about the listing + are defined in these macros: + + LISTING_HEADER The name of the architecture + LISTING_WORD_SIZE The make of the number of bytes in a word, this determines + the clumping of the output data. eg a value of + 2 makes words look like 1234 5678, whilst 1 + would make the same value look like 12 34 56 + 78 + LISTING_LHS_WIDTH Number of words of above size for the lhs + + LISTING_LHS_WIDTH_SECOND Number of words for the data on the lhs + for the second line + + LISTING_LHS_CONT_LINES Max number of lines to use up for a continuation + LISTING_RHS_WIDTH Number of chars from the input file to print + on a line. */ + +#include "as.h" +#include "filenames.h" +#include "obstack.h" +#include "safe-ctype.h" +#include "input-file.h" +#include "subsegs.h" +#include "bfdver.h" +#include +#include + +#ifndef NO_LISTING + +#ifndef LISTING_HEADER +#define LISTING_HEADER "GAS LISTING" +#endif +#ifndef LISTING_WORD_SIZE +#define LISTING_WORD_SIZE 4 +#endif +#ifndef LISTING_LHS_WIDTH +#define LISTING_LHS_WIDTH ((LISTING_WORD_SIZE) > 4 ? 1 : 4 / (LISTING_WORD_SIZE)) +#endif +#ifndef LISTING_LHS_WIDTH_SECOND +#define LISTING_LHS_WIDTH_SECOND LISTING_LHS_WIDTH +#endif +#ifndef LISTING_RHS_WIDTH +#define LISTING_RHS_WIDTH 100 +#endif +#ifndef LISTING_LHS_CONT_LINES +#define LISTING_LHS_CONT_LINES 4 +#endif +#define MAX_DATELEN 30 + +/* This structure remembers which .s were used. */ +typedef struct file_info_struct +{ + struct file_info_struct * next; + char * filename; + long pos; + unsigned int linenum; + int at_end; +} file_info_type; + +enum edict_enum +{ + EDICT_NONE, + EDICT_SBTTL, + EDICT_TITLE, + EDICT_NOLIST, + EDICT_LIST, + EDICT_NOLIST_NEXT, + EDICT_EJECT +}; + + +struct list_message +{ + char *message; + struct list_message *next; +}; + +/* This structure remembers which line from which file goes into which + frag. */ +struct list_info_struct +{ + /* Frag which this line of source is nearest to. */ + fragS *frag; + + /* The actual line in the source file. */ + unsigned int line; + + /* Pointer to the file info struct for the file which this line + belongs to. */ + file_info_type *file; + + /* The expanded text of any macro that may have been executing. */ + char *line_contents; + + /* Next in list. */ + struct list_info_struct *next; + + /* Pointer to the file info struct for the high level language + source line that belongs here. */ + file_info_type *hll_file; + + /* High level language source line. */ + unsigned int hll_line; + + /* Pointers to linked list of messages associated with this line. */ + struct list_message *messages, *last_message; + + enum edict_enum edict; + char *edict_arg; + + /* Nonzero if this line is to be omitted because it contains + debugging information. This can become a flags field if we come + up with more information to store here. */ + int debugging; +}; + +typedef struct list_info_struct list_info_type; + +int listing_lhs_width = LISTING_LHS_WIDTH; +int listing_lhs_width_second = LISTING_LHS_WIDTH_SECOND; +int listing_lhs_cont_lines = LISTING_LHS_CONT_LINES; +int listing_rhs_width = LISTING_RHS_WIDTH; + +struct list_info_struct * listing_tail; + +static file_info_type * file_info_head; +static file_info_type * last_open_file_info; +static FILE * last_open_file; +static struct list_info_struct * head; +static int paper_width = 200; +static int paper_height = 60; + +extern int listing; + +/* File to output listings to. */ +static FILE *list_file; + +/* This static array is used to keep the text of data to be printed + before the start of the line. */ + +#define MAX_BYTES \ + (((LISTING_WORD_SIZE * 2) + 1) * listing_lhs_width \ + + ((((LISTING_WORD_SIZE * 2) + 1) * listing_lhs_width_second) \ + * listing_lhs_cont_lines) \ + + 20) + +static char *data_buffer; + +/* Prototypes. */ +static void listing_message (const char *, const char *); +static file_info_type *file_info (const char *); +static void new_frag (void); +static void listing_page (list_info_type *); +static unsigned int calc_hex (list_info_type *); +static void print_lines (list_info_type *, unsigned int, char *, unsigned int); +static void list_symbol_table (void); +static int debugging_pseudo (list_info_type *, const char *); +static void listing_listing (char *); + +static void +listing_message (const char *name, const char *message) +{ + if (listing_tail != (list_info_type *) NULL) + { + unsigned int l = strlen (name) + strlen (message) + 1; + char *n = (char *) xmalloc (l); + struct list_message *lm = xmalloc (sizeof *lm); + strcpy (n, name); + strcat (n, message); + lm->message = n; + lm->next = NULL; + + if (listing_tail->last_message) + listing_tail->last_message->next = lm; + else + listing_tail->messages = lm; + listing_tail->last_message = lm; + } +} + +void +listing_warning (const char *message) +{ + listing_message (_("Warning:"), message); +} + +void +listing_error (const char *message) +{ + listing_message (_("Error:"), message); +} + +static file_info_type * +file_info (const char *file_name) +{ + /* Find an entry with this file name. */ + file_info_type *p = file_info_head; + + while (p != (file_info_type *) NULL) + { + if (filename_cmp (p->filename, file_name) == 0) + return p; + p = p->next; + } + + /* Make new entry. */ + p = (file_info_type *) xmalloc (sizeof (file_info_type)); + p->next = file_info_head; + file_info_head = p; + p->filename = xstrdup (file_name); + p->pos = 0; + p->linenum = 0; + p->at_end = 0; + + return p; +} + +static void +new_frag (void) +{ + frag_wane (frag_now); + frag_new (0); +} + +void +listing_newline (char *ps) +{ + char *file; + unsigned int line; + static unsigned int last_line = 0xffff; + static char *last_file = NULL; + list_info_type *new_i = NULL; + + if (listing == 0) + return; + + if (now_seg == absolute_section) + return; + +#ifdef OBJ_ELF + /* In ELF, anything in a section beginning with .debug or .line is + considered to be debugging information. This includes the + statement which switches us into the debugging section, which we + can only set after we are already in the debugging section. */ + if ((listing & LISTING_NODEBUG) != 0 + && listing_tail != NULL + && ! listing_tail->debugging) + { + const char *segname; + + segname = segment_name (now_seg); + if (strncmp (segname, ".debug", sizeof ".debug" - 1) == 0 + || strncmp (segname, ".line", sizeof ".line" - 1) == 0) + listing_tail->debugging = 1; + } +#endif + + as_where (&file, &line); + if (ps == NULL) + { + if (line == last_line + && !(last_file && file && filename_cmp (file, last_file))) + return; + + new_i = (list_info_type *) xmalloc (sizeof (list_info_type)); + + /* Detect if we are reading from stdin by examining the file + name returned by as_where(). + + [FIXME: We rely upon the name in the strcmp below being the + same as the one used by input_scrub_new_file(), if that is + not true, then this code will fail]. + + If we are reading from stdin, then we need to save each input + line here (assuming of course that we actually have a line of + input to read), so that it can be displayed in the listing + that is produced at the end of the assembly. */ + if (strcmp (file, _("{standard input}")) == 0 + && input_line_pointer != NULL) + { + char *copy; + int len; + int seen_quote = 0; + int seen_slash = 0; + + for (copy = input_line_pointer; + *copy && (seen_quote + || is_end_of_line [(unsigned char) *copy] != 1); + copy++) + { + if (seen_slash) + seen_slash = 0; + else if (*copy == '\\') + seen_slash = 1; + else if (*copy == '"') + seen_quote = !seen_quote; + } + + len = copy - input_line_pointer + 1; + + copy = (char *) xmalloc (len); + + if (copy != NULL) + { + char *src = input_line_pointer; + char *dest = copy; + + while (--len) + { + unsigned char c = *src++; + + /* Omit control characters in the listing. */ + if (!ISCNTRL (c)) + *dest++ = c; + } + + *dest = 0; + } + + new_i->line_contents = copy; + } + else + new_i->line_contents = NULL; + } + else + { + new_i = (list_info_type *) xmalloc (sizeof (list_info_type)); + new_i->line_contents = ps; + } + + last_line = line; + last_file = file; + + new_frag (); + + if (listing_tail) + listing_tail->next = new_i; + else + head = new_i; + + listing_tail = new_i; + + new_i->frag = frag_now; + new_i->line = line; + new_i->file = file_info (file); + new_i->next = (list_info_type *) NULL; + new_i->messages = NULL; + new_i->last_message = NULL; + new_i->edict = EDICT_NONE; + new_i->hll_file = (file_info_type *) NULL; + new_i->hll_line = 0; + new_i->debugging = 0; + + new_frag (); + +#ifdef OBJ_ELF + /* In ELF, anything in a section beginning with .debug or .line is + considered to be debugging information. */ + if ((listing & LISTING_NODEBUG) != 0) + { + const char *segname; + + segname = segment_name (now_seg); + if (strncmp (segname, ".debug", sizeof ".debug" - 1) == 0 + || strncmp (segname, ".line", sizeof ".line" - 1) == 0) + new_i->debugging = 1; + } +#endif +} + +/* Attach all current frags to the previous line instead of the + current line. This is called by the MIPS backend when it discovers + that it needs to add some NOP instructions; the added NOP + instructions should go with the instruction that has the delay, not + with the new instruction. */ + +void +listing_prev_line (void) +{ + list_info_type *l; + fragS *f; + + if (head == (list_info_type *) NULL + || head == listing_tail) + return; + + new_frag (); + + for (l = head; l->next != listing_tail; l = l->next) + ; + + for (f = frchain_now->frch_root; f != (fragS *) NULL; f = f->fr_next) + if (f->line == listing_tail) + f->line = l; + + listing_tail->frag = frag_now; + new_frag (); +} + +/* This function returns the next source line from the file supplied, + truncated to size. It appends a fake line to the end of each input + file to make using the returned buffer simpler. */ + +static char * +buffer_line (file_info_type *file, char *line, unsigned int size) +{ + unsigned int count = 0; + int c; + char *p = line; + + /* If we couldn't open the file, return an empty line. */ + if (file->at_end) + return ""; + + /* Check the cache and see if we last used this file. */ + if (!last_open_file_info || file != last_open_file_info) + { + if (last_open_file) + { + last_open_file_info->pos = ftell (last_open_file); + fclose (last_open_file); + } + + /* Open the file in the binary mode so that ftell above can + return a reliable value that we can feed to fseek below. */ + last_open_file_info = file; + last_open_file = fopen (file->filename, FOPEN_RB); + if (last_open_file == NULL) + { + file->at_end = 1; + return ""; + } + + /* Seek to where we were last time this file was open. */ + if (file->pos) + fseek (last_open_file, file->pos, SEEK_SET); + } + + /* Leave room for null. */ + size -= 1; + + c = fgetc (last_open_file); + + while (c != EOF && c != '\n' && c != '\r') + { + if (count < size) + *p++ = c; + count++; + + c = fgetc (last_open_file); + } + + /* If '\r' is followed by '\n', swallow that. Likewise, if '\n' + is followed by '\r', swallow that as well. */ + if (c == '\r' || c == '\n') + { + int next = fgetc (last_open_file); + + if ((c == '\r' && next != '\n') + || (c == '\n' && next != '\r')) + ungetc (next, last_open_file); + } + + if (c == EOF) + { + file->at_end = 1; + if (count + 2 < size) + { + *p++ = '.'; + *p++ = '.'; + *p++ = '.'; + } + } + file->linenum++; + *p++ = 0; + return line; +} + + +/* This function rewinds the requested file back to the line requested, + reads it in again into the buffer provided and then restores the file + back to its original location. Returns the buffer pointer upon success + or an empty string if an error occurs. */ + +static char * +rebuffer_line (file_info_type * file, + unsigned int linenum, + char * buffer, + unsigned int size) +{ + unsigned int count = 0; + unsigned int current_line; + char * p = buffer; + long pos; + long pos2; + int c; + bfd_boolean found = FALSE; + + /* Sanity checks. */ + if (file == NULL || buffer == NULL || size <= 1 || file->linenum <= linenum) + return ""; + + /* Check the cache and see if we last used this file. */ + if (last_open_file_info == NULL || file != last_open_file_info) + { + if (last_open_file) + { + last_open_file_info->pos = ftell (last_open_file); + fclose (last_open_file); + } + + /* Open the file in the binary mode so that ftell above can + return a reliable value that we can feed to fseek below. */ + last_open_file_info = file; + last_open_file = fopen (file->filename, FOPEN_RB); + if (last_open_file == NULL) + { + file->at_end = 1; + return ""; + } + + /* Seek to where we were last time this file was open. */ + if (file->pos) + fseek (last_open_file, file->pos, SEEK_SET); + } + + /* Remember where we are in the current file. */ + pos2 = pos = ftell (last_open_file); + if (pos < 3) + return ""; + current_line = file->linenum; + + /* Leave room for the nul at the end of the buffer. */ + size -= 1; + buffer[size] = 0; + + /* Increment the current line count by one. + This is to allow for the fact that we are searching for the + start of a previous line, but we do this by detecting end-of-line + character(s) not start-of-line characters. */ + ++ current_line; + + while (pos2 > 0 && ! found) + { + char * ptr; + + /* Move backwards through the file, looking for earlier lines. */ + pos2 = (long) size > pos2 ? 0 : pos2 - size; + fseek (last_open_file, pos2, SEEK_SET); + + /* Our caller has kindly provided us with a buffer, so we use it. */ + if (fread (buffer, 1, size, last_open_file) != size) + { + as_warn (_("unable to rebuffer file: %s\n"), file->filename); + return ""; + } + + for (ptr = buffer + size; ptr >= buffer; -- ptr) + { + if (*ptr == '\n') + { + -- current_line; + + if (current_line == linenum) + { + /* We have found the start of the line we seek. */ + found = TRUE; + + /* FIXME: We could skip the read-in-the-line code + below if we know that we already have the whole + line in the buffer. */ + + /* Advance pos2 to the newline character we have just located. */ + pos2 += (ptr - buffer); + + /* Skip the newline and, if present, the carriage return. */ + if (ptr + 1 == buffer + size) + { + ++pos2; + if (fgetc (last_open_file) == '\r') + ++ pos2; + } + else + pos2 += (ptr[1] == '\r' ? 2 : 1); + + /* Move the file pointer to this location. */ + fseek (last_open_file, pos2, SEEK_SET); + break; + } + } + } + } + + /* Read in the line. */ + c = fgetc (last_open_file); + + while (c != EOF && c != '\n' && c != '\r') + { + if (count < size) + *p++ = c; + count++; + + c = fgetc (last_open_file); + } + + /* If '\r' is followed by '\n', swallow that. Likewise, if '\n' + is followed by '\r', swallow that as well. */ + if (c == '\r' || c == '\n') + { + int next = fgetc (last_open_file); + + if ((c == '\r' && next != '\n') + || (c == '\n' && next != '\r')) + ungetc (next, last_open_file); + } + + /* Terminate the line. */ + *p++ = 0; + + /* Reset the file position. */ + fseek (last_open_file, pos, SEEK_SET); + + return buffer; +} + +static const char *fn; + +static unsigned int eject; /* Eject pending */ +static unsigned int page; /* Current page number */ +static char *title; /* Current title */ +static char *subtitle; /* Current subtitle */ +static unsigned int on_page; /* Number of lines printed on current page */ + +static void +listing_page (list_info_type *list) +{ + /* Grope around, see if we can see a title or subtitle edict coming up + soon. (we look down 10 lines of the page and see if it's there) */ + if ((eject || (on_page >= (unsigned int) paper_height)) + && paper_height != 0) + { + unsigned int c = 10; + int had_title = 0; + int had_subtitle = 0; + + page++; + + while (c != 0 && list) + { + if (list->edict == EDICT_SBTTL && !had_subtitle) + { + had_subtitle = 1; + subtitle = list->edict_arg; + } + if (list->edict == EDICT_TITLE && !had_title) + { + had_title = 1; + title = list->edict_arg; + } + list = list->next; + c--; + } + + if (page > 1) + { + fprintf (list_file, "\f"); + } + + fprintf (list_file, "%s %s \t\t\tpage %d\n", LISTING_HEADER, fn, page); + fprintf (list_file, "%s\n", title); + fprintf (list_file, "%s\n", subtitle); + on_page = 3; + eject = 0; + } +} + +/* Print a line into the list_file. Update the line count + and if necessary start a new page. */ + +static void +emit_line (list_info_type * list, const char * format, ...) +{ + va_list args; + + va_start (args, format); + + vfprintf (list_file, format, args); + on_page++; + listing_page (list); + + va_end (args); +} + +static unsigned int +calc_hex (list_info_type *list) +{ + int data_buffer_size; + list_info_type *first = list; + unsigned int address = ~(unsigned int) 0; + fragS *frag; + fragS *frag_ptr; + unsigned int octet_in_frag; + + /* Find first frag which says it belongs to this line. */ + frag = list->frag; + while (frag && frag->line != list) + frag = frag->fr_next; + + frag_ptr = frag; + + data_buffer_size = 0; + + /* Dump all the frags which belong to this line. */ + while (frag_ptr != (fragS *) NULL && frag_ptr->line == first) + { + /* Print as many bytes from the fixed part as is sensible. */ + octet_in_frag = 0; + while ((offsetT) octet_in_frag < frag_ptr->fr_fix + && data_buffer_size < MAX_BYTES - 3) + { + if (address == ~(unsigned int) 0) + address = frag_ptr->fr_address / OCTETS_PER_BYTE; + + sprintf (data_buffer + data_buffer_size, + "%02X", + (frag_ptr->fr_literal[octet_in_frag]) & 0xff); + data_buffer_size += 2; + octet_in_frag++; + } + if (frag_ptr->fr_type == rs_fill) + { + unsigned int var_rep_max = octet_in_frag; + unsigned int var_rep_idx = octet_in_frag; + + /* Print as many bytes from the variable part as is sensible. */ + while (((offsetT) octet_in_frag + < (frag_ptr->fr_fix + frag_ptr->fr_var * frag_ptr->fr_offset)) + && data_buffer_size < MAX_BYTES - 3) + { + if (address == ~(unsigned int) 0) + address = frag_ptr->fr_address / OCTETS_PER_BYTE; + + sprintf (data_buffer + data_buffer_size, + "%02X", + (frag_ptr->fr_literal[var_rep_idx]) & 0xff); + data_buffer_size += 2; + + var_rep_idx++; + octet_in_frag++; + + if ((offsetT) var_rep_idx >= frag_ptr->fr_fix + frag_ptr->fr_var) + var_rep_idx = var_rep_max; + } + } + + frag_ptr = frag_ptr->fr_next; + } + data_buffer[data_buffer_size] = '\0'; + return address; +} + +static void +print_lines (list_info_type *list, unsigned int lineno, + char *string, unsigned int address) +{ + unsigned int idx; + unsigned int nchars; + unsigned int lines; + unsigned int octet_in_word = 0; + char *src = data_buffer; + int cur; + struct list_message *msg; + + /* Print the stuff on the first line. */ + listing_page (list); + nchars = (LISTING_WORD_SIZE * 2 + 1) * listing_lhs_width; + + /* Print the hex for the first line. */ + if (address == ~(unsigned int) 0) + { + fprintf (list_file, "% 4d ", lineno); + for (idx = 0; idx < nchars; idx++) + fprintf (list_file, " "); + + emit_line (NULL, "\t%s\n", string ? string : ""); + return; + } + + if (had_errors ()) + fprintf (list_file, "% 4d ???? ", lineno); + else + fprintf (list_file, "% 4d %04x ", lineno, address); + + /* And the data to go along with it. */ + idx = 0; + cur = 0; + while (src[cur] && idx < nchars) + { + int offset; + offset = cur; + fprintf (list_file, "%c%c", src[offset], src[offset + 1]); + cur += 2; + octet_in_word++; + + if (octet_in_word == LISTING_WORD_SIZE) + { + fprintf (list_file, " "); + idx++; + octet_in_word = 0; + } + + idx += 2; + } + + for (; idx < nchars; idx++) + fprintf (list_file, " "); + + emit_line (list, "\t%s\n", string ? string : ""); + + for (msg = list->messages; msg; msg = msg->next) + emit_line (list, "**** %s\n", msg->message); + + for (lines = 0; + lines < (unsigned int) listing_lhs_cont_lines + && src[cur]; + lines++) + { + nchars = ((LISTING_WORD_SIZE * 2) + 1) * listing_lhs_width_second - 1; + idx = 0; + + /* Print any more lines of data, but more compactly. */ + fprintf (list_file, "% 4d ", lineno); + + while (src[cur] && idx < nchars) + { + int offset; + offset = cur; + fprintf (list_file, "%c%c", src[offset], src[offset + 1]); + cur += 2; + idx += 2; + octet_in_word++; + + if (octet_in_word == LISTING_WORD_SIZE) + { + fprintf (list_file, " "); + idx++; + octet_in_word = 0; + } + } + + emit_line (list, "\n"); + } +} + +static void +list_symbol_table (void) +{ + extern symbolS *symbol_rootP; + int got_some = 0; + + symbolS *ptr; + eject = 1; + listing_page (NULL); + + for (ptr = symbol_rootP; ptr != (symbolS *) NULL; ptr = symbol_next (ptr)) + { + if (SEG_NORMAL (S_GET_SEGMENT (ptr)) + || S_GET_SEGMENT (ptr) == absolute_section) + { + /* Don't report section symbols. They are not interesting. */ + if (symbol_section_p (ptr)) + continue; + + if (S_GET_NAME (ptr)) + { + char buf[30], fmt[8]; + valueT val = S_GET_VALUE (ptr); + + /* @@ Note that this is dependent on the compilation options, + not solely on the target characteristics. */ + if (sizeof (val) == 4 && sizeof (int) == 4) + sprintf (buf, "%08lx", (unsigned long) val); + else if (sizeof (val) <= sizeof (unsigned long)) + { + sprintf (fmt, "%%0%lulx", + (unsigned long) (sizeof (val) * 2)); + sprintf (buf, fmt, (unsigned long) val); + } +#if defined (BFD64) + else if (sizeof (val) > 4) + sprintf_vma (buf, val); +#endif + else + abort (); + + if (!got_some) + { + fprintf (list_file, "DEFINED SYMBOLS\n"); + on_page++; + got_some = 1; + } + + if (symbol_get_frag (ptr) && symbol_get_frag (ptr)->line) + { + fprintf (list_file, "%20s:%-5d %s:%s %s\n", + symbol_get_frag (ptr)->line->file->filename, + symbol_get_frag (ptr)->line->line, + segment_name (S_GET_SEGMENT (ptr)), + buf, S_GET_NAME (ptr)); + } + else + { + fprintf (list_file, "%33s:%s %s\n", + segment_name (S_GET_SEGMENT (ptr)), + buf, S_GET_NAME (ptr)); + } + + on_page++; + listing_page (NULL); + } + } + + } + if (!got_some) + { + fprintf (list_file, "NO DEFINED SYMBOLS\n"); + on_page++; + } + emit_line (NULL, "\n"); + + got_some = 0; + + for (ptr = symbol_rootP; ptr != (symbolS *) NULL; ptr = symbol_next (ptr)) + { + if (S_GET_NAME (ptr) && strlen (S_GET_NAME (ptr)) != 0) + { + if (S_GET_SEGMENT (ptr) == undefined_section) + { + if (!got_some) + { + got_some = 1; + + emit_line (NULL, "UNDEFINED SYMBOLS\n"); + } + + emit_line (NULL, "%s\n", S_GET_NAME (ptr)); + } + } + } + + if (!got_some) + emit_line (NULL, "NO UNDEFINED SYMBOLS\n"); +} + +typedef struct cached_line +{ + file_info_type * file; + unsigned int line; + char buffer [LISTING_RHS_WIDTH]; +} cached_line; + +static void +print_source (file_info_type * current_file, + list_info_type * list, + unsigned int width) +{ +#define NUM_CACHE_LINES 3 + static cached_line cached_lines[NUM_CACHE_LINES]; + static int next_free_line = 0; + cached_line * cache = NULL; + + if (current_file->linenum > list->hll_line + && list->hll_line > 0) + { + /* This can happen with modern optimizing compilers. The source + lines from the high level language input program are split up + and interleaved, meaning the line number we want to display + (list->hll_line) can have already been displayed. We have + three choices: + + a. Do nothing, since we have already displayed the source + line. This was the old behaviour. + + b. Display the particular line requested again, but only + that line. This is the new behaviour. + + c. Display the particular line requested again and reset + the current_file->line_num value so that we redisplay + all the following lines as well the next time we + encounter a larger line number. */ + int i; + + /* Check the cache, maybe we already have the line saved. */ + for (i = 0; i < NUM_CACHE_LINES; i++) + if (cached_lines[i].file == current_file + && cached_lines[i].line == list->hll_line) + { + cache = cached_lines + i; + break; + } + + if (i == NUM_CACHE_LINES) + { + cache = cached_lines + next_free_line; + next_free_line ++; + if (next_free_line == NUM_CACHE_LINES) + next_free_line = 0; + + cache->file = current_file; + cache->line = list->hll_line; + cache->buffer[0] = 0; + rebuffer_line (current_file, cache->line, cache->buffer, width); + } + + emit_line (list, "%4u:%-13s **** %s\n", + cache->line, cache->file->filename, cache->buffer); + return; + } + + if (!current_file->at_end) + { + int num_lines_shown = 0; + + while (current_file->linenum < list->hll_line + && !current_file->at_end) + { + char *p; + + cache = cached_lines + next_free_line; + cache->file = current_file; + cache->line = current_file->linenum + 1; + cache->buffer[0] = 0; + p = buffer_line (current_file, cache->buffer, width); + + /* Cache optimization: If printing a group of lines + cache the first and last lines in the group. */ + if (num_lines_shown == 0) + { + next_free_line ++; + if (next_free_line == NUM_CACHE_LINES) + next_free_line = 0; + } + + emit_line (list, "%4u:%-13s **** %s\n", + cache->line, cache->file->filename, p); + num_lines_shown ++; + } + } +} + +/* Sometimes the user doesn't want to be bothered by the debugging + records inserted by the compiler, see if the line is suspicious. */ + +static int +debugging_pseudo (list_info_type *list, const char *line) +{ +#ifdef OBJ_ELF + static int in_debug; + int was_debug; +#endif + + if (list->debugging) + { +#ifdef OBJ_ELF + in_debug = 1; +#endif + return 1; + } +#ifdef OBJ_ELF + was_debug = in_debug; + in_debug = 0; +#endif + + while (ISSPACE (*line)) + line++; + + if (*line != '.') + { +#ifdef OBJ_ELF + /* The ELF compiler sometimes emits blank lines after switching + out of a debugging section. If the next line drops us back + into debugging information, then don't print the blank line. + This is a hack for a particular compiler behaviour, not a + general case. */ + if (was_debug + && *line == '\0' + && list->next != NULL + && list->next->debugging) + { + in_debug = 1; + return 1; + } +#endif + + return 0; + } + + line++; + + if (strncmp (line, "def", 3) == 0) + return 1; + if (strncmp (line, "val", 3) == 0) + return 1; + if (strncmp (line, "scl", 3) == 0) + return 1; + if (strncmp (line, "line", 4) == 0) + return 1; + if (strncmp (line, "endef", 5) == 0) + return 1; + if (strncmp (line, "ln", 2) == 0) + return 1; + if (strncmp (line, "type", 4) == 0) + return 1; + if (strncmp (line, "size", 4) == 0) + return 1; + if (strncmp (line, "dim", 3) == 0) + return 1; + if (strncmp (line, "tag", 3) == 0) + return 1; + if (strncmp (line, "stabs", 5) == 0) + return 1; + if (strncmp (line, "stabn", 5) == 0) + return 1; + + return 0; +} + +static void +listing_listing (char *name ATTRIBUTE_UNUSED) +{ + list_info_type *list = head; + file_info_type *current_hll_file = (file_info_type *) NULL; + char *buffer; + char *p; + int show_listing = 1; + unsigned int width; + + buffer = (char *) xmalloc (listing_rhs_width); + data_buffer = (char *) xmalloc (MAX_BYTES); + eject = 1; + list = head->next; + + while (list) + { + unsigned int list_line; + + width = listing_rhs_width > paper_width ? paper_width : + listing_rhs_width; + + list_line = list->line; + switch (list->edict) + { + case EDICT_LIST: + /* Skip all lines up to the current. */ + list_line--; + break; + case EDICT_NOLIST: + show_listing--; + break; + case EDICT_NOLIST_NEXT: + if (show_listing == 0) + list_line--; + break; + case EDICT_EJECT: + break; + case EDICT_NONE: + break; + case EDICT_TITLE: + title = list->edict_arg; + break; + case EDICT_SBTTL: + subtitle = list->edict_arg; + break; + default: + abort (); + } + + if (show_listing <= 0) + { + while (list->file->linenum < list_line + && !list->file->at_end) + p = buffer_line (list->file, buffer, width); + } + + if (list->edict == EDICT_LIST + || (list->edict == EDICT_NOLIST_NEXT && show_listing == 0)) + { + /* Enable listing for the single line that caused the enable. */ + list_line++; + show_listing++; + } + + if (show_listing > 0) + { + /* Scan down the list and print all the stuff which can be done + with this line (or lines). */ + if (list->hll_file) + current_hll_file = list->hll_file; + + if (current_hll_file && list->hll_line && (listing & LISTING_HLL)) + print_source (current_hll_file, list, width); + + if (list->line_contents) + { + if (!((listing & LISTING_NODEBUG) + && debugging_pseudo (list, list->line_contents))) + print_lines (list, + list->file->linenum == 0 ? list->line : list->file->linenum, + list->line_contents, calc_hex (list)); + + free (list->line_contents); + list->line_contents = NULL; + } + else + { + while (list->file->linenum < list_line + && !list->file->at_end) + { + unsigned int address; + + p = buffer_line (list->file, buffer, width); + + if (list->file->linenum < list_line) + address = ~(unsigned int) 0; + else + address = calc_hex (list); + + if (!((listing & LISTING_NODEBUG) + && debugging_pseudo (list, p))) + print_lines (list, list->file->linenum, p, address); + } + } + + if (list->edict == EDICT_EJECT) + eject = 1; + } + + if (list->edict == EDICT_NOLIST_NEXT && show_listing == 1) + --show_listing; + + list = list->next; + } + + free (buffer); + free (data_buffer); + data_buffer = NULL; +} + +/* Print time stamp in ISO format: yyyy-mm-ddThh:mm:ss.ss+/-zzzz. */ + +static void +print_timestamp (void) +{ + const time_t now = time (NULL); + struct tm * timestamp; + char stampstr[MAX_DATELEN]; + + /* Any portable way to obtain subsecond values??? */ + timestamp = localtime (&now); + strftime (stampstr, MAX_DATELEN, "%Y-%m-%dT%H:%M:%S.000%z", timestamp); + fprintf (list_file, _("\n time stamp \t: %s\n\n"), stampstr); +} + +static void +print_single_option (char * opt, int *pos) +{ + int opt_len = strlen (opt); + + if ((*pos + opt_len) < paper_width) + { + fprintf (list_file, _("%s "), opt); + *pos = *pos + opt_len; + } + else + { + fprintf (list_file, _("\n\t%s "), opt); + *pos = opt_len; + } +} + +/* Print options passed to as. */ + +static void +print_options (char ** argv) +{ + const char *field_name = _("\n options passed\t: "); + int pos = strlen (field_name); + char **p; + + fputs (field_name, list_file); + for (p = &argv[1]; *p != NULL; p++) + if (**p == '-') + { + /* Ignore these. */ + if (strcmp (*p, "-o") == 0) + { + if (p[1] != NULL) + p++; + continue; + } + if (strcmp (*p, "-v") == 0) + continue; + + print_single_option (*p, &pos); + } +} + +/* Print a first section with basic info like file names, as version, + options passed, target, and timestamp. + The format of this section is as follows: + + AS VERSION + + fieldname TAB ':' fieldcontents + { TAB fieldcontents-cont } */ + +static void +listing_general_info (char ** argv) +{ + /* Print the stuff on the first line. */ + eject = 1; + listing_page (NULL); + + fprintf (list_file, + _(" GNU assembler version %s (%s)\n\t using BFD version %s."), + VERSION, TARGET_ALIAS, BFD_VERSION_STRING); + print_options (argv); + fprintf (list_file, _("\n input file \t: %s"), fn); + fprintf (list_file, _("\n output file \t: %s"), out_file_name); + fprintf (list_file, _("\n target \t: %s"), TARGET_CANONICAL); + print_timestamp (); +} + +void +listing_print (char *name, char **argv) +{ + int using_stdout; + + title = ""; + subtitle = ""; + + if (name == NULL) + { + list_file = stdout; + using_stdout = 1; + } + else + { + list_file = fopen (name, FOPEN_WT); + if (list_file != NULL) + using_stdout = 0; + else + { + as_warn (_("can't open %s: %s"), name, xstrerror (errno)); + list_file = stdout; + using_stdout = 1; + } + } + + if (listing & LISTING_NOFORM) + paper_height = 0; + + if (listing & LISTING_GENERAL) + listing_general_info (argv); + + if (listing & LISTING_LISTING) + listing_listing (name); + + if (listing & LISTING_SYMBOLS) + list_symbol_table (); + + if (! using_stdout) + { + if (fclose (list_file) == EOF) + as_warn (_("can't close %s: %s"), name, xstrerror (errno)); + } + + if (last_open_file) + fclose (last_open_file); +} + +void +listing_file (const char *name) +{ + fn = name; +} + +void +listing_eject (int ignore ATTRIBUTE_UNUSED) +{ + if (listing) + listing_tail->edict = EDICT_EJECT; +} + +/* Turn listing on or off. An argument of 0 means to turn off + listing. An argument of 1 means to turn on listing. An argument + of 2 means to turn off listing, but as of the next line; that is, + the current line should be listed, but the next line should not. */ + +void +listing_list (int on) +{ + if (listing) + { + switch (on) + { + case 0: + if (listing_tail->edict == EDICT_LIST) + listing_tail->edict = EDICT_NONE; + else + listing_tail->edict = EDICT_NOLIST; + break; + case 1: + if (listing_tail->edict == EDICT_NOLIST + || listing_tail->edict == EDICT_NOLIST_NEXT) + listing_tail->edict = EDICT_NONE; + else + listing_tail->edict = EDICT_LIST; + break; + case 2: + listing_tail->edict = EDICT_NOLIST_NEXT; + break; + default: + abort (); + } + } +} + +void +listing_psize (int width_only) +{ + if (! width_only) + { + paper_height = get_absolute_expression (); + + if (paper_height < 0 || paper_height > 1000) + { + paper_height = 0; + as_warn (_("strange paper height, set to no form")); + } + + if (*input_line_pointer != ',') + { + demand_empty_rest_of_line (); + return; + } + + ++input_line_pointer; + } + + paper_width = get_absolute_expression (); + + demand_empty_rest_of_line (); +} + +void +listing_nopage (int ignore ATTRIBUTE_UNUSED) +{ + paper_height = 0; +} + +void +listing_title (int depth) +{ + int quoted; + char *start; + char *ttl; + unsigned int length; + + SKIP_WHITESPACE (); + if (*input_line_pointer != '\"') + quoted = 0; + else + { + quoted = 1; + ++input_line_pointer; + } + + start = input_line_pointer; + + while (*input_line_pointer) + { + if (quoted + ? *input_line_pointer == '\"' + : is_end_of_line[(unsigned char) *input_line_pointer]) + { + if (listing) + { + length = input_line_pointer - start; + ttl = (char *) xmalloc (length + 1); + memcpy (ttl, start, length); + ttl[length] = 0; + listing_tail->edict = depth ? EDICT_SBTTL : EDICT_TITLE; + listing_tail->edict_arg = ttl; + } + if (quoted) + input_line_pointer++; + demand_empty_rest_of_line (); + return; + } + else if (*input_line_pointer == '\n') + { + as_bad (_("new line in title")); + demand_empty_rest_of_line (); + return; + } + else + { + input_line_pointer++; + } + } +} + +void +listing_source_line (unsigned int line) +{ + if (listing) + { + new_frag (); + listing_tail->hll_line = line; + new_frag (); + } +} + +void +listing_source_file (const char *file) +{ + if (listing) + listing_tail->hll_file = file_info (file); +} + +#else + +/* Dummy functions for when compiled without listing enabled. */ + +void +listing_list (int on) +{ + s_ignore (0); +} + +void +listing_eject (int ignore) +{ + s_ignore (0); +} + +void +listing_psize (int ignore) +{ + s_ignore (0); +} + +void +listing_nopage (int ignore) +{ + s_ignore (0); +} + +void +listing_title (int depth) +{ + s_ignore (0); +} + +void +listing_file (const char *name) +{ +} + +void +listing_newline (char *name) +{ +} + +void +listing_source_line (unsigned int n) +{ +} + +void +listing_source_file (const char *n) +{ +} + +#endif diff --git a/contrib/toolchain/binutils/gas/listing.h b/contrib/toolchain/binutils/gas/listing.h new file mode 100644 index 0000000000..79afdac6b6 --- /dev/null +++ b/contrib/toolchain/binutils/gas/listing.h @@ -0,0 +1,67 @@ +/* This file is listing.h + Copyright 1987, 1988, 1989, 1990, 1991, 1992, 1993, 1995, 1997, 1998, + 2003, 2005, 2007, 2008, 2009 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef __listing_h__ +#define __listing_h__ + +#define LISTING_LISTING 1 +#define LISTING_SYMBOLS 2 +#define LISTING_NOFORM 4 +#define LISTING_HLL 8 +#define LISTING_NODEBUG 16 +#define LISTING_NOCOND 32 +#define LISTING_MACEXP 64 +#define LISTING_GENERAL 128 + +#define LISTING_DEFAULT (LISTING_LISTING | LISTING_HLL | LISTING_SYMBOLS) + +#ifndef NO_LISTING +#define LISTING_NEWLINE() { if (listing) listing_newline (NULL); } +#else +#define LISTING_NEWLINE() {;} +#endif +#define LISTING_EOF() LISTING_NEWLINE() + +#define LISTING_SKIP_COND() ((listing & LISTING_NOCOND) != 0) + +void listing_eject (int); +void listing_error (const char *message); +void listing_file (const char *name); +void listing_list (int on); +void listing_newline (char *ps); +void listing_prev_line (void); +void listing_print (char *, char **); +void listing_psize (int); +void listing_nopage (int); +void listing_source_file (const char *); +void listing_source_line (unsigned int); +void listing_title (int depth); +void listing_warning (const char *message); +void listing_width (unsigned int x); + +extern int listing_lhs_width; +extern int listing_lhs_width_second; +extern int listing_lhs_cont_lines; +extern int listing_rhs_width; + +#endif /* __listing_h__ */ + +/* end of listing.h */ diff --git a/contrib/toolchain/binutils/gas/literal.c b/contrib/toolchain/binutils/gas/literal.c new file mode 100644 index 0000000000..6cbbe7e54a --- /dev/null +++ b/contrib/toolchain/binutils/gas/literal.c @@ -0,0 +1,96 @@ +/* literal.c - GAS literal pool management. + Copyright 1994, 2000, 2005, 2007 Free Software Foundation, Inc. + Written by Ken Raeburn (raeburn@cygnus.com). + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* This isn't quite a "constant" pool. Some of the values may get + adjusted at run time, e.g., for symbolic relocations when shared + libraries are in use. It's more of a "literal" pool. + + On the Alpha, this should be used for .lita and .lit8. (Is there + ever a .lit4?) On the MIPS, it could be used for .lit4 as well. + + The expressions passed here should contain either constants or symbols, + not a combination of both. Typically, the constant pool is accessed + with some sort of GP register, so the size of the pool must be kept down + if possible. The exception is section offsets -- if you're storing a + pointer to the start of .data, for example, and your machine provides + for 16-bit signed addends, you might want to store .data+32K, so that + you can access all of the first 64K of .data with the one pointer. + + This isn't a requirement, just a guideline that can help keep .o file + size down. */ + +#include "as.h" +#include "subsegs.h" + +#ifdef NEED_LITERAL_POOL + +valueT +add_to_literal_pool (sym, addend, sec, size) + symbolS *sym; + valueT addend; + segT sec; + int size; +{ + segT current_section = now_seg; + int current_subsec = now_subseg; + valueT offset; + bfd_reloc_code_real_type reloc_type; + char *p; + segment_info_type *seginfo = seg_info (sec); + fixS *fixp; + + offset = 0; + /* @@ This assumes all entries in a given section will be of the same + size... Probably correct, but unwise to rely on. */ + /* This must always be called with the same subsegment. */ + if (seginfo->frchainP) + for (fixp = seginfo->frchainP->fix_root; + fixp != (fixS *) NULL; + fixp = fixp->fx_next, offset += size) + { + if (fixp->fx_addsy == sym && fixp->fx_offset == addend) + return offset; + } + + subseg_set (sec, 0); + p = frag_more (size); + memset (p, 0, size); + + switch (size) + { + case 4: + reloc_type = BFD_RELOC_32; + break; + case 8: + reloc_type = BFD_RELOC_64; + break; + default: + abort (); + } + fix_new (frag_now, p - frag_now->fr_literal, size, sym, addend, 0, + reloc_type); + + subseg_set (current_section, current_subsec); + offset = seginfo->literal_pool_size; + seginfo->literal_pool_size += size; + return offset; +} +#endif diff --git a/contrib/toolchain/binutils/gas/macro.c b/contrib/toolchain/binutils/gas/macro.c new file mode 100644 index 0000000000..75b9b7ef93 --- /dev/null +++ b/contrib/toolchain/binutils/gas/macro.c @@ -0,0 +1,1382 @@ +/* macro.c - macro support for gas + Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, + 2004, 2005, 2006, 2007, 2008, 2011, 2012, 2013 Free Software Foundation, Inc. + + Written by Steve and Judy Chamberlain of Cygnus Support, + sac@cygnus.com + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" +#include "safe-ctype.h" +#include "sb.h" +#include "macro.h" + +/* The routines in this file handle macro definition and expansion. + They are called by gas. */ + +#define ISWHITE(x) ((x) == ' ' || (x) == '\t') + +#define ISSEP(x) \ + ((x) == ' ' || (x) == '\t' || (x) == ',' || (x) == '"' || (x) == ';' \ + || (x) == ')' || (x) == '(' \ + || ((macro_alternate || macro_mri) && ((x) == '<' || (x) == '>'))) + +#define ISBASE(x) \ + ((x) == 'b' || (x) == 'B' \ + || (x) == 'q' || (x) == 'Q' \ + || (x) == 'h' || (x) == 'H' \ + || (x) == 'd' || (x) == 'D') + +/* The macro hash table. */ + +struct hash_control *macro_hash; + +/* Whether any macros have been defined. */ + +int macro_defined; + +/* Whether we are in alternate syntax mode. */ + +static int macro_alternate; + +/* Whether we are in MRI mode. */ + +static int macro_mri; + +/* Whether we should strip '@' characters. */ + +static int macro_strip_at; + +/* Function to use to parse an expression. */ + +static size_t (*macro_expr) (const char *, size_t, sb *, offsetT *); + +/* Number of macro expansions that have been done. */ + +static int macro_number; + +/* Initialize macro processing. */ + +void +macro_init (int alternate, int mri, int strip_at, + size_t (*exp) (const char *, size_t, sb *, offsetT *)) +{ + macro_hash = hash_new (); + macro_defined = 0; + macro_alternate = alternate; + macro_mri = mri; + macro_strip_at = strip_at; + macro_expr = exp; +} + +/* Switch in and out of alternate mode on the fly. */ + +void +macro_set_alternate (int alternate) +{ + macro_alternate = alternate; +} + +/* Switch in and out of MRI mode on the fly. */ + +void +macro_mri_mode (int mri) +{ + macro_mri = mri; +} + +/* Read input lines till we get to a TO string. + Increase nesting depth if we get a FROM string. + Put the results into sb at PTR. + FROM may be NULL (or will be ignored) if TO is "ENDR". + Add a new input line to an sb using GET_LINE. + Return 1 on success, 0 on unexpected EOF. */ + +int +buffer_and_nest (const char *from, const char *to, sb *ptr, + size_t (*get_line) (sb *)) +{ + size_t from_len; + size_t to_len = strlen (to); + int depth = 1; + size_t line_start = ptr->len; + size_t more = get_line (ptr); + + if (to_len == 4 && strcasecmp (to, "ENDR") == 0) + { + from = NULL; + from_len = 0; + } + else + from_len = strlen (from); + + while (more) + { + /* Try to find the first pseudo op on the line. */ + size_t i = line_start; + bfd_boolean had_colon = FALSE; + + /* With normal syntax we can suck what we want till we get + to the dot. With the alternate, labels have to start in + the first column, since we can't tell what's a label and + what's a pseudoop. */ + + if (! LABELS_WITHOUT_COLONS) + { + /* Skip leading whitespace. */ + while (i < ptr->len && ISWHITE (ptr->ptr[i])) + i++; + } + + for (;;) + { + /* Skip over a label, if any. */ + if (i >= ptr->len || ! is_name_beginner (ptr->ptr[i])) + break; + i++; + while (i < ptr->len && is_part_of_name (ptr->ptr[i])) + i++; + if (i < ptr->len && is_name_ender (ptr->ptr[i])) + i++; + /* Skip whitespace. */ + while (i < ptr->len && ISWHITE (ptr->ptr[i])) + i++; + /* Check for the colon. */ + if (i >= ptr->len || ptr->ptr[i] != ':') + { + /* LABELS_WITHOUT_COLONS doesn't mean we cannot have a + colon after a label. If we do have a colon on the + first label then handle more than one label on the + line, assuming that each label has a colon. */ + if (LABELS_WITHOUT_COLONS && !had_colon) + break; + i = line_start; + break; + } + i++; + line_start = i; + had_colon = TRUE; + } + + /* Skip trailing whitespace. */ + while (i < ptr->len && ISWHITE (ptr->ptr[i])) + i++; + + if (i < ptr->len && (ptr->ptr[i] == '.' + || NO_PSEUDO_DOT + || macro_mri)) + { + if (! flag_m68k_mri && ptr->ptr[i] == '.') + i++; + if (from == NULL + && strncasecmp (ptr->ptr + i, "IRPC", from_len = 4) != 0 + && strncasecmp (ptr->ptr + i, "IRP", from_len = 3) != 0 + && strncasecmp (ptr->ptr + i, "IREPC", from_len = 5) != 0 + && strncasecmp (ptr->ptr + i, "IREP", from_len = 4) != 0 + && strncasecmp (ptr->ptr + i, "REPT", from_len = 4) != 0 + && strncasecmp (ptr->ptr + i, "REP", from_len = 3) != 0) + from_len = 0; + if ((from != NULL + ? strncasecmp (ptr->ptr + i, from, from_len) == 0 + : from_len > 0) + && (ptr->len == (i + from_len) + || ! (is_part_of_name (ptr->ptr[i + from_len]) + || is_name_ender (ptr->ptr[i + from_len])))) + depth++; + if (strncasecmp (ptr->ptr + i, to, to_len) == 0 + && (ptr->len == (i + to_len) + || ! (is_part_of_name (ptr->ptr[i + to_len]) + || is_name_ender (ptr->ptr[i + to_len])))) + { + depth--; + if (depth == 0) + { + /* Reset the string to not include the ending rune. */ + ptr->len = line_start; + break; + } + } + } + + /* Add the original end-of-line char to the end and keep running. */ + sb_add_char (ptr, more); + line_start = ptr->len; + more = get_line (ptr); + } + + /* Return 1 on success, 0 on unexpected EOF. */ + return depth == 0; +} + +/* Pick up a token. */ + +static size_t +get_token (size_t idx, sb *in, sb *name) +{ + if (idx < in->len + && is_name_beginner (in->ptr[idx])) + { + sb_add_char (name, in->ptr[idx++]); + while (idx < in->len + && is_part_of_name (in->ptr[idx])) + { + sb_add_char (name, in->ptr[idx++]); + } + if (idx < in->len + && is_name_ender (in->ptr[idx])) + { + sb_add_char (name, in->ptr[idx++]); + } + } + /* Ignore trailing &. */ + if (macro_alternate && idx < in->len && in->ptr[idx] == '&') + idx++; + return idx; +} + +/* Pick up a string. */ + +static size_t +getstring (size_t idx, sb *in, sb *acc) +{ + while (idx < in->len + && (in->ptr[idx] == '"' + || (in->ptr[idx] == '<' && (macro_alternate || macro_mri)) + || (in->ptr[idx] == '\'' && macro_alternate))) + { + if (in->ptr[idx] == '<') + { + int nest = 0; + idx++; + while ((in->ptr[idx] != '>' || nest) + && idx < in->len) + { + if (in->ptr[idx] == '!') + { + idx++; + sb_add_char (acc, in->ptr[idx++]); + } + else + { + if (in->ptr[idx] == '>') + nest--; + if (in->ptr[idx] == '<') + nest++; + sb_add_char (acc, in->ptr[idx++]); + } + } + idx++; + } + else if (in->ptr[idx] == '"' || in->ptr[idx] == '\'') + { + char tchar = in->ptr[idx]; + int escaped = 0; + + idx++; + + while (idx < in->len) + { + if (in->ptr[idx - 1] == '\\') + escaped ^= 1; + else + escaped = 0; + + if (macro_alternate && in->ptr[idx] == '!') + { + idx ++; + + sb_add_char (acc, in->ptr[idx]); + + idx ++; + } + else if (escaped && in->ptr[idx] == tchar) + { + sb_add_char (acc, tchar); + idx ++; + } + else + { + if (in->ptr[idx] == tchar) + { + idx ++; + + if (idx >= in->len || in->ptr[idx] != tchar) + break; + } + + sb_add_char (acc, in->ptr[idx]); + idx ++; + } + } + } + } + + return idx; +} + +/* Fetch string from the input stream, + rules: + 'Bxyx -> return 'Bxyza + % -> return string of decimal value of + "string" -> return string + (string) -> return (string-including-whitespaces) + xyx -> return xyz. */ + +static size_t +get_any_string (size_t idx, sb *in, sb *out) +{ + sb_reset (out); + idx = sb_skip_white (idx, in); + + if (idx < in->len) + { + if (in->len > idx + 2 && in->ptr[idx + 1] == '\'' && ISBASE (in->ptr[idx])) + { + while (!ISSEP (in->ptr[idx])) + sb_add_char (out, in->ptr[idx++]); + } + else if (in->ptr[idx] == '%' && macro_alternate) + { + offsetT val; + char buf[20]; + + /* Turns the next expression into a string. */ + /* xgettext: no-c-format */ + idx = (*macro_expr) (_("% operator needs absolute expression"), + idx + 1, + in, + &val); + sprintf (buf, "%" BFD_VMA_FMT "d", val); + sb_add_string (out, buf); + } + else if (in->ptr[idx] == '"' + || (in->ptr[idx] == '<' && (macro_alternate || macro_mri)) + || (macro_alternate && in->ptr[idx] == '\'')) + { + if (macro_alternate && ! macro_strip_at && in->ptr[idx] != '<') + { + /* Keep the quotes. */ + sb_add_char (out, '"'); + idx = getstring (idx, in, out); + sb_add_char (out, '"'); + } + else + { + idx = getstring (idx, in, out); + } + } + else + { + char *br_buf = (char *) xmalloc (1); + char *in_br = br_buf; + + *in_br = '\0'; + while (idx < in->len + && (*in_br + || (in->ptr[idx] != ' ' + && in->ptr[idx] != '\t')) + && in->ptr[idx] != ',' + && (in->ptr[idx] != '<' + || (! macro_alternate && ! macro_mri))) + { + char tchar = in->ptr[idx]; + + switch (tchar) + { + case '"': + case '\'': + sb_add_char (out, in->ptr[idx++]); + while (idx < in->len + && in->ptr[idx] != tchar) + sb_add_char (out, in->ptr[idx++]); + if (idx == in->len) + { + free (br_buf); + return idx; + } + break; + case '(': + case '[': + if (in_br > br_buf) + --in_br; + else + { + br_buf = (char *) xmalloc (strlen (in_br) + 2); + strcpy (br_buf + 1, in_br); + free (in_br); + in_br = br_buf; + } + *in_br = tchar; + break; + case ')': + if (*in_br == '(') + ++in_br; + break; + case ']': + if (*in_br == '[') + ++in_br; + break; + } + sb_add_char (out, tchar); + ++idx; + } + free (br_buf); + } + } + + return idx; +} + +/* Allocate a new formal. */ + +static formal_entry * +new_formal (void) +{ + formal_entry *formal; + + formal = (formal_entry *) xmalloc (sizeof (formal_entry)); + + sb_new (&formal->name); + sb_new (&formal->def); + sb_new (&formal->actual); + formal->next = NULL; + formal->type = FORMAL_OPTIONAL; + return formal; +} + +/* Free a formal. */ + +static void +del_formal (formal_entry *formal) +{ + sb_kill (&formal->actual); + sb_kill (&formal->def); + sb_kill (&formal->name); + free (formal); +} + +/* Pick up the formal parameters of a macro definition. */ + +static size_t +do_formals (macro_entry *macro, size_t idx, sb *in) +{ + formal_entry **p = ¯o->formals; + const char *name; + + idx = sb_skip_white (idx, in); + while (idx < in->len) + { + formal_entry *formal = new_formal (); + size_t cidx; + + idx = get_token (idx, in, &formal->name); + if (formal->name.len == 0) + { + if (macro->formal_count) + --idx; + del_formal (formal); /* 'formal' goes out of scope. */ + break; + } + idx = sb_skip_white (idx, in); + /* This is a formal. */ + name = sb_terminate (&formal->name); + if (! macro_mri + && idx < in->len + && in->ptr[idx] == ':' + && (! is_name_beginner (':') + || idx + 1 >= in->len + || ! is_part_of_name (in->ptr[idx + 1]))) + { + /* Got a qualifier. */ + sb qual; + + sb_new (&qual); + idx = get_token (sb_skip_white (idx + 1, in), in, &qual); + sb_terminate (&qual); + if (qual.len == 0) + as_bad_where (macro->file, + macro->line, + _("Missing parameter qualifier for `%s' in macro `%s'"), + name, + macro->name); + else if (strcmp (qual.ptr, "req") == 0) + formal->type = FORMAL_REQUIRED; + else if (strcmp (qual.ptr, "vararg") == 0) + formal->type = FORMAL_VARARG; + else + as_bad_where (macro->file, + macro->line, + _("`%s' is not a valid parameter qualifier for `%s' in macro `%s'"), + qual.ptr, + name, + macro->name); + sb_kill (&qual); + idx = sb_skip_white (idx, in); + } + if (idx < in->len && in->ptr[idx] == '=') + { + /* Got a default. */ + idx = get_any_string (idx + 1, in, &formal->def); + idx = sb_skip_white (idx, in); + if (formal->type == FORMAL_REQUIRED) + { + sb_reset (&formal->def); + as_warn_where (macro->file, + macro->line, + _("Pointless default value for required parameter `%s' in macro `%s'"), + name, + macro->name); + } + } + + /* Add to macro's hash table. */ + if (! hash_find (macro->formal_hash, name)) + hash_jam (macro->formal_hash, name, formal); + else + as_bad_where (macro->file, + macro->line, + _("A parameter named `%s' already exists for macro `%s'"), + name, + macro->name); + + formal->index = macro->formal_count++; + *p = formal; + p = &formal->next; + if (formal->type == FORMAL_VARARG) + break; + cidx = idx; + idx = sb_skip_comma (idx, in); + if (idx != cidx && idx >= in->len) + { + idx = cidx; + break; + } + } + + if (macro_mri) + { + formal_entry *formal = new_formal (); + + /* Add a special NARG formal, which macro_expand will set to the + number of arguments. */ + /* The same MRI assemblers which treat '@' characters also use + the name $NARG. At least until we find an exception. */ + if (macro_strip_at) + name = "$NARG"; + else + name = "NARG"; + + sb_add_string (&formal->name, name); + + /* Add to macro's hash table. */ + if (hash_find (macro->formal_hash, name)) + as_bad_where (macro->file, + macro->line, + _("Reserved word `%s' used as parameter in macro `%s'"), + name, + macro->name); + hash_jam (macro->formal_hash, name, formal); + + formal->index = NARG_INDEX; + *p = formal; + } + + return idx; +} + +/* Free the memory allocated to a macro. */ + +static void +free_macro (macro_entry *macro) +{ + formal_entry *formal; + + for (formal = macro->formals; formal; ) + { + formal_entry *f; + + f = formal; + formal = formal->next; + del_formal (f); + } + hash_die (macro->formal_hash); + sb_kill (¯o->sub); + free (macro); +} + +/* Define a new macro. Returns NULL on success, otherwise returns an + error message. If NAMEP is not NULL, *NAMEP is set to the name of + the macro which was defined. */ + +const char * +define_macro (size_t idx, sb *in, sb *label, + size_t (*get_line) (sb *), + char *file, unsigned int line, + const char **namep) +{ + macro_entry *macro; + sb name; + const char *error = NULL; + + macro = (macro_entry *) xmalloc (sizeof (macro_entry)); + sb_new (¯o->sub); + sb_new (&name); + macro->file = file; + macro->line = line; + + macro->formal_count = 0; + macro->formals = 0; + macro->formal_hash = hash_new_sized (7); + + idx = sb_skip_white (idx, in); + if (! buffer_and_nest ("MACRO", "ENDM", ¯o->sub, get_line)) + error = _("unexpected end of file in macro `%s' definition"); + if (label != NULL && label->len != 0) + { + sb_add_sb (&name, label); + macro->name = sb_terminate (&name); + if (idx < in->len && in->ptr[idx] == '(') + { + /* It's the label: MACRO (formals,...) sort */ + idx = do_formals (macro, idx + 1, in); + if (idx < in->len && in->ptr[idx] == ')') + idx = sb_skip_white (idx + 1, in); + else if (!error) + error = _("missing `)' after formals in macro definition `%s'"); + } + else + { + /* It's the label: MACRO formals,... sort */ + idx = do_formals (macro, idx, in); + } + } + else + { + size_t cidx; + + idx = get_token (idx, in, &name); + macro->name = sb_terminate (&name); + if (name.len == 0) + error = _("Missing macro name"); + cidx = sb_skip_white (idx, in); + idx = sb_skip_comma (cidx, in); + if (idx == cidx || idx < in->len) + idx = do_formals (macro, idx, in); + else + idx = cidx; + } + if (!error && idx < in->len) + error = _("Bad parameter list for macro `%s'"); + + /* And stick it in the macro hash table. */ + for (idx = 0; idx < name.len; idx++) + name.ptr[idx] = TOLOWER (name.ptr[idx]); + if (hash_find (macro_hash, macro->name)) + error = _("Macro `%s' was already defined"); + if (!error) + error = hash_jam (macro_hash, macro->name, (void *) macro); + + if (namep != NULL) + *namep = macro->name; + + if (!error) + macro_defined = 1; + else + free_macro (macro); + + return error; +} + +/* Scan a token, and then skip KIND. */ + +static size_t +get_apost_token (size_t idx, sb *in, sb *name, int kind) +{ + idx = get_token (idx, in, name); + if (idx < in->len + && in->ptr[idx] == kind + && (! macro_mri || macro_strip_at) + && (! macro_strip_at || kind == '@')) + idx++; + return idx; +} + +/* Substitute the actual value for a formal parameter. */ + +static size_t +sub_actual (size_t start, sb *in, sb *t, struct hash_control *formal_hash, + int kind, sb *out, int copyifnotthere) +{ + size_t src; + formal_entry *ptr; + + src = get_apost_token (start, in, t, kind); + /* See if it's in the macro's hash table, unless this is + macro_strip_at and kind is '@' and the token did not end in '@'. */ + if (macro_strip_at + && kind == '@' + && (src == start || in->ptr[src - 1] != '@')) + ptr = NULL; + else + ptr = (formal_entry *) hash_find (formal_hash, sb_terminate (t)); + if (ptr) + { + if (ptr->actual.len) + { + sb_add_sb (out, &ptr->actual); + } + else + { + sb_add_sb (out, &ptr->def); + } + } + else if (kind == '&') + { + /* Doing this permits people to use & in macro bodies. */ + sb_add_char (out, '&'); + sb_add_sb (out, t); + if (src != start && in->ptr[src - 1] == '&') + sb_add_char (out, '&'); + } + else if (copyifnotthere) + { + sb_add_sb (out, t); + } + else + { + sb_add_char (out, '\\'); + sb_add_sb (out, t); + } + return src; +} + +/* Expand the body of a macro. */ + +static const char * +macro_expand_body (sb *in, sb *out, formal_entry *formals, + struct hash_control *formal_hash, const macro_entry *macro) +{ + sb t; + size_t src = 0; + int inquote = 0, macro_line = 0; + formal_entry *loclist = NULL; + const char *err = NULL; + + sb_new (&t); + + while (src < in->len && !err) + { + if (in->ptr[src] == '&') + { + sb_reset (&t); + if (macro_mri) + { + if (src + 1 < in->len && in->ptr[src + 1] == '&') + src = sub_actual (src + 2, in, &t, formal_hash, '\'', out, 1); + else + sb_add_char (out, in->ptr[src++]); + } + else + { + /* Permit macro parameter substition delineated with + an '&' prefix and optional '&' suffix. */ + src = sub_actual (src + 1, in, &t, formal_hash, '&', out, 0); + } + } + else if (in->ptr[src] == '\\') + { + src++; + if (src < in->len && in->ptr[src] == '(') + { + /* Sub in till the next ')' literally. */ + src++; + while (src < in->len && in->ptr[src] != ')') + { + sb_add_char (out, in->ptr[src++]); + } + if (src < in->len) + src++; + else if (!macro) + err = _("missing `)'"); + else + as_bad_where (macro->file, macro->line + macro_line, _("missing `)'")); + } + else if (src < in->len && in->ptr[src] == '@') + { + /* Sub in the macro invocation number. */ + + char buffer[10]; + src++; + sprintf (buffer, "%d", macro_number); + sb_add_string (out, buffer); + } + else if (src < in->len && in->ptr[src] == '&') + { + /* This is a preprocessor variable name, we don't do them + here. */ + sb_add_char (out, '\\'); + sb_add_char (out, '&'); + src++; + } + else if (macro_mri && src < in->len && ISALNUM (in->ptr[src])) + { + int ind; + formal_entry *f; + + if (ISDIGIT (in->ptr[src])) + ind = in->ptr[src] - '0'; + else if (ISUPPER (in->ptr[src])) + ind = in->ptr[src] - 'A' + 10; + else + ind = in->ptr[src] - 'a' + 10; + ++src; + for (f = formals; f != NULL; f = f->next) + { + if (f->index == ind - 1) + { + if (f->actual.len != 0) + sb_add_sb (out, &f->actual); + else + sb_add_sb (out, &f->def); + break; + } + } + } + else + { + sb_reset (&t); + src = sub_actual (src, in, &t, formal_hash, '\'', out, 0); + } + } + else if ((macro_alternate || macro_mri) + && is_name_beginner (in->ptr[src]) + && (! inquote + || ! macro_strip_at + || (src > 0 && in->ptr[src - 1] == '@'))) + { + if (! macro + || src + 5 >= in->len + || strncasecmp (in->ptr + src, "LOCAL", 5) != 0 + || ! ISWHITE (in->ptr[src + 5]) + /* PR 11507: Skip keyword LOCAL if it is found inside a quoted string. */ + || inquote) + { + sb_reset (&t); + src = sub_actual (src, in, &t, formal_hash, + (macro_strip_at && inquote) ? '@' : '\'', + out, 1); + } + else + { + src = sb_skip_white (src + 5, in); + while (in->ptr[src] != '\n') + { + const char *name; + formal_entry *f = new_formal (); + + src = get_token (src, in, &f->name); + name = sb_terminate (&f->name); + if (! hash_find (formal_hash, name)) + { + static int loccnt; + char buf[20]; + + f->index = LOCAL_INDEX; + f->next = loclist; + loclist = f; + + sprintf (buf, IS_ELF ? ".LL%04x" : "LL%04x", ++loccnt); + sb_add_string (&f->actual, buf); + + err = hash_jam (formal_hash, name, f); + if (err != NULL) + break; + } + else + { + as_bad_where (macro->file, + macro->line + macro_line, + _("`%s' was already used as parameter (or another local) name"), + name); + del_formal (f); + } + + src = sb_skip_comma (src, in); + } + } + } + else if (in->ptr[src] == '"' + || (macro_mri && in->ptr[src] == '\'')) + { + inquote = !inquote; + sb_add_char (out, in->ptr[src++]); + } + else if (in->ptr[src] == '@' && macro_strip_at) + { + ++src; + if (src < in->len + && in->ptr[src] == '@') + { + sb_add_char (out, '@'); + ++src; + } + } + else if (macro_mri + && in->ptr[src] == '=' + && src + 1 < in->len + && in->ptr[src + 1] == '=') + { + formal_entry *ptr; + + sb_reset (&t); + src = get_token (src + 2, in, &t); + ptr = (formal_entry *) hash_find (formal_hash, sb_terminate (&t)); + if (ptr == NULL) + { + /* FIXME: We should really return a warning string here, + but we can't, because the == might be in the MRI + comment field, and, since the nature of the MRI + comment field depends upon the exact instruction + being used, we don't have enough information here to + figure out whether it is or not. Instead, we leave + the == in place, which should cause a syntax error if + it is not in a comment. */ + sb_add_char (out, '='); + sb_add_char (out, '='); + sb_add_sb (out, &t); + } + else + { + if (ptr->actual.len) + { + sb_add_string (out, "-1"); + } + else + { + sb_add_char (out, '0'); + } + } + } + else + { + if (in->ptr[src] == '\n') + ++macro_line; + sb_add_char (out, in->ptr[src++]); + } + } + + sb_kill (&t); + + while (loclist != NULL) + { + formal_entry *f; + const char *name; + + f = loclist->next; + name = sb_terminate (&loclist->name); + hash_delete (formal_hash, name, f == NULL); + del_formal (loclist); + loclist = f; + } + + return err; +} + +/* Assign values to the formal parameters of a macro, and expand the + body. */ + +static const char * +macro_expand (size_t idx, sb *in, macro_entry *m, sb *out) +{ + sb t; + formal_entry *ptr; + formal_entry *f; + int is_keyword = 0; + int narg = 0; + const char *err = NULL; + + sb_new (&t); + + /* Reset any old value the actuals may have. */ + for (f = m->formals; f; f = f->next) + sb_reset (&f->actual); + f = m->formals; + while (f != NULL && f->index < 0) + f = f->next; + + if (macro_mri) + { + /* The macro may be called with an optional qualifier, which may + be referred to in the macro body as \0. */ + if (idx < in->len && in->ptr[idx] == '.') + { + /* The Microtec assembler ignores this if followed by a white space. + (Macro invocation with empty extension) */ + idx++; + if ( idx < in->len + && in->ptr[idx] != ' ' + && in->ptr[idx] != '\t') + { + formal_entry *n = new_formal (); + + n->index = QUAL_INDEX; + + n->next = m->formals; + m->formals = n; + + idx = get_any_string (idx, in, &n->actual); + } + } + } + + /* Peel off the actuals and store them away in the hash tables' actuals. */ + idx = sb_skip_white (idx, in); + while (idx < in->len) + { + size_t scan; + + /* Look and see if it's a positional or keyword arg. */ + scan = idx; + while (scan < in->len + && !ISSEP (in->ptr[scan]) + && !(macro_mri && in->ptr[scan] == '\'') + && (!macro_alternate && in->ptr[scan] != '=')) + scan++; + if (scan < in->len && !macro_alternate && in->ptr[scan] == '=') + { + is_keyword = 1; + + /* It's OK to go from positional to keyword. */ + + /* This is a keyword arg, fetch the formal name and + then the actual stuff. */ + sb_reset (&t); + idx = get_token (idx, in, &t); + if (in->ptr[idx] != '=') + { + err = _("confusion in formal parameters"); + break; + } + + /* Lookup the formal in the macro's list. */ + ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t)); + if (!ptr) + { + as_bad (_("Parameter named `%s' does not exist for macro `%s'"), + t.ptr, + m->name); + sb_reset (&t); + idx = get_any_string (idx + 1, in, &t); + } + else + { + /* Insert this value into the right place. */ + if (ptr->actual.len) + { + as_warn (_("Value for parameter `%s' of macro `%s' was already specified"), + ptr->name.ptr, + m->name); + sb_reset (&ptr->actual); + } + idx = get_any_string (idx + 1, in, &ptr->actual); + if (ptr->actual.len > 0) + ++narg; + } + } + else + { + if (is_keyword) + { + err = _("can't mix positional and keyword arguments"); + break; + } + + if (!f) + { + formal_entry **pf; + int c; + + if (!macro_mri) + { + err = _("too many positional arguments"); + break; + } + + f = new_formal (); + + c = -1; + for (pf = &m->formals; *pf != NULL; pf = &(*pf)->next) + if ((*pf)->index >= c) + c = (*pf)->index + 1; + if (c == -1) + c = 0; + *pf = f; + f->index = c; + } + + if (f->type != FORMAL_VARARG) + idx = get_any_string (idx, in, &f->actual); + else + { + sb_add_buffer (&f->actual, in->ptr + idx, in->len - idx); + idx = in->len; + } + if (f->actual.len > 0) + ++narg; + do + { + f = f->next; + } + while (f != NULL && f->index < 0); + } + + if (! macro_mri) + idx = sb_skip_comma (idx, in); + else + { + if (in->ptr[idx] == ',') + ++idx; + if (ISWHITE (in->ptr[idx])) + break; + } + } + + if (! err) + { + for (ptr = m->formals; ptr; ptr = ptr->next) + { + if (ptr->type == FORMAL_REQUIRED && ptr->actual.len == 0) + as_bad (_("Missing value for required parameter `%s' of macro `%s'"), + ptr->name.ptr, + m->name); + } + + if (macro_mri) + { + char buffer[20]; + + sb_reset (&t); + sb_add_string (&t, macro_strip_at ? "$NARG" : "NARG"); + ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t)); + sprintf (buffer, "%d", narg); + sb_add_string (&ptr->actual, buffer); + } + + err = macro_expand_body (&m->sub, out, m->formals, m->formal_hash, m); + } + + /* Discard any unnamed formal arguments. */ + if (macro_mri) + { + formal_entry **pf; + + pf = &m->formals; + while (*pf != NULL) + { + if ((*pf)->name.len != 0) + pf = &(*pf)->next; + else + { + f = (*pf)->next; + del_formal (*pf); + *pf = f; + } + } + } + + sb_kill (&t); + if (!err) + macro_number++; + + return err; +} + +/* Check for a macro. If one is found, put the expansion into + *EXPAND. Return 1 if a macro is found, 0 otherwise. */ + +int +check_macro (const char *line, sb *expand, + const char **error, macro_entry **info) +{ + const char *s; + char *copy, *cls; + macro_entry *macro; + sb line_sb; + + if (! is_name_beginner (*line) + && (! macro_mri || *line != '.')) + return 0; + + s = line + 1; + while (is_part_of_name (*s)) + ++s; + if (is_name_ender (*s)) + ++s; + + copy = (char *) alloca (s - line + 1); + memcpy (copy, line, s - line); + copy[s - line] = '\0'; + for (cls = copy; *cls != '\0'; cls ++) + *cls = TOLOWER (*cls); + + macro = (macro_entry *) hash_find (macro_hash, copy); + + if (macro == NULL) + return 0; + + /* Wrap the line up in an sb. */ + sb_new (&line_sb); + while (*s != '\0' && *s != '\n' && *s != '\r') + sb_add_char (&line_sb, *s++); + + sb_new (expand); + *error = macro_expand (0, &line_sb, macro, expand); + + sb_kill (&line_sb); + + /* Export the macro information if requested. */ + if (info) + *info = macro; + + return 1; +} + +/* Delete a macro. */ + +void +delete_macro (const char *name) +{ + char *copy; + size_t i, len; + macro_entry *macro; + + len = strlen (name); + copy = (char *) alloca (len + 1); + for (i = 0; i < len; ++i) + copy[i] = TOLOWER (name[i]); + copy[i] = '\0'; + + /* We can only ask hash_delete to free memory if we are deleting + macros in reverse order to their definition. + So just clear out the entry. */ + if ((macro = (macro_entry *) hash_find (macro_hash, copy)) != NULL) + { + hash_jam (macro_hash, copy, NULL); + free_macro (macro); + } + else + as_warn (_("Attempt to purge non-existant macro `%s'"), copy); +} + +/* Handle the MRI IRP and IRPC pseudo-ops. These are handled as a + combined macro definition and execution. This returns NULL on + success, or an error message otherwise. */ + +const char * +expand_irp (int irpc, size_t idx, sb *in, sb *out, size_t (*get_line) (sb *)) +{ + sb sub; + formal_entry f; + struct hash_control *h; + const char *err; + + idx = sb_skip_white (idx, in); + + sb_new (&sub); + if (! buffer_and_nest (NULL, "ENDR", &sub, get_line)) + return _("unexpected end of file in irp or irpc"); + + sb_new (&f.name); + sb_new (&f.def); + sb_new (&f.actual); + + idx = get_token (idx, in, &f.name); + if (f.name.len == 0) + return _("missing model parameter"); + + h = hash_new (); + err = hash_jam (h, sb_terminate (&f.name), &f); + if (err != NULL) + return err; + + f.index = 1; + f.next = NULL; + f.type = FORMAL_OPTIONAL; + + sb_reset (out); + + idx = sb_skip_comma (idx, in); + if (idx >= in->len) + { + /* Expand once with a null string. */ + err = macro_expand_body (&sub, out, &f, h, 0); + } + else + { + bfd_boolean in_quotes = FALSE; + + if (irpc && in->ptr[idx] == '"') + { + in_quotes = TRUE; + ++idx; + } + + while (idx < in->len) + { + if (!irpc) + idx = get_any_string (idx, in, &f.actual); + else + { + if (in->ptr[idx] == '"') + { + size_t nxt; + + if (irpc) + in_quotes = ! in_quotes; + + nxt = sb_skip_white (idx + 1, in); + if (nxt >= in->len) + { + idx = nxt; + break; + } + } + sb_reset (&f.actual); + sb_add_char (&f.actual, in->ptr[idx]); + ++idx; + } + + err = macro_expand_body (&sub, out, &f, h, 0); + if (err != NULL) + break; + if (!irpc) + idx = sb_skip_comma (idx, in); + else if (! in_quotes) + idx = sb_skip_white (idx, in); + } + } + + hash_die (h); + sb_kill (&f.actual); + sb_kill (&f.def); + sb_kill (&f.name); + sb_kill (&sub); + + return err; +} diff --git a/contrib/toolchain/binutils/gas/macro.h b/contrib/toolchain/binutils/gas/macro.h new file mode 100644 index 0000000000..b109178262 --- /dev/null +++ b/contrib/toolchain/binutils/gas/macro.h @@ -0,0 +1,97 @@ +/* macro.h - header file for macro support for gas + Copyright 1994, 1995, 1996, 1997, 1998, 2000, 2002, 2003, 2004, 2005, 2006, + 2007, 2012 Free Software Foundation, Inc. + + Written by Steve and Judy Chamberlain of Cygnus Support, + sac@cygnus.com + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef MACRO_H + +#define MACRO_H + +/* Structures used to store macros. + + Each macro knows its name and included text. It gets built with a + list of formal arguments, and also keeps a hash table which points + into the list to speed up formal search. Each formal knows its + name and its default value. Each time the macro is expanded, the + formals get the actual values attached to them. */ + +enum formal_type + { + FORMAL_OPTIONAL, + FORMAL_REQUIRED, + FORMAL_VARARG + }; + +/* Describe the formal arguments to a macro. */ + +typedef struct formal_struct { + struct formal_struct *next; /* Next formal in list. */ + sb name; /* Name of the formal. */ + sb def; /* The default value. */ + sb actual; /* The actual argument (changed on each expansion). */ + int index; /* The index of the formal 0..formal_count - 1. */ + enum formal_type type; /* The kind of the formal. */ +} formal_entry; + +/* Other values found in the index field of a formal_entry. */ +#define QUAL_INDEX (-1) +#define NARG_INDEX (-2) +#define LOCAL_INDEX (-3) + +/* Describe the macro. */ + +typedef struct macro_struct +{ + sb sub; /* Substitution text. */ + int formal_count; /* Number of formal args. */ + formal_entry *formals; /* Pointer to list of formal_structs. */ + struct hash_control *formal_hash; /* Hash table of formals. */ + const char *name; /* Macro name. */ + char *file; /* File the macro was defined in. */ + unsigned int line; /* Line number of definition. */ +} macro_entry; + +/* Whether any macros have been defined. */ + +extern int macro_defined; + +/* The macro nesting level. */ + +extern int macro_nest; + +/* The macro hash table. */ + +extern struct hash_control *macro_hash; + +extern int buffer_and_nest (const char *, const char *, sb *, + size_t (*) (sb *)); +extern void macro_init (int, int, int, + size_t (*) (const char *, size_t, sb *, offsetT *)); +extern void macro_set_alternate (int); +extern void macro_mri_mode (int); +extern const char *define_macro (size_t, sb *, sb *, size_t (*) (sb *), + char *, unsigned int, const char **); +extern int check_macro (const char *, sb *, const char **, macro_entry **); +extern void delete_macro (const char *); +extern const char *expand_irp (int, size_t, sb *, sb *, size_t (*) (sb *)); + +#endif diff --git a/contrib/toolchain/binutils/gas/messages.c b/contrib/toolchain/binutils/gas/messages.c new file mode 100644 index 0000000000..e1734f2506 --- /dev/null +++ b/contrib/toolchain/binutils/gas/messages.c @@ -0,0 +1,440 @@ +/* messages.c - error reporter - + Copyright 1987, 1991, 1992, 1993, 1994, 1995, 1996, 1998, 2000, 2001, + 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 + Free Software Foundation, Inc. + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" + +static void identify (char *); +static void as_show_where (void); +static void as_warn_internal (char *, unsigned int, char *); +static void as_bad_internal (char *, unsigned int, char *); + +/* Despite the rest of the comments in this file, (FIXME-SOON), + here is the current scheme for error messages etc: + + as_fatal() is used when gas is quite confused and + continuing the assembly is pointless. In this case we + exit immediately with error status. + + as_bad() is used to mark errors that result in what we + presume to be a useless object file. Say, we ignored + something that might have been vital. If we see any of + these, assembly will continue to the end of the source, + no object file will be produced, and we will terminate + with error status. The new option, -Z, tells us to + produce an object file anyway but we still exit with + error status. The assumption here is that you don't want + this object file but we could be wrong. + + as_warn() is used when we have an error from which we + have a plausible error recovery. eg, masking the top + bits of a constant that is longer than will fit in the + destination. In this case we will continue to assemble + the source, although we may have made a bad assumption, + and we will produce an object file and return normal exit + status (ie, no error). The new option -X tells us to + treat all as_warn() errors as as_bad() errors. That is, + no object file will be produced and we will exit with + error status. The idea here is that we don't kill an + entire make because of an error that we knew how to + correct. On the other hand, sometimes you might want to + stop the make at these points. + + as_tsktsk() is used when we see a minor error for which + our error recovery action is almost certainly correct. + In this case, we print a message and then assembly + continues as though no error occurred. */ + +static void +identify (char *file) +{ + static int identified; + + if (identified) + return; + identified++; + + if (!file) + { + unsigned int x; + as_where (&file, &x); + } + + if (file) + fprintf (stderr, "%s: ", file); + fprintf (stderr, _("Assembler messages:\n")); +} + +/* The number of warnings issued. */ +static int warning_count; + +int +had_warnings (void) +{ + return warning_count; +} + +/* Nonzero if we've hit a 'bad error', and should not write an obj file, + and exit with a nonzero error code. */ + +static int error_count; + +int +had_errors (void) +{ + return error_count; +} + +/* Print the current location to stderr. */ + +static void +as_show_where (void) +{ + char *file; + unsigned int line; + + as_where (&file, &line); + identify (file); + if (file) + { + if (line != 0) + fprintf (stderr, "%s:%u: ", file, line); + else + fprintf (stderr, "%s: ", file); + } +} + +/* Send to stderr a string as a warning, and locate warning + in input file(s). + Please only use this for when we have some recovery action. + Please explain in string (which may have '\n's) what recovery was + done. */ + +void +as_tsktsk (const char *format, ...) +{ + va_list args; + + as_show_where (); + va_start (args, format); + vfprintf (stderr, format, args); + va_end (args); + (void) putc ('\n', stderr); +} + +/* The common portion of as_warn and as_warn_where. */ + +static void +as_warn_internal (char *file, unsigned int line, char *buffer) +{ + ++warning_count; + + if (file == NULL) + as_where (&file, &line); + + identify (file); + if (file) + { + if (line != 0) + fprintf (stderr, "%s:%u: ", file, line); + else + fprintf (stderr, "%s: ", file); + } + fprintf (stderr, _("Warning: ")); + fputs (buffer, stderr); + (void) putc ('\n', stderr); +#ifndef NO_LISTING + listing_warning (buffer); +#endif +} + +/* Send to stderr a string as a warning, and locate warning + in input file(s). + Please only use this for when we have some recovery action. + Please explain in string (which may have '\n's) what recovery was + done. */ + +void +as_warn (const char *format, ...) +{ + va_list args; + char buffer[2000]; + + if (!flag_no_warnings) + { + va_start (args, format); + vsnprintf (buffer, sizeof (buffer), format, args); + va_end (args); + as_warn_internal ((char *) NULL, 0, buffer); + } +} + +/* Like as_bad but the file name and line number are passed in. + Unfortunately, we have to repeat the function in order to handle + the varargs correctly and portably. */ + +void +as_warn_where (char *file, unsigned int line, const char *format, ...) +{ + va_list args; + char buffer[2000]; + + if (!flag_no_warnings) + { + va_start (args, format); + vsnprintf (buffer, sizeof (buffer), format, args); + va_end (args); + as_warn_internal (file, line, buffer); + } +} + +/* The common portion of as_bad and as_bad_where. */ + +static void +as_bad_internal (char *file, unsigned int line, char *buffer) +{ + ++error_count; + + if (file == NULL) + as_where (&file, &line); + + identify (file); + if (file) + { + if (line != 0) + fprintf (stderr, "%s:%u: ", file, line); + else + fprintf (stderr, "%s: ", file); + } + fprintf (stderr, _("Error: ")); + fputs (buffer, stderr); + (void) putc ('\n', stderr); +#ifndef NO_LISTING + listing_error (buffer); +#endif +} + +/* Send to stderr a string as a warning, and locate warning in input + file(s). Please us when there is no recovery, but we want to + continue processing but not produce an object file. + Please explain in string (which may have '\n's) what recovery was + done. */ + +void +as_bad (const char *format, ...) +{ + va_list args; + char buffer[2000]; + + va_start (args, format); + vsnprintf (buffer, sizeof (buffer), format, args); + va_end (args); + + as_bad_internal ((char *) NULL, 0, buffer); +} + +/* Like as_bad but the file name and line number are passed in. + Unfortunately, we have to repeat the function in order to handle + the varargs correctly and portably. */ + +void +as_bad_where (char *file, unsigned int line, const char *format, ...) +{ + va_list args; + char buffer[2000]; + + va_start (args, format); + vsnprintf (buffer, sizeof (buffer), format, args); + va_end (args); + + as_bad_internal (file, line, buffer); +} + +/* Send to stderr a string as a fatal message, and print location of + error in input file(s). + Please only use this for when we DON'T have some recovery action. + It xexit()s with a warning status. */ + +void +as_fatal (const char *format, ...) +{ + va_list args; + + as_show_where (); + va_start (args, format); + fprintf (stderr, _("Fatal error: ")); + vfprintf (stderr, format, args); + (void) putc ('\n', stderr); + va_end (args); + /* Delete the output file, if it exists. This will prevent make from + thinking that a file was created and hence does not need rebuilding. */ + if (out_file_name != NULL) + unlink_if_ordinary (out_file_name); + xexit (EXIT_FAILURE); +} + +/* Indicate assertion failure. + Arguments: Filename, line number, optional function name. */ + +void +as_assert (const char *file, int line, const char *fn) +{ + as_show_where (); + fprintf (stderr, _("Internal error!\n")); + if (fn) + fprintf (stderr, _("Assertion failure in %s at %s line %d.\n"), + fn, file, line); + else + fprintf (stderr, _("Assertion failure at %s line %d.\n"), file, line); + fprintf (stderr, _("Please report this bug.\n")); + xexit (EXIT_FAILURE); +} + +/* as_abort: Print a friendly message saying how totally hosed we are, + and exit without producing a core file. */ + +void +as_abort (const char *file, int line, const char *fn) +{ + as_show_where (); + if (fn) + fprintf (stderr, _("Internal error, aborting at %s line %d in %s\n"), + file, line, fn); + else + fprintf (stderr, _("Internal error, aborting at %s line %d\n"), + file, line); + fprintf (stderr, _("Please report this bug.\n")); + xexit (EXIT_FAILURE); +} + +/* Support routines. */ + +void +sprint_value (char *buf, valueT val) +{ + if (sizeof (val) <= sizeof (long)) + { + sprintf (buf, "%ld", (long) val); + return; + } + if (sizeof (val) <= sizeof (bfd_vma)) + { + sprintf_vma (buf, val); + return; + } + abort (); +} + +#define HEX_MAX_THRESHOLD 1024 +#define HEX_MIN_THRESHOLD -(HEX_MAX_THRESHOLD) + +static void +as_internal_value_out_of_range (char * prefix, + offsetT val, + offsetT min, + offsetT max, + char * file, + unsigned line, + int bad) +{ + const char * err; + + if (prefix == NULL) + prefix = ""; + + if (val >= min && val <= max) + { + addressT right = max & -max; + + if (max <= 1) + abort (); + + /* xgettext:c-format */ + err = _("%s out of domain (%d is not a multiple of %d)"); + if (bad) + as_bad_where (file, line, err, + prefix, (int) val, (int) right); + else + as_warn_where (file, line, err, + prefix, (int) val, (int) right); + return; + } + + if ( val < HEX_MAX_THRESHOLD + && min < HEX_MAX_THRESHOLD + && max < HEX_MAX_THRESHOLD + && val > HEX_MIN_THRESHOLD + && min > HEX_MIN_THRESHOLD + && max > HEX_MIN_THRESHOLD) + { + /* xgettext:c-format */ + err = _("%s out of range (%d is not between %d and %d)"); + + if (bad) + as_bad_where (file, line, err, + prefix, (int) val, (int) min, (int) max); + else + as_warn_where (file, line, err, + prefix, (int) val, (int) min, (int) max); + } + else + { + char val_buf [sizeof (val) * 3 + 2]; + char min_buf [sizeof (val) * 3 + 2]; + char max_buf [sizeof (val) * 3 + 2]; + + if (sizeof (val) > sizeof (bfd_vma)) + abort (); + + sprintf_vma (val_buf, (bfd_vma) val); + sprintf_vma (min_buf, (bfd_vma) min); + sprintf_vma (max_buf, (bfd_vma) max); + + /* xgettext:c-format. */ + err = _("%s out of range (0x%s is not between 0x%s and 0x%s)"); + + if (bad) + as_bad_where (file, line, err, prefix, val_buf, min_buf, max_buf); + else + as_warn_where (file, line, err, prefix, val_buf, min_buf, max_buf); + } +} + +void +as_warn_value_out_of_range (char * prefix, + offsetT value, + offsetT min, + offsetT max, + char * file, + unsigned line) +{ + as_internal_value_out_of_range (prefix, value, min, max, file, line, 0); +} + +void +as_bad_value_out_of_range (char * prefix, + offsetT value, + offsetT min, + offsetT max, + char * file, + unsigned line) +{ + as_internal_value_out_of_range (prefix, value, min, max, file, line, 1); +} diff --git a/contrib/toolchain/binutils/gas/obj-format.h b/contrib/toolchain/binutils/gas/obj-format.h new file mode 100644 index 0000000000..53f9f60e7f --- /dev/null +++ b/contrib/toolchain/binutils/gas/obj-format.h @@ -0,0 +1 @@ +#include "obj-coff.h" diff --git a/contrib/toolchain/binutils/gas/obj.h b/contrib/toolchain/binutils/gas/obj.h new file mode 100644 index 0000000000..c2d4836a62 --- /dev/null +++ b/contrib/toolchain/binutils/gas/obj.h @@ -0,0 +1,87 @@ +/* obj.h - defines the object dependent hooks for all object + format backends. + + Copyright 1987, 1990, 1991, 1992, 1993, 1995, 1996, 1997, 1999, 2000, + 2002, 2003, 2004, 2005, 2007, 2009, 2010 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +char *obj_default_output_file_name (void); +void obj_emit_relocations (char **where, fixS * fixP, + relax_addressT segment_address_in_file); +void obj_emit_strings (char **where); +void obj_emit_symbols (char **where, symbolS * symbols); +#ifndef obj_read_begin_hook +void obj_read_begin_hook (void); +#endif + +#ifndef obj_symbol_new_hook +void obj_symbol_new_hook (symbolS * symbolP); +#endif + +void obj_symbol_to_chars (char **where, symbolS * symbolP); + +extern const pseudo_typeS obj_pseudo_table[]; + +struct format_ops { + int flavor; + unsigned dfl_leading_underscore : 1; + unsigned emit_section_symbols : 1; + void (*begin) (void); + void (*app_file) (const char *, int); + void (*frob_symbol) (symbolS *, int *); + void (*frob_file) (void); + void (*frob_file_before_adjust) (void); + void (*frob_file_before_fix) (void); + void (*frob_file_after_relocs) (void); + bfd_vma (*s_get_size) (symbolS *); + void (*s_set_size) (symbolS *, bfd_vma); + bfd_vma (*s_get_align) (symbolS *); + void (*s_set_align) (symbolS *, bfd_vma); + int (*s_get_other) (symbolS *); + void (*s_set_other) (symbolS *, int); + int (*s_get_desc) (symbolS *); + void (*s_set_desc) (symbolS *, int); + int (*s_get_type) (symbolS *); + void (*s_set_type) (symbolS *, int); + void (*copy_symbol_attributes) (symbolS *, symbolS *); + void (*generate_asm_lineno) (void); + void (*process_stab) (segT, int, const char *, int, int, int); + int (*separate_stab_sections) (void); + void (*init_stab_section) (segT); + int (*sec_sym_ok_for_reloc) (asection *); + void (*pop_insert) (void); + /* For configurations using ECOFF_DEBUGGING, this callback is used. */ + void (*ecoff_set_ext) (symbolS *, struct ecoff_extr *); + + void (*read_begin_hook) (void); + void (*symbol_new_hook) (symbolS *); + void (*symbol_clone_hook) (symbolS *, symbolS *); + void (*adjust_symtab) (void); +}; + +extern const struct format_ops elf_format_ops; +extern const struct format_ops ecoff_format_ops; +extern const struct format_ops coff_format_ops; +extern const struct format_ops aout_format_ops; + +#ifndef this_format +COMMON const struct format_ops *this_format; +#endif + +/* end of obj.h */ diff --git a/contrib/toolchain/binutils/gas/output-file.c b/contrib/toolchain/binutils/gas/output-file.c new file mode 100644 index 0000000000..25bd291d98 --- /dev/null +++ b/contrib/toolchain/binutils/gas/output-file.c @@ -0,0 +1,74 @@ +/* output-file.c - Deal with the output file + Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1996, 1998, 1999, 2001, + 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" +#include "output-file.h" + +#ifndef TARGET_MACH +#define TARGET_MACH 0 +#endif + +bfd *stdoutput; + +void +output_file_create (char *name) +{ + if (name[0] == '-' && name[1] == '\0') + as_fatal (_("can't open a bfd on stdout %s"), name); + + else if (!(stdoutput = bfd_openw (name, TARGET_FORMAT))) + { + bfd_error_type err = bfd_get_error (); + + if (err == bfd_error_invalid_target) + as_fatal (_("selected target format '%s' unknown"), TARGET_FORMAT); + else + as_fatal (_("can't create %s: %s"), name, bfd_errmsg (err)); + } + + bfd_set_format (stdoutput, bfd_object); + bfd_set_arch_mach (stdoutput, TARGET_ARCH, TARGET_MACH); + if (flag_traditional_format) + stdoutput->flags |= BFD_TRADITIONAL_FORMAT; +} + +void +output_file_close (char *filename) +{ + bfd_boolean res; + + if (stdoutput == NULL) + return; + + /* Close the bfd. */ + if (had_errors ()) + res = bfd_cache_close_all (); + else + res = bfd_close (stdoutput); + + /* Prevent an infinite loop - if the close failed we will call as_fatal + which will call xexit() which may call this function again... */ + stdoutput = NULL; + + if (! res) + as_fatal (_("can't close %s: %s"), filename, + bfd_errmsg (bfd_get_error ())); +} diff --git a/contrib/toolchain/binutils/gas/output-file.h b/contrib/toolchain/binutils/gas/output-file.h new file mode 100644 index 0000000000..d276d28e62 --- /dev/null +++ b/contrib/toolchain/binutils/gas/output-file.h @@ -0,0 +1,26 @@ +/* This file is output-file.h + + Copyright 1987, 1988, 1989, 1990, 1991, 1992, 2003, 2005, 2007 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ + +void output_file_append (char *where, long length, char *filename); +void output_file_close (char *filename); +void output_file_create (char *name); + +/* end of output-file.h */ diff --git a/contrib/toolchain/binutils/gas/read.c b/contrib/toolchain/binutils/gas/read.c new file mode 100644 index 0000000000..082670c445 --- /dev/null +++ b/contrib/toolchain/binutils/gas/read.c @@ -0,0 +1,6107 @@ +/* read.c - read a source file - + Copyright 1986, 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, + 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, + 2010, 2011, 2012 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* If your chars aren't 8 bits, you will change this a bit (eg. to 0xFF). + But then, GNU isn't spozed to run on your machine anyway. + (RMS is so shortsighted sometimes.) */ +#define MASK_CHAR ((int)(unsigned char) -1) + +/* This is the largest known floating point format (for now). It will + grow when we do 4361 style flonums. */ +#define MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT (16) + +/* Routines that read assembler source text to build spaghetti in memory. + Another group of these functions is in the expr.c module. */ + +#include "as.h" +#include "safe-ctype.h" +#include "subsegs.h" +#include "sb.h" +#include "macro.h" +#include "obstack.h" +#include "ecoff.h" +#include "dw2gencfi.h" +#include "wchar.h" + +#ifndef TC_START_LABEL +#define TC_START_LABEL(x,y,z) (x == ':') +#endif + +/* Set by the object-format or the target. */ +#ifndef TC_IMPLICIT_LCOMM_ALIGNMENT +#define TC_IMPLICIT_LCOMM_ALIGNMENT(SIZE, P2VAR) \ + do \ + { \ + if ((SIZE) >= 8) \ + (P2VAR) = 3; \ + else if ((SIZE) >= 4) \ + (P2VAR) = 2; \ + else if ((SIZE) >= 2) \ + (P2VAR) = 1; \ + else \ + (P2VAR) = 0; \ + } \ + while (0) +#endif + +char *input_line_pointer; /*->next char of source file to parse. */ + +#if BITS_PER_CHAR != 8 +/* The following table is indexed by[(char)] and will break if + a char does not have exactly 256 states (hopefully 0:255!)! */ +die horribly; +#endif + +#ifndef LEX_AT +#define LEX_AT 0 +#endif + +#ifndef LEX_BR +/* The RS/6000 assembler uses {,},[,] as parts of symbol names. */ +#define LEX_BR 0 +#endif + +#ifndef LEX_PCT +/* The Delta 68k assembler permits % inside label names. */ +#define LEX_PCT 0 +#endif + +#ifndef LEX_QM +/* The PowerPC Windows NT assemblers permits ? inside label names. */ +#define LEX_QM 0 +#endif + +#ifndef LEX_HASH +/* The IA-64 assembler uses # as a suffix designating a symbol. We include + it in the symbol and strip it out in tc_canonicalize_symbol_name. */ +#define LEX_HASH 0 +#endif + +#ifndef LEX_DOLLAR +#define LEX_DOLLAR 3 +#endif + +#ifndef LEX_TILDE +/* The Delta 68k assembler permits ~ at start of label names. */ +#define LEX_TILDE 0 +#endif + +/* Used by is_... macros. our ctype[]. */ +char lex_type[256] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* @ABCDEFGHIJKLMNO */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* PQRSTUVWXYZ[\]^_ */ + 0, 0, 0, LEX_HASH, LEX_DOLLAR, LEX_PCT, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, /* _!"#$%&'()*+,-./ */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, LEX_QM, /* 0123456789:;<=>? */ + LEX_AT, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* @ABCDEFGHIJKLMNO */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, LEX_BR, 0, LEX_BR, 0, 3, /* PQRSTUVWXYZ[\]^_ */ + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* `abcdefghijklmno */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, LEX_BR, 0, LEX_BR, LEX_TILDE, 0, /* pqrstuvwxyz{|}~. */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 +}; + +/* In: a character. + Out: 1 if this character ends a line. + 2 if this character is a line separator. */ +char is_end_of_line[256] = { +#ifdef CR_EOL + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, /* @abcdefghijklmno */ +#else + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* @abcdefghijklmno */ +#endif + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* _!"#$%&'()*+,-./ */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0123456789:;<=>? */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 /* */ +}; + +#ifndef TC_CASE_SENSITIVE +char original_case_string[128]; +#endif + +/* Functions private to this file. */ + +static char *buffer; /* 1st char of each buffer of lines is here. */ +static char *buffer_limit; /*->1 + last char in buffer. */ + +/* TARGET_BYTES_BIG_ENDIAN is required to be defined to either 0 or 1 + in the tc-.h file. See the "Porting GAS" section of the + internals manual. */ +int target_big_endian = TARGET_BYTES_BIG_ENDIAN; + +/* Variables for handling include file directory table. */ + +/* Table of pointers to directories to search for .include's. */ +char **include_dirs; + +/* How many are in the table. */ +int include_dir_count; + +/* Length of longest in table. */ +int include_dir_maxlen = 1; + +#ifndef WORKING_DOT_WORD +struct broken_word *broken_words; +int new_broken_words; +#endif + +/* The current offset into the absolute section. We don't try to + build frags in the absolute section, since no data can be stored + there. We just keep track of the current offset. */ +addressT abs_section_offset; + +/* If this line had an MRI style label, it is stored in this variable. + This is used by some of the MRI pseudo-ops. */ +symbolS *line_label; + +/* This global variable is used to support MRI common sections. We + translate such sections into a common symbol. This variable is + non-NULL when we are in an MRI common section. */ +symbolS *mri_common_symbol; + +/* In MRI mode, after a dc.b pseudo-op with an odd number of bytes, we + need to align to an even byte boundary unless the next pseudo-op is + dc.b, ds.b, or dcb.b. This variable is set to 1 if an alignment + may be needed. */ +static int mri_pending_align; + +#ifndef NO_LISTING +#ifdef OBJ_ELF +/* This variable is set to be non-zero if the next string we see might + be the name of the source file in DWARF debugging information. See + the comment in emit_expr for the format we look for. */ +static int dwarf_file_string; +#endif +#endif + +/* If the target defines the md_frag_max_var hook then we know + enough to implement the .bundle_align_mode features. */ +#ifdef md_frag_max_var +# define HANDLE_BUNDLE +#endif + +#ifdef HANDLE_BUNDLE +/* .bundle_align_mode sets this. Normally it's zero. When nonzero, + it's the exponent of the bundle size, and aligned instruction bundle + mode is in effect. */ +static unsigned int bundle_align_p2; + +/* These are set by .bundle_lock and .bundle_unlock. .bundle_lock sets + bundle_lock_frag to frag_now and then starts a new frag with + frag_align_code. At the same time, bundle_lock_frain gets frchain_now, + so that .bundle_unlock can verify that we didn't change segments. + .bundle_unlock resets both to NULL. If we detect a bundling violation, + then we reset bundle_lock_frchain to NULL as an indicator that we've + already diagnosed the error with as_bad and don't need a cascade of + redundant errors, but bundle_lock_frag remains set to indicate that + we are expecting to see .bundle_unlock. */ +static fragS *bundle_lock_frag; +static frchainS *bundle_lock_frchain; + +/* This is incremented by .bundle_lock and decremented by .bundle_unlock, + to allow nesting. */ +static unsigned int bundle_lock_depth; +#endif + +static void do_s_func (int end_p, const char *default_prefix); +static void do_align (int, char *, int, int); +static void s_align (int, int); +static void s_altmacro (int); +static void s_bad_end (int); +static void s_reloc (int); +static int hex_float (int, char *); +static segT get_known_segmented_expression (expressionS * expP); +static void pobegin (void); +static size_t get_non_macro_line_sb (sb *); +static void generate_file_debug (void); +static char *_find_end_of_line (char *, int, int, int); + +void +read_begin (void) +{ + const char *p; + + pobegin (); + obj_read_begin_hook (); + + /* Something close -- but not too close -- to a multiple of 1024. + The debugging malloc I'm using has 24 bytes of overhead. */ + obstack_begin (¬es, chunksize); + obstack_begin (&cond_obstack, chunksize); + + /* Use machine dependent syntax. */ + for (p = line_separator_chars; *p; p++) + is_end_of_line[(unsigned char) *p] = 2; + /* Use more. FIXME-SOMEDAY. */ + + if (flag_mri) + lex_type['?'] = 3; +} + +#ifndef TC_ADDRESS_BYTES +#define TC_ADDRESS_BYTES address_bytes + +static inline int +address_bytes (void) +{ + /* Choose smallest of 1, 2, 4, 8 bytes that is large enough to + contain an address. */ + int n = (stdoutput->arch_info->bits_per_address - 1) / 8; + n |= n >> 1; + n |= n >> 2; + n += 1; + return n; +} +#endif + +/* Set up pseudo-op tables. */ + +static struct hash_control *po_hash; + +static const pseudo_typeS potable[] = { + {"abort", s_abort, 0}, + {"align", s_align_ptwo, 0}, + {"altmacro", s_altmacro, 1}, + {"ascii", stringer, 8+0}, + {"asciz", stringer, 8+1}, + {"balign", s_align_bytes, 0}, + {"balignw", s_align_bytes, -2}, + {"balignl", s_align_bytes, -4}, +/* block */ +#ifdef HANDLE_BUNDLE + {"bundle_align_mode", s_bundle_align_mode, 0}, + {"bundle_lock", s_bundle_lock, 0}, + {"bundle_unlock", s_bundle_unlock, 0}, +#endif + {"byte", cons, 1}, + {"comm", s_comm, 0}, + {"common", s_mri_common, 0}, + {"common.s", s_mri_common, 1}, + {"data", s_data, 0}, + {"dc", cons, 2}, +#ifdef TC_ADDRESS_BYTES + {"dc.a", cons, 0}, +#endif + {"dc.b", cons, 1}, + {"dc.d", float_cons, 'd'}, + {"dc.l", cons, 4}, + {"dc.s", float_cons, 'f'}, + {"dc.w", cons, 2}, + {"dc.x", float_cons, 'x'}, + {"dcb", s_space, 2}, + {"dcb.b", s_space, 1}, + {"dcb.d", s_float_space, 'd'}, + {"dcb.l", s_space, 4}, + {"dcb.s", s_float_space, 'f'}, + {"dcb.w", s_space, 2}, + {"dcb.x", s_float_space, 'x'}, + {"ds", s_space, 2}, + {"ds.b", s_space, 1}, + {"ds.d", s_space, 8}, + {"ds.l", s_space, 4}, + {"ds.p", s_space, 12}, + {"ds.s", s_space, 4}, + {"ds.w", s_space, 2}, + {"ds.x", s_space, 12}, + {"debug", s_ignore, 0}, +#ifdef S_SET_DESC + {"desc", s_desc, 0}, +#endif +/* dim */ + {"double", float_cons, 'd'}, +/* dsect */ + {"eject", listing_eject, 0}, /* Formfeed listing. */ + {"else", s_else, 0}, + {"elsec", s_else, 0}, + {"elseif", s_elseif, (int) O_ne}, + {"end", s_end, 0}, + {"endc", s_endif, 0}, + {"endfunc", s_func, 1}, + {"endif", s_endif, 0}, + {"endm", s_bad_end, 0}, + {"endr", s_bad_end, 1}, +/* endef */ + {"equ", s_set, 0}, + {"equiv", s_set, 1}, + {"eqv", s_set, -1}, + {"err", s_err, 0}, + {"error", s_errwarn, 1}, + {"exitm", s_mexit, 0}, +/* extend */ + {"extern", s_ignore, 0}, /* We treat all undef as ext. */ + {"appfile", s_app_file, 1}, + {"appline", s_app_line, 1}, + {"fail", s_fail, 0}, + {"file", s_app_file, 0}, + {"fill", s_fill, 0}, + {"float", float_cons, 'f'}, + {"format", s_ignore, 0}, + {"func", s_func, 0}, + {"global", s_globl, 0}, + {"globl", s_globl, 0}, + {"hword", cons, 2}, + {"if", s_if, (int) O_ne}, + {"ifb", s_ifb, 1}, + {"ifc", s_ifc, 0}, + {"ifdef", s_ifdef, 0}, + {"ifeq", s_if, (int) O_eq}, + {"ifeqs", s_ifeqs, 0}, + {"ifge", s_if, (int) O_ge}, + {"ifgt", s_if, (int) O_gt}, + {"ifle", s_if, (int) O_le}, + {"iflt", s_if, (int) O_lt}, + {"ifnb", s_ifb, 0}, + {"ifnc", s_ifc, 1}, + {"ifndef", s_ifdef, 1}, + {"ifne", s_if, (int) O_ne}, + {"ifnes", s_ifeqs, 1}, + {"ifnotdef", s_ifdef, 1}, + {"incbin", s_incbin, 0}, + {"include", s_include, 0}, + {"int", cons, 4}, + {"irp", s_irp, 0}, + {"irep", s_irp, 0}, + {"irpc", s_irp, 1}, + {"irepc", s_irp, 1}, + {"lcomm", s_lcomm, 0}, + {"lflags", s_ignore, 0}, /* Listing flags. */ + {"linefile", s_app_line, 0}, + {"linkonce", s_linkonce, 0}, + {"list", listing_list, 1}, /* Turn listing on. */ + {"llen", listing_psize, 1}, + {"long", cons, 4}, + {"lsym", s_lsym, 0}, + {"macro", s_macro, 0}, + {"mexit", s_mexit, 0}, + {"mri", s_mri, 0}, + {".mri", s_mri, 0}, /* Special case so .mri works in MRI mode. */ + {"name", s_ignore, 0}, + {"noaltmacro", s_altmacro, 0}, + {"noformat", s_ignore, 0}, + {"nolist", listing_list, 0}, /* Turn listing off. */ + {"nopage", listing_nopage, 0}, + {"octa", cons, 16}, + {"offset", s_struct, 0}, + {"org", s_org, 0}, + {"p2align", s_align_ptwo, 0}, + {"p2alignw", s_align_ptwo, -2}, + {"p2alignl", s_align_ptwo, -4}, + {"page", listing_eject, 0}, + {"plen", listing_psize, 0}, + {"print", s_print, 0}, + {"psize", listing_psize, 0}, /* Set paper size. */ + {"purgem", s_purgem, 0}, + {"quad", cons, 8}, + {"reloc", s_reloc, 0}, + {"rep", s_rept, 0}, + {"rept", s_rept, 0}, + {"rva", s_rva, 4}, + {"sbttl", listing_title, 1}, /* Subtitle of listing. */ +/* scl */ +/* sect */ + {"set", s_set, 0}, + {"short", cons, 2}, + {"single", float_cons, 'f'}, +/* size */ + {"space", s_space, 0}, + {"skip", s_space, 0}, + {"sleb128", s_leb128, 1}, + {"spc", s_ignore, 0}, + {"stabd", s_stab, 'd'}, + {"stabn", s_stab, 'n'}, + {"stabs", s_stab, 's'}, + {"string", stringer, 8+1}, + {"string8", stringer, 8+1}, + {"string16", stringer, 16+1}, + {"string32", stringer, 32+1}, + {"string64", stringer, 64+1}, + {"struct", s_struct, 0}, +/* tag */ + {"text", s_text, 0}, + + /* This is for gcc to use. It's only just been added (2/94), so gcc + won't be able to use it for a while -- probably a year or more. + But once this has been released, check with gcc maintainers + before deleting it or even changing the spelling. */ + {"this_GCC_requires_the_GNU_assembler", s_ignore, 0}, + /* If we're folding case -- done for some targets, not necessarily + all -- the above string in an input file will be converted to + this one. Match it either way... */ + {"this_gcc_requires_the_gnu_assembler", s_ignore, 0}, + + {"title", listing_title, 0}, /* Listing title. */ + {"ttl", listing_title, 0}, +/* type */ + {"uleb128", s_leb128, 0}, +/* use */ +/* val */ + {"xcom", s_comm, 0}, + {"xdef", s_globl, 0}, + {"xref", s_ignore, 0}, + {"xstabs", s_xstab, 's'}, + {"warning", s_errwarn, 0}, + {"weakref", s_weakref, 0}, + {"word", cons, 2}, + {"zero", s_space, 0}, + {NULL, NULL, 0} /* End sentinel. */ +}; + +static offsetT +get_absolute_expr (expressionS *exp) +{ + expression_and_evaluate (exp); + if (exp->X_op != O_constant) + { + if (exp->X_op != O_absent) + as_bad (_("bad or irreducible absolute expression")); + exp->X_add_number = 0; + } + return exp->X_add_number; +} + +offsetT +get_absolute_expression (void) +{ + expressionS exp; + + return get_absolute_expr (&exp); +} + +static int pop_override_ok = 0; +static const char *pop_table_name; + +void +pop_insert (const pseudo_typeS *table) +{ + const char *errtxt; + const pseudo_typeS *pop; + for (pop = table; pop->poc_name; pop++) + { + errtxt = hash_insert (po_hash, pop->poc_name, (char *) pop); + if (errtxt && (!pop_override_ok || strcmp (errtxt, "exists"))) + as_fatal (_("error constructing %s pseudo-op table: %s"), pop_table_name, + errtxt); + } +} + +#ifndef md_pop_insert +#define md_pop_insert() pop_insert(md_pseudo_table) +#endif + +#ifndef obj_pop_insert +#define obj_pop_insert() pop_insert(obj_pseudo_table) +#endif + +#ifndef cfi_pop_insert +#define cfi_pop_insert() pop_insert(cfi_pseudo_table) +#endif + +static void +pobegin (void) +{ + po_hash = hash_new (); + + /* Do the target-specific pseudo ops. */ + pop_table_name = "md"; + md_pop_insert (); + + /* Now object specific. Skip any that were in the target table. */ + pop_table_name = "obj"; + pop_override_ok = 1; + obj_pop_insert (); + + /* Now portable ones. Skip any that we've seen already. */ + pop_table_name = "standard"; + pop_insert (potable); + + /* Now CFI ones. */ + pop_table_name = "cfi"; + pop_override_ok = 1; + cfi_pop_insert (); +} + +#define HANDLE_CONDITIONAL_ASSEMBLY() \ + if (ignore_input ()) \ + { \ + char *eol = find_end_of_line (input_line_pointer, flag_m68k_mri); \ + input_line_pointer = (input_line_pointer <= buffer_limit \ + && eol >= buffer_limit) \ + ? buffer_limit \ + : eol + 1; \ + continue; \ + } + +/* This function is used when scrubbing the characters between #APP + and #NO_APP. */ + +static char *scrub_string; +static char *scrub_string_end; + +static size_t +scrub_from_string (char *buf, size_t buflen) +{ + size_t copy; + + copy = scrub_string_end - scrub_string; + if (copy > buflen) + copy = buflen; + memcpy (buf, scrub_string, copy); + scrub_string += copy; + return copy; +} + +/* Helper function of read_a_source_file, which tries to expand a macro. */ +static int +try_macro (char term, const char *line) +{ + sb out; + const char *err; + macro_entry *macro; + + if (check_macro (line, &out, &err, ¯o)) + { + if (err != NULL) + as_bad ("%s", err); + *input_line_pointer++ = term; + input_scrub_include_sb (&out, + input_line_pointer, 1); + sb_kill (&out); + buffer_limit = + input_scrub_next_buffer (&input_line_pointer); +#ifdef md_macro_info + md_macro_info (macro); +#endif + return 1; + } + return 0; +} + +#ifdef HANDLE_BUNDLE +/* Start a new instruction bundle. Returns the rs_align_code frag that + will be used to align the new bundle. */ +static fragS * +start_bundle (void) +{ + fragS *frag = frag_now; + + frag_align_code (0, 0); + + while (frag->fr_type != rs_align_code) + frag = frag->fr_next; + + gas_assert (frag != frag_now); + + return frag; +} + +/* Calculate the maximum size after relaxation of the region starting + at the given frag and extending through frag_now (which is unfinished). */ +static unsigned int +pending_bundle_size (fragS *frag) +{ + unsigned int offset = frag->fr_fix; + unsigned int size = 0; + + gas_assert (frag != frag_now); + gas_assert (frag->fr_type == rs_align_code); + + while (frag != frag_now) + { + /* This should only happen in what will later become an error case. */ + if (frag == NULL) + return 0; + + size += frag->fr_fix; + if (frag->fr_type == rs_machine_dependent) + size += md_frag_max_var (frag); + + frag = frag->fr_next; + } + + gas_assert (frag == frag_now); + size += frag_now_fix (); + if (frag->fr_type == rs_machine_dependent) + size += md_frag_max_var (frag); + + gas_assert (size >= offset); + + return size - offset; +} + +/* Finish off the frag created to ensure bundle alignment. */ +static void +finish_bundle (fragS *frag, unsigned int size) +{ + gas_assert (bundle_align_p2 > 0); + gas_assert (frag->fr_type == rs_align_code); + + if (size > 1) + { + /* If there is more than a single byte, then we need to set up the + alignment frag. Otherwise we leave it at its initial state from + calling frag_align_code (0, 0), so that it does nothing. */ + frag->fr_offset = bundle_align_p2; + frag->fr_subtype = size - 1; + } + + /* We do this every time rather than just in s_bundle_align_mode + so that we catch any affected section without needing hooks all + over for all paths that do section changes. It's cheap enough. */ + record_alignment (now_seg, bundle_align_p2 - OCTETS_PER_BYTE_POWER); +} + +/* Assemble one instruction. This takes care of the bundle features + around calling md_assemble. */ +static void +assemble_one (char *line) +{ + fragS *insn_start_frag = NULL; + + if (bundle_lock_frchain != NULL && bundle_lock_frchain != frchain_now) + { + as_bad (_("cannot change section or subsection inside .bundle_lock")); + /* Clearing this serves as a marker that we have already complained. */ + bundle_lock_frchain = NULL; + } + + if (bundle_lock_frchain == NULL && bundle_align_p2 > 0) + insn_start_frag = start_bundle (); + + md_assemble (line); + + if (bundle_lock_frchain != NULL) + { + /* Make sure this hasn't pushed the locked sequence + past the bundle size. */ + unsigned int bundle_size = pending_bundle_size (bundle_lock_frag); + if (bundle_size > (1U << bundle_align_p2)) + as_bad (_("\ +.bundle_lock sequence at %u bytes but .bundle_align_mode limit is %u bytes"), + bundle_size, 1U << bundle_align_p2); + } + else if (bundle_align_p2 > 0) + { + unsigned int insn_size = pending_bundle_size (insn_start_frag); + + if (insn_size > (1U << bundle_align_p2)) + as_bad (_("\ +single instruction is %u bytes long but .bundle_align_mode limit is %u"), + (unsigned int) insn_size, 1U << bundle_align_p2); + + finish_bundle (insn_start_frag, insn_size); + } +} + +#else /* !HANDLE_BUNDLE */ + +# define assemble_one(line) md_assemble(line) + +#endif /* HANDLE_BUNDLE */ + +/* We read the file, putting things into a web that represents what we + have been reading. */ +void +read_a_source_file (char *name) +{ + char c; + char *s; /* String of symbol, '\0' appended. */ + int temp; + pseudo_typeS *pop; + +#ifdef WARN_COMMENTS + found_comment = 0; +#endif + + buffer = input_scrub_new_file (name); + + listing_file (name); + listing_newline (NULL); + register_dependency (name); + + /* Generate debugging information before we've read anything in to denote + this file as the "main" source file and not a subordinate one + (e.g. N_SO vs N_SOL in stabs). */ + generate_file_debug (); + + while ((buffer_limit = input_scrub_next_buffer (&input_line_pointer)) != 0) + { /* We have another line to parse. */ +#ifndef NO_LISTING + /* In order to avoid listing macro expansion lines with labels + multiple times, keep track of which line was last issued. */ + static char *last_eol; + + last_eol = NULL; +#endif + while (input_line_pointer < buffer_limit) + { + bfd_boolean was_new_line; + /* We have more of this buffer to parse. */ + + /* We now have input_line_pointer->1st char of next line. + If input_line_pointer [-1] == '\n' then we just + scanned another line: so bump line counters. */ + was_new_line = is_end_of_line[(unsigned char) input_line_pointer[-1]]; + if (was_new_line) + { + symbol_set_value_now (&dot_symbol); +#ifdef md_start_line_hook + md_start_line_hook (); +#endif + if (input_line_pointer[-1] == '\n') + bump_line_counters (); + } + +#ifndef NO_LISTING + /* If listing is on, and we are expanding a macro, then give + the listing code the contents of the expanded line. */ + if (listing) + { + if ((listing & LISTING_MACEXP) && macro_nest > 0) + { + /* Find the end of the current expanded macro line. */ + s = find_end_of_line (input_line_pointer, flag_m68k_mri); + + if (s != last_eol) + { + char *copy; + int len; + + last_eol = s; + /* Copy it for safe keeping. Also give an indication of + how much macro nesting is involved at this point. */ + len = s - input_line_pointer; + copy = (char *) xmalloc (len + macro_nest + 2); + memset (copy, '>', macro_nest); + copy[macro_nest] = ' '; + memcpy (copy + macro_nest + 1, input_line_pointer, len); + copy[macro_nest + 1 + len] = '\0'; + + /* Install the line with the listing facility. */ + listing_newline (copy); + } + } + else + listing_newline (NULL); + } +#endif + if (was_new_line) + { + line_label = NULL; + + if (LABELS_WITHOUT_COLONS || flag_m68k_mri) + { + /* Text at the start of a line must be a label, we + run down and stick a colon in. */ + if (is_name_beginner (*input_line_pointer)) + { + char *line_start = input_line_pointer; + int mri_line_macro; + + HANDLE_CONDITIONAL_ASSEMBLY (); + + c = get_symbol_end (); + + /* In MRI mode, the EQU and MACRO pseudoops must + be handled specially. */ + mri_line_macro = 0; + if (flag_m68k_mri) + { + char *rest = input_line_pointer + 1; + + if (*rest == ':') + ++rest; + if (*rest == ' ' || *rest == '\t') + ++rest; + if ((strncasecmp (rest, "EQU", 3) == 0 + || strncasecmp (rest, "SET", 3) == 0) + && (rest[3] == ' ' || rest[3] == '\t')) + { + input_line_pointer = rest + 3; + equals (line_start, + strncasecmp (rest, "SET", 3) == 0); + continue; + } + if (strncasecmp (rest, "MACRO", 5) == 0 + && (rest[5] == ' ' + || rest[5] == '\t' + || is_end_of_line[(unsigned char) rest[5]])) + mri_line_macro = 1; + } + + /* In MRI mode, we need to handle the MACRO + pseudo-op specially: we don't want to put the + symbol in the symbol table. */ + if (!mri_line_macro +#ifdef TC_START_LABEL_WITHOUT_COLON + && TC_START_LABEL_WITHOUT_COLON(c, + input_line_pointer) +#endif + ) + line_label = colon (line_start); + else + line_label = symbol_create (line_start, + absolute_section, + (valueT) 0, + &zero_address_frag); + + *input_line_pointer = c; + if (c == ':') + input_line_pointer++; + } + } + } + + /* We are at the beginning of a line, or similar place. + We expect a well-formed assembler statement. + A "symbol-name:" is a statement. + + Depending on what compiler is used, the order of these tests + may vary to catch most common case 1st. + Each test is independent of all other tests at the (top) + level. */ + do + c = *input_line_pointer++; + while (c == '\t' || c == ' ' || c == '\f'); + + /* C is the 1st significant character. + Input_line_pointer points after that character. */ + if (is_name_beginner (c)) + { + /* Want user-defined label or pseudo/opcode. */ + HANDLE_CONDITIONAL_ASSEMBLY (); + + s = --input_line_pointer; + c = get_symbol_end (); /* name's delimiter. */ + + /* C is character after symbol. + That character's place in the input line is now '\0'. + S points to the beginning of the symbol. + [In case of pseudo-op, s->'.'.] + Input_line_pointer->'\0' where c was. */ + if (TC_START_LABEL (c, s, input_line_pointer)) + { + if (flag_m68k_mri) + { + char *rest = input_line_pointer + 1; + + /* In MRI mode, \tsym: set 0 is permitted. */ + if (*rest == ':') + ++rest; + + if (*rest == ' ' || *rest == '\t') + ++rest; + + if ((strncasecmp (rest, "EQU", 3) == 0 + || strncasecmp (rest, "SET", 3) == 0) + && (rest[3] == ' ' || rest[3] == '\t')) + { + input_line_pointer = rest + 3; + equals (s, 1); + continue; + } + } + + line_label = colon (s); /* User-defined label. */ + /* Put ':' back for error messages' sake. */ + *input_line_pointer++ = ':'; +#ifdef tc_check_label + tc_check_label (line_label); +#endif + /* Input_line_pointer->after ':'. */ + SKIP_WHITESPACE (); + } + else if ((c == '=' && input_line_pointer[1] == '=') + || ((c == ' ' || c == '\t') + && input_line_pointer[1] == '=' + && input_line_pointer[2] == '=')) + { + equals (s, -1); + demand_empty_rest_of_line (); + } + else if ((c == '=' + || ((c == ' ' || c == '\t') + && input_line_pointer[1] == '=')) +#ifdef TC_EQUAL_IN_INSN + && !TC_EQUAL_IN_INSN (c, s) +#endif + ) + { + equals (s, 1); + demand_empty_rest_of_line (); + } + else + { + /* Expect pseudo-op or machine instruction. */ + pop = NULL; + +#ifndef TC_CASE_SENSITIVE + { + char *s2 = s; + + strncpy (original_case_string, s2, sizeof (original_case_string)); + original_case_string[sizeof (original_case_string) - 1] = 0; + + while (*s2) + { + *s2 = TOLOWER (*s2); + s2++; + } + } +#endif + if (NO_PSEUDO_DOT || flag_m68k_mri) + { + /* The MRI assembler uses pseudo-ops without + a period. */ + pop = (pseudo_typeS *) hash_find (po_hash, s); + if (pop != NULL && pop->poc_handler == NULL) + pop = NULL; + } + + if (pop != NULL + || (!flag_m68k_mri && *s == '.')) + { + /* PSEUDO - OP. + + WARNING: c has next char, which may be end-of-line. + We lookup the pseudo-op table with s+1 because we + already know that the pseudo-op begins with a '.'. */ + + if (pop == NULL) + pop = (pseudo_typeS *) hash_find (po_hash, s + 1); + if (pop && !pop->poc_handler) + pop = NULL; + + /* In MRI mode, we may need to insert an + automatic alignment directive. What a hack + this is. */ + if (mri_pending_align + && (pop == NULL + || !((pop->poc_handler == cons + && pop->poc_val == 1) + || (pop->poc_handler == s_space + && pop->poc_val == 1) +#ifdef tc_conditional_pseudoop + || tc_conditional_pseudoop (pop) +#endif + || pop->poc_handler == s_if + || pop->poc_handler == s_ifdef + || pop->poc_handler == s_ifc + || pop->poc_handler == s_ifeqs + || pop->poc_handler == s_else + || pop->poc_handler == s_endif + || pop->poc_handler == s_globl + || pop->poc_handler == s_ignore))) + { + do_align (1, (char *) NULL, 0, 0); + mri_pending_align = 0; + + if (line_label != NULL) + { + symbol_set_frag (line_label, frag_now); + S_SET_VALUE (line_label, frag_now_fix ()); + } + } + + /* Print the error msg now, while we still can. */ + if (pop == NULL) + { + char *end = input_line_pointer; + + *input_line_pointer = c; + s_ignore (0); + c = *--input_line_pointer; + *input_line_pointer = '\0'; + if (! macro_defined || ! try_macro (c, s)) + { + *end = '\0'; + as_bad (_("unknown pseudo-op: `%s'"), s); + *input_line_pointer++ = c; + } + continue; + } + + /* Put it back for error messages etc. */ + *input_line_pointer = c; + /* The following skip of whitespace is compulsory. + A well shaped space is sometimes all that separates + keyword from operands. */ + if (c == ' ' || c == '\t') + input_line_pointer++; + + /* Input_line is restored. + Input_line_pointer->1st non-blank char + after pseudo-operation. */ + (*pop->poc_handler) (pop->poc_val); + + /* If that was .end, just get out now. */ + if (pop->poc_handler == s_end) + goto quit; + } + else + { + /* WARNING: c has char, which may be end-of-line. */ + /* Also: input_line_pointer->`\0` where c was. */ + *input_line_pointer = c; + input_line_pointer = _find_end_of_line (input_line_pointer, flag_m68k_mri, 1, 0); + c = *input_line_pointer; + *input_line_pointer = '\0'; + + generate_lineno_debug (); + + if (macro_defined && try_macro (c, s)) + continue; + + if (mri_pending_align) + { + do_align (1, (char *) NULL, 0, 0); + mri_pending_align = 0; + if (line_label != NULL) + { + symbol_set_frag (line_label, frag_now); + S_SET_VALUE (line_label, frag_now_fix ()); + } + } + + assemble_one (s); /* Assemble 1 instruction. */ + + *input_line_pointer++ = c; + + /* We resume loop AFTER the end-of-line from + this instruction. */ + } + } + continue; + } + + /* Empty statement? */ + if (is_end_of_line[(unsigned char) c]) + continue; + + if ((LOCAL_LABELS_DOLLAR || LOCAL_LABELS_FB) && ISDIGIT (c)) + { + /* local label ("4:") */ + char *backup = input_line_pointer; + + HANDLE_CONDITIONAL_ASSEMBLY (); + + temp = c - '0'; + + /* Read the whole number. */ + while (ISDIGIT (*input_line_pointer)) + { + temp = (temp * 10) + *input_line_pointer - '0'; + ++input_line_pointer; + } + + if (LOCAL_LABELS_DOLLAR + && *input_line_pointer == '$' + && *(input_line_pointer + 1) == ':') + { + input_line_pointer += 2; + + if (dollar_label_defined (temp)) + { + as_fatal (_("label \"%d$\" redefined"), temp); + } + + define_dollar_label (temp); + colon (dollar_label_name (temp, 0)); + continue; + } + + if (LOCAL_LABELS_FB + && *input_line_pointer++ == ':') + { + fb_label_instance_inc (temp); + colon (fb_label_name (temp, 0)); + continue; + } + + input_line_pointer = backup; + } /* local label ("4:") */ + + if (c && strchr (line_comment_chars, c)) + { /* Its a comment. Better say APP or NO_APP. */ + sb sbuf; + char *ends; + char *new_buf; + char *new_tmp; + unsigned int new_length; + char *tmp_buf = 0; + + s = input_line_pointer; + if (strncmp (s, "APP\n", 4)) + { + /* We ignore it. */ + ignore_rest_of_line (); + continue; + } + bump_line_counters (); + s += 4; + + ends = strstr (s, "#NO_APP\n"); + + if (!ends) + { + unsigned int tmp_len; + unsigned int num; + + /* The end of the #APP wasn't in this buffer. We + keep reading in buffers until we find the #NO_APP + that goes with this #APP There is one. The specs + guarantee it... */ + tmp_len = buffer_limit - s; + tmp_buf = (char *) xmalloc (tmp_len + 1); + memcpy (tmp_buf, s, tmp_len); + do + { + new_tmp = input_scrub_next_buffer (&buffer); + if (!new_tmp) + break; + else + buffer_limit = new_tmp; + input_line_pointer = buffer; + ends = strstr (buffer, "#NO_APP\n"); + if (ends) + num = ends - buffer; + else + num = buffer_limit - buffer; + + tmp_buf = (char *) xrealloc (tmp_buf, tmp_len + num); + memcpy (tmp_buf + tmp_len, buffer, num); + tmp_len += num; + } + while (!ends); + + input_line_pointer = ends ? ends + 8 : NULL; + + s = tmp_buf; + ends = s + tmp_len; + + } + else + { + input_line_pointer = ends + 8; + } + + scrub_string = s; + scrub_string_end = ends; + + new_length = ends - s; + new_buf = (char *) xmalloc (new_length); + new_tmp = new_buf; + for (;;) + { + size_t space; + size_t size; + + space = (new_buf + new_length) - new_tmp; + size = do_scrub_chars (scrub_from_string, new_tmp, space); + + if (size < space) + { + new_tmp[size] = 0; + break; + } + + new_buf = (char *) xrealloc (new_buf, new_length + 100); + new_tmp = new_buf + new_length; + new_length += 100; + } + + if (tmp_buf) + free (tmp_buf); + + /* We've "scrubbed" input to the preferred format. In the + process we may have consumed the whole of the remaining + file (and included files). We handle this formatted + input similar to that of macro expansion, letting + actual macro expansion (possibly nested) and other + input expansion work. Beware that in messages, line + numbers and possibly file names will be incorrect. */ + new_length = strlen (new_buf); + sb_build (&sbuf, new_length); + sb_add_buffer (&sbuf, new_buf, new_length); + input_scrub_include_sb (&sbuf, input_line_pointer, 0); + sb_kill (&sbuf); + buffer_limit = input_scrub_next_buffer (&input_line_pointer); + free (new_buf); + continue; + } + + HANDLE_CONDITIONAL_ASSEMBLY (); + +#ifdef tc_unrecognized_line + if (tc_unrecognized_line (c)) + continue; +#endif + input_line_pointer--; + /* Report unknown char as error. */ + demand_empty_rest_of_line (); + } + } + + quit: + symbol_set_value_now (&dot_symbol); + +#ifdef HANDLE_BUNDLE + if (bundle_lock_frag != NULL) + { + as_bad_where (bundle_lock_frag->fr_file, bundle_lock_frag->fr_line, + _(".bundle_lock with no matching .bundle_unlock")); + bundle_lock_frag = NULL; + bundle_lock_frchain = NULL; + bundle_lock_depth = 0; + } +#endif + +#ifdef md_cleanup + md_cleanup (); +#endif + /* Close the input file. */ + input_scrub_close (); +#ifdef WARN_COMMENTS + { + if (warn_comment && found_comment) + as_warn_where (found_comment_file, found_comment, + "first comment found here"); + } +#endif +} + +/* Convert O_constant expression EXP into the equivalent O_big representation. + Take the sign of the number from SIGN rather than X_add_number. */ + +static void +convert_to_bignum (expressionS *exp, int sign) +{ + valueT value; + unsigned int i; + + value = exp->X_add_number; + for (i = 0; i < sizeof (exp->X_add_number) / CHARS_PER_LITTLENUM; i++) + { + generic_bignum[i] = value & LITTLENUM_MASK; + value >>= LITTLENUM_NUMBER_OF_BITS; + } + /* Add a sequence of sign bits if the top bit of X_add_number is not + the sign of the original value. */ + if ((exp->X_add_number < 0) == !sign) + generic_bignum[i++] = sign ? LITTLENUM_MASK : 0; + exp->X_op = O_big; + exp->X_add_number = i; +} + +/* For most MRI pseudo-ops, the line actually ends at the first + nonquoted space. This function looks for that point, stuffs a null + in, and sets *STOPCP to the character that used to be there, and + returns the location. + + Until I hear otherwise, I am going to assume that this is only true + for the m68k MRI assembler. */ + +char * +mri_comment_field (char *stopcp) +{ + char *s; +#ifdef TC_M68K + int inquote = 0; + + know (flag_m68k_mri); + + for (s = input_line_pointer; + ((!is_end_of_line[(unsigned char) *s] && *s != ' ' && *s != '\t') + || inquote); + s++) + { + if (*s == '\'') + inquote = !inquote; + } +#else + for (s = input_line_pointer; + !is_end_of_line[(unsigned char) *s]; + s++) + ; +#endif + *stopcp = *s; + *s = '\0'; + + return s; +} + +/* Skip to the end of an MRI comment field. */ + +void +mri_comment_end (char *stop, int stopc) +{ + know (flag_mri); + + input_line_pointer = stop; + *stop = stopc; + while (!is_end_of_line[(unsigned char) *input_line_pointer]) + ++input_line_pointer; +} + +void +s_abort (int ignore ATTRIBUTE_UNUSED) +{ + as_fatal (_(".abort detected. Abandoning ship.")); +} + +/* Guts of .align directive. N is the power of two to which to align. + FILL may be NULL, or it may point to the bytes of the fill pattern. + LEN is the length of whatever FILL points to, if anything. MAX is + the maximum number of characters to skip when doing the alignment, + or 0 if there is no maximum. */ + +static void +do_align (int n, char *fill, int len, int max) +{ + if (now_seg == absolute_section) + { + if (fill != NULL) + while (len-- > 0) + if (*fill++ != '\0') + { + as_warn (_("ignoring fill value in absolute section")); + break; + } + fill = NULL; + len = 0; + } + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif +#ifdef md_do_align + md_do_align (n, fill, len, max, just_record_alignment); +#endif + + /* Only make a frag if we HAVE to... */ + if (n != 0 && !need_pass_2) + { + if (fill == NULL) + { + if (subseg_text_p (now_seg)) + frag_align_code (n, max); + else + frag_align (n, 0, max); + } + else if (len <= 1) + frag_align (n, *fill, max); + else + frag_align_pattern (n, fill, len, max); + } + +#ifdef md_do_align + just_record_alignment: ATTRIBUTE_UNUSED_LABEL +#endif + + record_alignment (now_seg, n - OCTETS_PER_BYTE_POWER); +} + +/* Handle the .align pseudo-op. A positive ARG is a default alignment + (in bytes). A negative ARG is the negative of the length of the + fill pattern. BYTES_P is non-zero if the alignment value should be + interpreted as the byte boundary, rather than the power of 2. */ +#ifndef TC_ALIGN_LIMIT +#define TC_ALIGN_LIMIT (stdoutput->arch_info->bits_per_address - 1) +#endif + +static void +s_align (int arg, int bytes_p) +{ + unsigned int align_limit = TC_ALIGN_LIMIT; + unsigned int align; + char *stop = NULL; + char stopc = 0; + offsetT fill = 0; + int max; + int fill_p; + + if (flag_mri) + stop = mri_comment_field (&stopc); + + if (is_end_of_line[(unsigned char) *input_line_pointer]) + { + if (arg < 0) + align = 0; + else + align = arg; /* Default value from pseudo-op table. */ + } + else + { + align = get_absolute_expression (); + SKIP_WHITESPACE (); + } + + if (bytes_p) + { + /* Convert to a power of 2. */ + if (align != 0) + { + unsigned int i; + + for (i = 0; (align & 1) == 0; align >>= 1, ++i) + ; + if (align != 1) + as_bad (_("alignment not a power of 2")); + + align = i; + } + } + + if (align > align_limit) + { + align = align_limit; + as_warn (_("alignment too large: %u assumed"), align); + } + + if (*input_line_pointer != ',') + { + fill_p = 0; + max = 0; + } + else + { + ++input_line_pointer; + if (*input_line_pointer == ',') + fill_p = 0; + else + { + fill = get_absolute_expression (); + SKIP_WHITESPACE (); + fill_p = 1; + } + + if (*input_line_pointer != ',') + max = 0; + else + { + ++input_line_pointer; + max = get_absolute_expression (); + } + } + + if (!fill_p) + { + if (arg < 0) + as_warn (_("expected fill pattern missing")); + do_align (align, (char *) NULL, 0, max); + } + else + { + int fill_len; + + if (arg >= 0) + fill_len = 1; + else + fill_len = -arg; + if (fill_len <= 1) + { + char fill_char; + + fill_char = fill; + do_align (align, &fill_char, fill_len, max); + } + else + { + char ab[16]; + + if ((size_t) fill_len > sizeof ab) + abort (); + md_number_to_chars (ab, fill, fill_len); + do_align (align, ab, fill_len, max); + } + } + + demand_empty_rest_of_line (); + + if (flag_mri) + mri_comment_end (stop, stopc); +} + +/* Handle the .align pseudo-op on machines where ".align 4" means + align to a 4 byte boundary. */ + +void +s_align_bytes (int arg) +{ + s_align (arg, 1); +} + +/* Handle the .align pseudo-op on machines where ".align 4" means align + to a 2**4 boundary. */ + +void +s_align_ptwo (int arg) +{ + s_align (arg, 0); +} + +/* Switch in and out of alternate macro mode. */ + +void +s_altmacro (int on) +{ + demand_empty_rest_of_line (); + macro_set_alternate (on); +} + +/* Read a symbol name from input_line_pointer. + + Stores the symbol name in a buffer and returns a pointer to this buffer. + The buffer is xalloc'ed. It is the caller's responsibility to free + this buffer. + + The name is not left in the i_l_p buffer as it may need processing + to handle escape characters. + + Advances i_l_p to the next non-whitespace character. + + If a symbol name could not be read, the routine issues an error + messages, skips to the end of the line and returns NULL. */ + +static char * +read_symbol_name (void) +{ + char * name; + char * start; + char c; + + c = *input_line_pointer++; + + if (c == '"') + { +#define SYM_NAME_CHUNK_LEN 128 + ptrdiff_t len = SYM_NAME_CHUNK_LEN; + char * name_end; + unsigned int C; + + start = name = xmalloc (len + 1); + + name_end = name + SYM_NAME_CHUNK_LEN; + + while (is_a_char (C = next_char_of_string ())) + { + if (name >= name_end) + { + ptrdiff_t sofar; + + sofar = name - start; + len += SYM_NAME_CHUNK_LEN; + start = xrealloc (start, len + 1); + name_end = start + len; + name = start + sofar; + } + + *name++ = (char) C; + } + *name = 0; + + /* Since quoted symbol names can contain non-ASCII characters, + check the string and warn if it cannot be recognised by the + current character set. */ + if (mbstowcs (NULL, name, len) == (size_t) -1) + as_warn (_("symbol name not recognised in the current locale")); + } + else if (is_name_beginner (c) || c == '\001') + { + ptrdiff_t len; + + name = input_line_pointer - 1; + + /* We accept \001 in a name in case this is + being called with a constructed string. */ + while (is_part_of_name (c = *input_line_pointer++) + || c == '\001') + ; + + len = (input_line_pointer - name) - 1; + start = xmalloc (len + 1); + + memcpy (start, name, len); + start[len] = 0; + + /* Skip a name ender char if one is present. */ + if (! is_name_ender (c)) + --input_line_pointer; + } + else + name = start = NULL; + + if (name == start) + { + as_bad (_("expected symbol name")); + ignore_rest_of_line (); + return NULL; + } + + SKIP_WHITESPACE (); + + return start; +} + + +symbolS * +s_comm_internal (int param, + symbolS *(*comm_parse_extra) (int, symbolS *, addressT)) +{ + char *name; + offsetT temp, size; + symbolS *symbolP = NULL; + char *stop = NULL; + char stopc = 0; + expressionS exp; + + if (flag_mri) + stop = mri_comment_field (&stopc); + + if ((name = read_symbol_name ()) == NULL) + goto out; + + /* Accept an optional comma after the name. The comma used to be + required, but Irix 5 cc does not generate it for .lcomm. */ + if (*input_line_pointer == ',') + input_line_pointer++; + + temp = get_absolute_expr (&exp); + size = temp; + size &= ((offsetT) 2 << (stdoutput->arch_info->bits_per_address - 1)) - 1; + if (exp.X_op == O_absent) + { + as_bad (_("missing size expression")); + ignore_rest_of_line (); + goto out; + } + else if (temp != size || !exp.X_unsigned) + { + as_warn (_("size (%ld) out of range, ignored"), (long) temp); + ignore_rest_of_line (); + goto out; + } + + symbolP = symbol_find_or_make (name); + if ((S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP)) + && !S_IS_COMMON (symbolP)) + { + if (!S_IS_VOLATILE (symbolP)) + { + symbolP = NULL; + as_bad (_("symbol `%s' is already defined"), name); + ignore_rest_of_line (); + goto out; + } + symbolP = symbol_clone (symbolP, 1); + S_SET_SEGMENT (symbolP, undefined_section); + S_SET_VALUE (symbolP, 0); + symbol_set_frag (symbolP, &zero_address_frag); + S_CLEAR_VOLATILE (symbolP); + } + + size = S_GET_VALUE (symbolP); + if (size == 0) + size = temp; + else if (size != temp) + as_warn (_("size of \"%s\" is already %ld; not changing to %ld"), + name, (long) size, (long) temp); + + if (comm_parse_extra != NULL) + symbolP = (*comm_parse_extra) (param, symbolP, size); + else + { + S_SET_VALUE (symbolP, (valueT) size); + S_SET_EXTERNAL (symbolP); + S_SET_SEGMENT (symbolP, bfd_com_section_ptr); + } + + demand_empty_rest_of_line (); + out: + if (flag_mri) + mri_comment_end (stop, stopc); + if (name != NULL) + free (name); + return symbolP; +} + +void +s_comm (int ignore) +{ + s_comm_internal (ignore, NULL); +} + +/* The MRI COMMON pseudo-op. We handle this by creating a common + symbol with the appropriate name. We make s_space do the right + thing by increasing the size. */ + +void +s_mri_common (int small ATTRIBUTE_UNUSED) +{ + char *name; + char c; + char *alc = NULL; + symbolS *sym; + offsetT align; + char *stop = NULL; + char stopc = 0; + + if (!flag_mri) + { + s_comm (0); + return; + } + + stop = mri_comment_field (&stopc); + + SKIP_WHITESPACE (); + + name = input_line_pointer; + if (!ISDIGIT (*name)) + c = get_symbol_end (); + else + { + do + { + ++input_line_pointer; + } + while (ISDIGIT (*input_line_pointer)); + + c = *input_line_pointer; + *input_line_pointer = '\0'; + + if (line_label != NULL) + { + alc = (char *) xmalloc (strlen (S_GET_NAME (line_label)) + + (input_line_pointer - name) + + 1); + sprintf (alc, "%s%s", name, S_GET_NAME (line_label)); + name = alc; + } + } + + sym = symbol_find_or_make (name); + *input_line_pointer = c; + if (alc != NULL) + free (alc); + + if (*input_line_pointer != ',') + align = 0; + else + { + ++input_line_pointer; + align = get_absolute_expression (); + } + + if (S_IS_DEFINED (sym) && !S_IS_COMMON (sym)) + { + as_bad (_("symbol `%s' is already defined"), S_GET_NAME (sym)); + ignore_rest_of_line (); + mri_comment_end (stop, stopc); + return; + } + + S_SET_EXTERNAL (sym); + S_SET_SEGMENT (sym, bfd_com_section_ptr); + mri_common_symbol = sym; + +#ifdef S_SET_ALIGN + if (align != 0) + S_SET_ALIGN (sym, align); +#else + (void) align; +#endif + + if (line_label != NULL) + { + expressionS exp; + exp.X_op = O_symbol; + exp.X_add_symbol = sym; + exp.X_add_number = 0; + symbol_set_value_expression (line_label, &exp); + symbol_set_frag (line_label, &zero_address_frag); + S_SET_SEGMENT (line_label, expr_section); + } + + /* FIXME: We just ignore the small argument, which distinguishes + COMMON and COMMON.S. I don't know what we can do about it. */ + + /* Ignore the type and hptype. */ + if (*input_line_pointer == ',') + input_line_pointer += 2; + if (*input_line_pointer == ',') + input_line_pointer += 2; + + demand_empty_rest_of_line (); + + mri_comment_end (stop, stopc); +} + +void +s_data (int ignore ATTRIBUTE_UNUSED) +{ + segT section; + int temp; + + temp = get_absolute_expression (); + if (flag_readonly_data_in_text) + { + section = text_section; + temp += 1000; + } + else + section = data_section; + + subseg_set (section, (subsegT) temp); + + demand_empty_rest_of_line (); +} + +/* Handle the .appfile pseudo-op. This is automatically generated by + do_scrub_chars when a preprocessor # line comment is seen with a + file name. This default definition may be overridden by the object + or CPU specific pseudo-ops. This function is also the default + definition for .file; the APPFILE argument is 1 for .appfile, 0 for + .file. */ + +void +s_app_file_string (char *file, int appfile ATTRIBUTE_UNUSED) +{ +#ifdef LISTING + if (listing) + listing_source_file (file); +#endif + register_dependency (file); +#ifdef obj_app_file + obj_app_file (file, appfile); +#endif +} + +void +s_app_file (int appfile) +{ + char *s; + int length; + + /* Some assemblers tolerate immediately following '"'. */ + if ((s = demand_copy_string (&length)) != 0) + { + int may_omit + = (!new_logical_line_flags (s, -1, 1) && appfile); + + /* In MRI mode, the preprocessor may have inserted an extraneous + backquote. */ + if (flag_m68k_mri + && *input_line_pointer == '\'' + && is_end_of_line[(unsigned char) input_line_pointer[1]]) + ++input_line_pointer; + + demand_empty_rest_of_line (); + if (!may_omit) + s_app_file_string (s, appfile); + } +} + +static int +get_linefile_number (int *flag) +{ + SKIP_WHITESPACE (); + + if (*input_line_pointer < '0' || *input_line_pointer > '9') + return 0; + + *flag = get_absolute_expression (); + + return 1; +} + +/* Handle the .appline pseudo-op. This is automatically generated by + do_scrub_chars when a preprocessor # line comment is seen. This + default definition may be overridden by the object or CPU specific + pseudo-ops. */ + +void +s_app_line (int appline) +{ + char *file = NULL; + int l; + + /* The given number is that of the next line. */ + if (appline) + l = get_absolute_expression (); + else if (!get_linefile_number (&l)) + { + ignore_rest_of_line (); + return; + } + + l--; + + if (l < -1) + /* Some of the back ends can't deal with non-positive line numbers. + Besides, it's silly. GCC however will generate a line number of + zero when it is pre-processing builtins for assembler-with-cpp files: + + # 0 "" + + We do not want to barf on this, especially since such files are used + in the GCC and GDB testsuites. So we check for negative line numbers + rather than non-positive line numbers. */ + as_warn (_("line numbers must be positive; line number %d rejected"), + l + 1); + else + { + int flags = 0; + int length = 0; + + if (!appline) + { + SKIP_WHITESPACE (); + + if (*input_line_pointer == '"') + file = demand_copy_string (&length); + + if (file) + { + int this_flag; + + while (get_linefile_number (&this_flag)) + switch (this_flag) + { + /* From GCC's cpp documentation: + 1: start of a new file. + 2: returning to a file after having included + another file. + 3: following text comes from a system header file. + 4: following text should be treated as extern "C". + + 4 is nonsensical for the assembler; 3, we don't + care about, so we ignore it just in case a + system header file is included while + preprocessing assembly. So 1 and 2 are all we + care about, and they are mutually incompatible. + new_logical_line_flags() demands this. */ + case 1: + case 2: + if (flags && flags != (1 << this_flag)) + as_warn (_("incompatible flag %i in line directive"), + this_flag); + else + flags |= 1 << this_flag; + break; + + case 3: + case 4: + /* We ignore these. */ + break; + + default: + as_warn (_("unsupported flag %i in line directive"), + this_flag); + break; + } + + if (!is_end_of_line[(unsigned char)*input_line_pointer]) + file = 0; + } + } + + if (appline || file) + { + new_logical_line_flags (file, l, flags); +#ifdef LISTING + if (listing) + listing_source_line (l); +#endif + } + } + if (appline || file) + demand_empty_rest_of_line (); + else + ignore_rest_of_line (); +} + +/* Handle the .end pseudo-op. Actually, the real work is done in + read_a_source_file. */ + +void +s_end (int ignore ATTRIBUTE_UNUSED) +{ + if (flag_mri) + { + /* The MRI assembler permits the start symbol to follow .end, + but we don't support that. */ + SKIP_WHITESPACE (); + if (!is_end_of_line[(unsigned char) *input_line_pointer] + && *input_line_pointer != '*' + && *input_line_pointer != '!') + as_warn (_("start address not supported")); + } +} + +/* Handle the .err pseudo-op. */ + +void +s_err (int ignore ATTRIBUTE_UNUSED) +{ + as_bad (_(".err encountered")); + demand_empty_rest_of_line (); +} + +/* Handle the .error and .warning pseudo-ops. */ + +void +s_errwarn (int err) +{ + int len; + /* The purpose for the conditional assignment is not to + internationalize the directive itself, but that we need a + self-contained message, one that can be passed like the + demand_copy_C_string return value, and with no assumption on the + location of the name of the directive within the message. */ + char *msg + = (err ? _(".error directive invoked in source file") + : _(".warning directive invoked in source file")); + + if (!is_it_end_of_statement ()) + { + if (*input_line_pointer != '\"') + { + as_bad (_("%s argument must be a string"), + err ? ".error" : ".warning"); + ignore_rest_of_line (); + return; + } + + msg = demand_copy_C_string (&len); + if (msg == NULL) + return; + } + + if (err) + as_bad ("%s", msg); + else + as_warn ("%s", msg); + demand_empty_rest_of_line (); +} + +/* Handle the MRI fail pseudo-op. */ + +void +s_fail (int ignore ATTRIBUTE_UNUSED) +{ + offsetT temp; + char *stop = NULL; + char stopc = 0; + + if (flag_mri) + stop = mri_comment_field (&stopc); + + temp = get_absolute_expression (); + if (temp >= 500) + as_warn (_(".fail %ld encountered"), (long) temp); + else + as_bad (_(".fail %ld encountered"), (long) temp); + + demand_empty_rest_of_line (); + + if (flag_mri) + mri_comment_end (stop, stopc); +} + +void +s_fill (int ignore ATTRIBUTE_UNUSED) +{ + expressionS rep_exp; + long size = 1; + long fill = 0; + char *p; + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + +#ifdef md_cons_align + md_cons_align (1); +#endif + + get_known_segmented_expression (&rep_exp); + if (*input_line_pointer == ',') + { + input_line_pointer++; + size = get_absolute_expression (); + if (*input_line_pointer == ',') + { + input_line_pointer++; + fill = get_absolute_expression (); + } + } + + /* This is to be compatible with BSD 4.2 AS, not for any rational reason. */ +#define BSD_FILL_SIZE_CROCK_8 (8) + if (size > BSD_FILL_SIZE_CROCK_8) + { + as_warn (_(".fill size clamped to %d"), BSD_FILL_SIZE_CROCK_8); + size = BSD_FILL_SIZE_CROCK_8; + } + if (size < 0) + { + as_warn (_("size negative; .fill ignored")); + size = 0; + } + else if (rep_exp.X_op == O_constant && rep_exp.X_add_number <= 0) + { + if (rep_exp.X_add_number < 0) + as_warn (_("repeat < 0; .fill ignored")); + size = 0; + } + + if (size && !need_pass_2) + { + if (rep_exp.X_op == O_constant) + { + p = frag_var (rs_fill, (int) size, (int) size, + (relax_substateT) 0, (symbolS *) 0, + (offsetT) rep_exp.X_add_number, + (char *) 0); + } + else + { + /* We don't have a constant repeat count, so we can't use + rs_fill. We can get the same results out of rs_space, + but its argument is in bytes, so we must multiply the + repeat count by size. */ + + symbolS *rep_sym; + rep_sym = make_expr_symbol (&rep_exp); + if (size != 1) + { + expressionS size_exp; + size_exp.X_op = O_constant; + size_exp.X_add_number = size; + + rep_exp.X_op = O_multiply; + rep_exp.X_add_symbol = rep_sym; + rep_exp.X_op_symbol = make_expr_symbol (&size_exp); + rep_exp.X_add_number = 0; + rep_sym = make_expr_symbol (&rep_exp); + } + + p = frag_var (rs_space, (int) size, (int) size, + (relax_substateT) 0, rep_sym, (offsetT) 0, (char *) 0); + } + + memset (p, 0, (unsigned int) size); + + /* The magic number BSD_FILL_SIZE_CROCK_4 is from BSD 4.2 VAX + flavoured AS. The following bizarre behaviour is to be + compatible with above. I guess they tried to take up to 8 + bytes from a 4-byte expression and they forgot to sign + extend. */ +#define BSD_FILL_SIZE_CROCK_4 (4) + md_number_to_chars (p, (valueT) fill, + (size > BSD_FILL_SIZE_CROCK_4 + ? BSD_FILL_SIZE_CROCK_4 + : (int) size)); + /* Note: .fill (),0 emits no frag (since we are asked to .fill 0 bytes) + but emits no error message because it seems a legal thing to do. + It is a degenerate case of .fill but could be emitted by a + compiler. */ + } + demand_empty_rest_of_line (); +} + +void +s_globl (int ignore ATTRIBUTE_UNUSED) +{ + char *name; + int c; + symbolS *symbolP; + char *stop = NULL; + char stopc = 0; + + if (flag_mri) + stop = mri_comment_field (&stopc); + + do + { + if ((name = read_symbol_name ()) == NULL) + return; + + symbolP = symbol_find_or_make (name); + S_SET_EXTERNAL (symbolP); + + SKIP_WHITESPACE (); + c = *input_line_pointer; + if (c == ',') + { + input_line_pointer++; + SKIP_WHITESPACE (); + if (is_end_of_line[(unsigned char) *input_line_pointer]) + c = '\n'; + } + + free (name); + } + while (c == ','); + + demand_empty_rest_of_line (); + + if (flag_mri) + mri_comment_end (stop, stopc); +} + +/* Handle the MRI IRP and IRPC pseudo-ops. */ + +void +s_irp (int irpc) +{ + char *file, *eol; + unsigned int line; + sb s; + const char *err; + sb out; + + as_where (&file, &line); + + eol = find_end_of_line (input_line_pointer, 0); + sb_build (&s, eol - input_line_pointer); + sb_add_buffer (&s, input_line_pointer, eol - input_line_pointer); + input_line_pointer = eol; + + sb_new (&out); + + err = expand_irp (irpc, 0, &s, &out, get_non_macro_line_sb); + if (err != NULL) + as_bad_where (file, line, "%s", err); + + sb_kill (&s); + + input_scrub_include_sb (&out, input_line_pointer, 1); + sb_kill (&out); + buffer_limit = input_scrub_next_buffer (&input_line_pointer); +} + +/* Handle the .linkonce pseudo-op. This tells the assembler to mark + the section to only be linked once. However, this is not supported + by most object file formats. This takes an optional argument, + which is what to do about duplicates. */ + +void +s_linkonce (int ignore ATTRIBUTE_UNUSED) +{ + enum linkonce_type type; + + SKIP_WHITESPACE (); + + type = LINKONCE_DISCARD; + + if (!is_end_of_line[(unsigned char) *input_line_pointer]) + { + char *s; + char c; + + s = input_line_pointer; + c = get_symbol_end (); + if (strcasecmp (s, "discard") == 0) + type = LINKONCE_DISCARD; + else if (strcasecmp (s, "one_only") == 0) + type = LINKONCE_ONE_ONLY; + else if (strcasecmp (s, "same_size") == 0) + type = LINKONCE_SAME_SIZE; + else if (strcasecmp (s, "same_contents") == 0) + type = LINKONCE_SAME_CONTENTS; + else + as_warn (_("unrecognized .linkonce type `%s'"), s); + + *input_line_pointer = c; + } + +#ifdef obj_handle_link_once + obj_handle_link_once (type); +#else /* ! defined (obj_handle_link_once) */ + { + flagword flags; + + if ((bfd_applicable_section_flags (stdoutput) & SEC_LINK_ONCE) == 0) + as_warn (_(".linkonce is not supported for this object file format")); + + flags = bfd_get_section_flags (stdoutput, now_seg); + flags |= SEC_LINK_ONCE; + switch (type) + { + default: + abort (); + case LINKONCE_DISCARD: + flags |= SEC_LINK_DUPLICATES_DISCARD; + break; + case LINKONCE_ONE_ONLY: + flags |= SEC_LINK_DUPLICATES_ONE_ONLY; + break; + case LINKONCE_SAME_SIZE: + flags |= SEC_LINK_DUPLICATES_SAME_SIZE; + break; + case LINKONCE_SAME_CONTENTS: + flags |= SEC_LINK_DUPLICATES_SAME_CONTENTS; + break; + } + if (!bfd_set_section_flags (stdoutput, now_seg, flags)) + as_bad (_("bfd_set_section_flags: %s"), + bfd_errmsg (bfd_get_error ())); + } +#endif /* ! defined (obj_handle_link_once) */ + + demand_empty_rest_of_line (); +} + +void +bss_alloc (symbolS *symbolP, addressT size, int align) +{ + char *pfrag; + segT current_seg = now_seg; + subsegT current_subseg = now_subseg; + segT bss_seg = bss_section; + +#if defined (TC_MIPS) || defined (TC_ALPHA) + if (OUTPUT_FLAVOR == bfd_target_ecoff_flavour + || OUTPUT_FLAVOR == bfd_target_elf_flavour) + { + /* For MIPS and Alpha ECOFF or ELF, small objects are put in .sbss. */ + if (size <= bfd_get_gp_size (stdoutput)) + { + bss_seg = subseg_new (".sbss", 1); + seg_info (bss_seg)->bss = 1; + if (!bfd_set_section_flags (stdoutput, bss_seg, SEC_ALLOC)) + as_warn (_("error setting flags for \".sbss\": %s"), + bfd_errmsg (bfd_get_error ())); + } + } +#endif + subseg_set (bss_seg, 1); + + if (align) + { + record_alignment (bss_seg, align); + frag_align (align, 0, 0); + } + + /* Detach from old frag. */ + if (S_GET_SEGMENT (symbolP) == bss_seg) + symbol_get_frag (symbolP)->fr_symbol = NULL; + + symbol_set_frag (symbolP, frag_now); + pfrag = frag_var (rs_org, 1, 1, 0, symbolP, size, NULL); + *pfrag = 0; + +#ifdef S_SET_SIZE + S_SET_SIZE (symbolP, size); +#endif + S_SET_SEGMENT (symbolP, bss_seg); + +#ifdef OBJ_COFF + /* The symbol may already have been created with a preceding + ".globl" directive -- be careful not to step on storage class + in that case. Otherwise, set it to static. */ + if (S_GET_STORAGE_CLASS (symbolP) != C_EXT) + S_SET_STORAGE_CLASS (symbolP, C_STAT); +#endif /* OBJ_COFF */ + + subseg_set (current_seg, current_subseg); +} + +offsetT +parse_align (int align_bytes) +{ + expressionS exp; + addressT align; + + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + { + no_align: + as_bad (_("expected alignment after size")); + ignore_rest_of_line (); + return -1; + } + + input_line_pointer++; + SKIP_WHITESPACE (); + + align = get_absolute_expr (&exp); + if (exp.X_op == O_absent) + goto no_align; + + if (!exp.X_unsigned) + { + as_warn (_("alignment negative; 0 assumed")); + align = 0; + } + + if (align_bytes && align != 0) + { + /* convert to a power of 2 alignment */ + unsigned int alignp2 = 0; + while ((align & 1) == 0) + align >>= 1, ++alignp2; + if (align != 1) + { + as_bad (_("alignment not a power of 2")); + ignore_rest_of_line (); + return -1; + } + align = alignp2; + } + return align; +} + +/* Called from s_comm_internal after symbol name and size have been + parsed. NEEDS_ALIGN is 0 if it was an ".lcomm" (2 args only), + 1 if this was a ".bss" directive which has a 3rd argument + (alignment as a power of 2), or 2 if this was a ".bss" directive + with alignment in bytes. */ + +symbolS * +s_lcomm_internal (int needs_align, symbolS *symbolP, addressT size) +{ + addressT align = 0; + + if (needs_align) + { + align = parse_align (needs_align - 1); + if (align == (addressT) -1) + return NULL; + } + else + /* Assume some objects may require alignment on some systems. */ + TC_IMPLICIT_LCOMM_ALIGNMENT (size, align); + + bss_alloc (symbolP, size, align); + return symbolP; +} + +void +s_lcomm (int needs_align) +{ + s_comm_internal (needs_align, s_lcomm_internal); +} + +void +s_lcomm_bytes (int needs_align) +{ + s_comm_internal (needs_align * 2, s_lcomm_internal); +} + +void +s_lsym (int ignore ATTRIBUTE_UNUSED) +{ + char *name; + expressionS exp; + symbolS *symbolP; + + /* We permit ANY defined expression: BSD4.2 demands constants. */ + if ((name = read_symbol_name ()) == NULL) + return; + + if (*input_line_pointer != ',') + { + as_bad (_("expected comma after \"%s\""), name); + goto err_out; + } + + input_line_pointer++; + expression_and_evaluate (&exp); + + if (exp.X_op != O_constant + && exp.X_op != O_register) + { + as_bad (_("bad expression")); + goto err_out; + } + + symbolP = symbol_find_or_make (name); + + if (S_GET_SEGMENT (symbolP) == undefined_section) + { + /* The name might be an undefined .global symbol; be sure to + keep the "external" bit. */ + S_SET_SEGMENT (symbolP, + (exp.X_op == O_constant + ? absolute_section + : reg_section)); + S_SET_VALUE (symbolP, (valueT) exp.X_add_number); + } + else + { + as_bad (_("symbol `%s' is already defined"), name); + } + + demand_empty_rest_of_line (); + free (name); + return; + + err_out: + ignore_rest_of_line (); + free (name); + return; +} + +/* Read a line into an sb. Returns the character that ended the line + or zero if there are no more lines. */ + +static int +get_line_sb (sb *line, int in_macro) +{ + char *eol; + + if (input_line_pointer[-1] == '\n') + bump_line_counters (); + + if (input_line_pointer >= buffer_limit) + { + buffer_limit = input_scrub_next_buffer (&input_line_pointer); + if (buffer_limit == 0) + return 0; + } + + eol = _find_end_of_line (input_line_pointer, flag_m68k_mri, 0, in_macro); + sb_add_buffer (line, input_line_pointer, eol - input_line_pointer); + input_line_pointer = eol; + + /* Don't skip multiple end-of-line characters, because that breaks support + for the IA-64 stop bit (;;) which looks like two consecutive end-of-line + characters but isn't. Instead just skip one end of line character and + return the character skipped so that the caller can re-insert it if + necessary. */ + return *input_line_pointer++; +} + +static size_t +get_non_macro_line_sb (sb *line) +{ + return get_line_sb (line, 0); +} + +static size_t +get_macro_line_sb (sb *line) +{ + return get_line_sb (line, 1); +} + +/* Define a macro. This is an interface to macro.c. */ + +void +s_macro (int ignore ATTRIBUTE_UNUSED) +{ + char *file, *eol; + unsigned int line; + sb s; + const char *err; + const char *name; + + as_where (&file, &line); + + eol = find_end_of_line (input_line_pointer, 0); + sb_build (&s, eol - input_line_pointer); + sb_add_buffer (&s, input_line_pointer, eol - input_line_pointer); + input_line_pointer = eol; + + if (line_label != NULL) + { + sb label; + size_t len; + + name = S_GET_NAME (line_label); + len = strlen (name); + sb_build (&label, len); + sb_add_buffer (&label, name, len); + err = define_macro (0, &s, &label, get_macro_line_sb, file, line, &name); + sb_kill (&label); + } + else + err = define_macro (0, &s, NULL, get_macro_line_sb, file, line, &name); + if (err != NULL) + as_bad_where (file, line, err, name); + else + { + if (line_label != NULL) + { + S_SET_SEGMENT (line_label, absolute_section); + S_SET_VALUE (line_label, 0); + symbol_set_frag (line_label, &zero_address_frag); + } + + if (((NO_PSEUDO_DOT || flag_m68k_mri) + && hash_find (po_hash, name) != NULL) + || (!flag_m68k_mri + && *name == '.' + && hash_find (po_hash, name + 1) != NULL)) + as_warn_where (file, + line, + _("attempt to redefine pseudo-op `%s' ignored"), + name); + } + + sb_kill (&s); +} + +/* Handle the .mexit pseudo-op, which immediately exits a macro + expansion. */ + +void +s_mexit (int ignore ATTRIBUTE_UNUSED) +{ + if (macro_nest) + { + cond_exit_macro (macro_nest); + buffer_limit = input_scrub_next_buffer (&input_line_pointer); + } + else + as_warn (_("ignoring macro exit outside a macro definition.")); +} + +/* Switch in and out of MRI mode. */ + +void +s_mri (int ignore ATTRIBUTE_UNUSED) +{ + int on; +#ifdef MRI_MODE_CHANGE + int old_flag; +#endif + + on = get_absolute_expression (); +#ifdef MRI_MODE_CHANGE + old_flag = flag_mri; +#endif + if (on != 0) + { + flag_mri = 1; +#ifdef TC_M68K + flag_m68k_mri = 1; +#endif + macro_mri_mode (1); + } + else + { + flag_mri = 0; +#ifdef TC_M68K + flag_m68k_mri = 0; +#endif + macro_mri_mode (0); + } + + /* Operator precedence changes in m68k MRI mode, so we need to + update the operator rankings. */ + expr_set_precedence (); + +#ifdef MRI_MODE_CHANGE + if (on != old_flag) + MRI_MODE_CHANGE (on); +#endif + + demand_empty_rest_of_line (); +} + +/* Handle changing the location counter. */ + +static void +do_org (segT segment, expressionS *exp, int fill) +{ + if (segment != now_seg + && segment != absolute_section + && segment != expr_section) + as_bad (_("invalid segment \"%s\""), segment_name (segment)); + + if (now_seg == absolute_section) + { + if (fill != 0) + as_warn (_("ignoring fill value in absolute section")); + if (exp->X_op != O_constant) + { + as_bad (_("only constant offsets supported in absolute section")); + exp->X_add_number = 0; + } + abs_section_offset = exp->X_add_number; + } + else + { + char *p; + symbolS *sym = exp->X_add_symbol; + offsetT off = exp->X_add_number * OCTETS_PER_BYTE; + + if (exp->X_op != O_constant && exp->X_op != O_symbol) + { + /* Handle complex expressions. */ + sym = make_expr_symbol (exp); + off = 0; + } + + p = frag_var (rs_org, 1, 1, (relax_substateT) 0, sym, off, (char *) 0); + *p = fill; + } +} + +void +s_org (int ignore ATTRIBUTE_UNUSED) +{ + segT segment; + expressionS exp; + long temp_fill; + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + + /* The m68k MRI assembler has a different meaning for .org. It + means to create an absolute section at a given address. We can't + support that--use a linker script instead. */ + if (flag_m68k_mri) + { + as_bad (_("MRI style ORG pseudo-op not supported")); + ignore_rest_of_line (); + return; + } + + /* Don't believe the documentation of BSD 4.2 AS. There is no such + thing as a sub-segment-relative origin. Any absolute origin is + given a warning, then assumed to be segment-relative. Any + segmented origin expression ("foo+42") had better be in the right + segment or the .org is ignored. + + BSD 4.2 AS warns if you try to .org backwards. We cannot because + we never know sub-segment sizes when we are reading code. BSD + will crash trying to emit negative numbers of filler bytes in + certain .orgs. We don't crash, but see as-write for that code. + + Don't make frag if need_pass_2==1. */ + segment = get_known_segmented_expression (&exp); + if (*input_line_pointer == ',') + { + input_line_pointer++; + temp_fill = get_absolute_expression (); + } + else + temp_fill = 0; + + if (!need_pass_2) + do_org (segment, &exp, temp_fill); + + demand_empty_rest_of_line (); +} + +/* Handle parsing for the MRI SECT/SECTION pseudo-op. This should be + called by the obj-format routine which handles section changing + when in MRI mode. It will create a new section, and return it. It + will set *TYPE to the section type: one of 'C' (code), 'D' (data), + 'M' (mixed), or 'R' (romable). The flags will be set in the section. */ + +void +s_mri_sect (char *type ATTRIBUTE_UNUSED) +{ +#ifdef TC_M68K + + char *name; + char c; + segT seg; + + SKIP_WHITESPACE (); + + name = input_line_pointer; + if (!ISDIGIT (*name)) + c = get_symbol_end (); + else + { + do + { + ++input_line_pointer; + } + while (ISDIGIT (*input_line_pointer)); + + c = *input_line_pointer; + *input_line_pointer = '\0'; + } + + name = xstrdup (name); + + *input_line_pointer = c; + + seg = subseg_new (name, 0); + + if (*input_line_pointer == ',') + { + int align; + + ++input_line_pointer; + align = get_absolute_expression (); + record_alignment (seg, align); + } + + *type = 'C'; + if (*input_line_pointer == ',') + { + c = *++input_line_pointer; + c = TOUPPER (c); + if (c == 'C' || c == 'D' || c == 'M' || c == 'R') + *type = c; + else + as_bad (_("unrecognized section type")); + ++input_line_pointer; + + { + flagword flags; + + flags = SEC_NO_FLAGS; + if (*type == 'C') + flags = SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE; + else if (*type == 'D' || *type == 'M') + flags = SEC_ALLOC | SEC_LOAD | SEC_DATA; + else if (*type == 'R') + flags = SEC_ALLOC | SEC_LOAD | SEC_DATA | SEC_READONLY | SEC_ROM; + if (flags != SEC_NO_FLAGS) + { + if (!bfd_set_section_flags (stdoutput, seg, flags)) + as_warn (_("error setting flags for \"%s\": %s"), + bfd_section_name (stdoutput, seg), + bfd_errmsg (bfd_get_error ())); + } + } + } + + /* Ignore the HP type. */ + if (*input_line_pointer == ',') + input_line_pointer += 2; + + demand_empty_rest_of_line (); + +#else /* ! TC_M68K */ +#ifdef TC_I960 + + char *name; + char c; + segT seg; + + SKIP_WHITESPACE (); + + name = input_line_pointer; + c = get_symbol_end (); + + name = xstrdup (name); + + *input_line_pointer = c; + + seg = subseg_new (name, 0); + + if (*input_line_pointer != ',') + *type = 'C'; + else + { + char *sectype; + + ++input_line_pointer; + SKIP_WHITESPACE (); + sectype = input_line_pointer; + c = get_symbol_end (); + if (*sectype == '\0') + *type = 'C'; + else if (strcasecmp (sectype, "text") == 0) + *type = 'C'; + else if (strcasecmp (sectype, "data") == 0) + *type = 'D'; + else if (strcasecmp (sectype, "romdata") == 0) + *type = 'R'; + else + as_warn (_("unrecognized section type `%s'"), sectype); + *input_line_pointer = c; + } + + if (*input_line_pointer == ',') + { + char *seccmd; + + ++input_line_pointer; + SKIP_WHITESPACE (); + seccmd = input_line_pointer; + c = get_symbol_end (); + if (strcasecmp (seccmd, "absolute") == 0) + { + as_bad (_("absolute sections are not supported")); + *input_line_pointer = c; + ignore_rest_of_line (); + return; + } + else if (strcasecmp (seccmd, "align") == 0) + { + int align; + + *input_line_pointer = c; + align = get_absolute_expression (); + record_alignment (seg, align); + } + else + { + as_warn (_("unrecognized section command `%s'"), seccmd); + *input_line_pointer = c; + } + } + + demand_empty_rest_of_line (); + +#else /* ! TC_I960 */ + /* The MRI assembler seems to use different forms of .sect for + different targets. */ + as_bad ("MRI mode not supported for this target"); + ignore_rest_of_line (); +#endif /* ! TC_I960 */ +#endif /* ! TC_M68K */ +} + +/* Handle the .print pseudo-op. */ + +void +s_print (int ignore ATTRIBUTE_UNUSED) +{ + char *s; + int len; + + s = demand_copy_C_string (&len); + if (s != NULL) + printf ("%s\n", s); + demand_empty_rest_of_line (); +} + +/* Handle the .purgem pseudo-op. */ + +void +s_purgem (int ignore ATTRIBUTE_UNUSED) +{ + if (is_it_end_of_statement ()) + { + demand_empty_rest_of_line (); + return; + } + + do + { + char *name; + char c; + + SKIP_WHITESPACE (); + name = input_line_pointer; + c = get_symbol_end (); + delete_macro (name); + *input_line_pointer = c; + SKIP_WHITESPACE (); + } + while (*input_line_pointer++ == ','); + + --input_line_pointer; + demand_empty_rest_of_line (); +} + +/* Handle the .endm/.endr pseudo-ops. */ + +static void +s_bad_end (int endr) +{ + as_warn (_(".end%c encountered without preceding %s"), + endr ? 'r' : 'm', + endr ? ".rept, .irp, or .irpc" : ".macro"); + demand_empty_rest_of_line (); +} + +/* Handle the .rept pseudo-op. */ + +void +s_rept (int ignore ATTRIBUTE_UNUSED) +{ + int count; + + count = get_absolute_expression (); + + do_repeat (count, "REPT", "ENDR"); +} + +/* This function provides a generic repeat block implementation. It allows + different directives to be used as the start/end keys. */ + +void +do_repeat (int count, const char *start, const char *end) +{ + sb one; + sb many; + + sb_new (&one); + if (!buffer_and_nest (start, end, &one, get_non_macro_line_sb)) + { + as_bad (_("%s without %s"), start, end); + return; + } + + sb_build (&many, count * one.len); + while (count-- > 0) + sb_add_sb (&many, &one); + + sb_kill (&one); + + input_scrub_include_sb (&many, input_line_pointer, 1); + sb_kill (&many); + buffer_limit = input_scrub_next_buffer (&input_line_pointer); +} + +/* Like do_repeat except that any text matching EXPANDER in the + block is replaced by the itteration count. */ + +void +do_repeat_with_expander (int count, + const char * start, + const char * end, + const char * expander) +{ + sb one; + sb many; + + sb_new (&one); + if (!buffer_and_nest (start, end, &one, get_non_macro_line_sb)) + { + as_bad (_("%s without %s"), start, end); + return; + } + + sb_new (&many); + + if (expander != NULL && strstr (one.ptr, expander) != NULL) + { + while (count -- > 0) + { + int len; + char * sub; + sb processed; + + sb_build (& processed, one.len); + sb_add_sb (& processed, & one); + sub = strstr (processed.ptr, expander); + len = sprintf (sub, "%d", count); + gas_assert (len < 8); + strcpy (sub + len, sub + 8); + processed.len -= (8 - len); + sb_add_sb (& many, & processed); + sb_kill (& processed); + } + } + else + while (count-- > 0) + sb_add_sb (&many, &one); + + sb_kill (&one); + + input_scrub_include_sb (&many, input_line_pointer, 1); + sb_kill (&many); + buffer_limit = input_scrub_next_buffer (&input_line_pointer); +} + +/* Skip to end of current repeat loop; EXTRA indicates how many additional + input buffers to skip. Assumes that conditionals preceding the loop end + are properly nested. + + This function makes it easier to implement a premature "break" out of the + loop. The EXTRA arg accounts for other buffers we might have inserted, + such as line substitutions. */ + +void +end_repeat (int extra) +{ + cond_exit_macro (macro_nest); + while (extra-- >= 0) + buffer_limit = input_scrub_next_buffer (&input_line_pointer); +} + +static void +assign_symbol (char *name, int mode) +{ + symbolS *symbolP; + + if (name[0] == '.' && name[1] == '\0') + { + /* Turn '. = mumble' into a .org mumble. */ + segT segment; + expressionS exp; + + segment = get_known_segmented_expression (&exp); + + if (!need_pass_2) + do_org (segment, &exp, 0); + + return; + } + + if ((symbolP = symbol_find (name)) == NULL + && (symbolP = md_undefined_symbol (name)) == NULL) + { + symbolP = symbol_find_or_make (name); +#ifndef NO_LISTING + /* When doing symbol listings, play games with dummy fragments living + outside the normal fragment chain to record the file and line info + for this symbol. */ + if (listing & LISTING_SYMBOLS) + { + extern struct list_info_struct *listing_tail; + fragS *dummy_frag = (fragS *) xcalloc (1, sizeof (fragS)); + dummy_frag->line = listing_tail; + dummy_frag->fr_symbol = symbolP; + symbol_set_frag (symbolP, dummy_frag); + } +#endif +#ifdef OBJ_COFF + /* "set" symbols are local unless otherwise specified. */ + SF_SET_LOCAL (symbolP); +#endif + } + + if (S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP)) + { + if ((mode != 0 || !S_IS_VOLATILE (symbolP)) + && !S_CAN_BE_REDEFINED (symbolP)) + { + as_bad (_("symbol `%s' is already defined"), name); + symbolP = symbol_clone (symbolP, 0); + } + /* If the symbol is volatile, copy the symbol and replace the + original with the copy, so that previous uses of the symbol will + retain the value of the symbol at the point of use. */ + else if (S_IS_VOLATILE (symbolP)) + symbolP = symbol_clone (symbolP, 1); + } + + if (mode == 0) + S_SET_VOLATILE (symbolP); + else if (mode < 0) + S_SET_FORWARD_REF (symbolP); + + pseudo_set (symbolP); +} + +/* Handle the .equ, .equiv, .eqv, and .set directives. If EQUIV is 1, + then this is .equiv, and it is an error if the symbol is already + defined. If EQUIV is -1, the symbol additionally is a forward + reference. */ + +void +s_set (int equiv) +{ + char *name; + + /* Especial apologies for the random logic: + this just grew, and could be parsed much more simply! + Dean in haste. */ + if ((name = read_symbol_name ()) == NULL) + return; + + if (*input_line_pointer != ',') + { + as_bad (_("expected comma after \"%s\""), name); + ignore_rest_of_line (); + free (name); + return; + } + + input_line_pointer++; + assign_symbol (name, equiv); + demand_empty_rest_of_line (); + free (name); +} + +void +s_space (int mult) +{ + expressionS exp; + expressionS val; + char *p = 0; + char *stop = NULL; + char stopc = 0; + int bytes; + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + +#ifdef md_cons_align + md_cons_align (1); +#endif + + if (flag_mri) + stop = mri_comment_field (&stopc); + + /* In m68k MRI mode, we need to align to a word boundary, unless + this is ds.b. */ + if (flag_m68k_mri && mult > 1) + { + if (now_seg == absolute_section) + { + abs_section_offset += abs_section_offset & 1; + if (line_label != NULL) + S_SET_VALUE (line_label, abs_section_offset); + } + else if (mri_common_symbol != NULL) + { + valueT mri_val; + + mri_val = S_GET_VALUE (mri_common_symbol); + if ((mri_val & 1) != 0) + { + S_SET_VALUE (mri_common_symbol, mri_val + 1); + if (line_label != NULL) + { + expressionS *symexp; + + symexp = symbol_get_value_expression (line_label); + know (symexp->X_op == O_symbol); + know (symexp->X_add_symbol == mri_common_symbol); + symexp->X_add_number += 1; + } + } + } + else + { + do_align (1, (char *) NULL, 0, 0); + if (line_label != NULL) + { + symbol_set_frag (line_label, frag_now); + S_SET_VALUE (line_label, frag_now_fix ()); + } + } + } + + bytes = mult; + + expression (&exp); + + SKIP_WHITESPACE (); + if (*input_line_pointer == ',') + { + ++input_line_pointer; + expression (&val); + } + else + { + val.X_op = O_constant; + val.X_add_number = 0; + } + + if (val.X_op != O_constant + || val.X_add_number < - 0x80 + || val.X_add_number > 0xff + || (mult != 0 && mult != 1 && val.X_add_number != 0)) + { + resolve_expression (&exp); + if (exp.X_op != O_constant) + as_bad (_("unsupported variable size or fill value")); + else + { + offsetT i; + + if (mult == 0) + mult = 1; + bytes = mult * exp.X_add_number; + for (i = 0; i < exp.X_add_number; i++) + emit_expr (&val, mult); + } + } + else + { + if (now_seg == absolute_section || mri_common_symbol != NULL) + resolve_expression (&exp); + + if (exp.X_op == O_constant) + { + offsetT repeat; + + repeat = exp.X_add_number; + if (mult) + repeat *= mult; + bytes = repeat; + if (repeat <= 0) + { + if (!flag_mri) + as_warn (_(".space repeat count is zero, ignored")); + else if (repeat < 0) + as_warn (_(".space repeat count is negative, ignored")); + goto getout; + } + + /* If we are in the absolute section, just bump the offset. */ + if (now_seg == absolute_section) + { + abs_section_offset += repeat; + goto getout; + } + + /* If we are secretly in an MRI common section, then + creating space just increases the size of the common + symbol. */ + if (mri_common_symbol != NULL) + { + S_SET_VALUE (mri_common_symbol, + S_GET_VALUE (mri_common_symbol) + repeat); + goto getout; + } + + if (!need_pass_2) + p = frag_var (rs_fill, 1, 1, (relax_substateT) 0, (symbolS *) 0, + (offsetT) repeat, (char *) 0); + } + else + { + if (now_seg == absolute_section) + { + as_bad (_("space allocation too complex in absolute section")); + subseg_set (text_section, 0); + } + + if (mri_common_symbol != NULL) + { + as_bad (_("space allocation too complex in common section")); + mri_common_symbol = NULL; + } + + if (!need_pass_2) + p = frag_var (rs_space, 1, 1, (relax_substateT) 0, + make_expr_symbol (&exp), (offsetT) 0, (char *) 0); + } + + if (p) + *p = val.X_add_number; + } + + getout: + + /* In MRI mode, after an odd number of bytes, we must align to an + even word boundary, unless the next instruction is a dc.b, ds.b + or dcb.b. */ + if (flag_mri && (bytes & 1) != 0) + mri_pending_align = 1; + + demand_empty_rest_of_line (); + + if (flag_mri) + mri_comment_end (stop, stopc); +} + +/* This is like s_space, but the value is a floating point number with + the given precision. This is for the MRI dcb.s pseudo-op and + friends. */ + +void +s_float_space (int float_type) +{ + offsetT count; + int flen; + char temp[MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT]; + char *stop = NULL; + char stopc = 0; + +#ifdef md_cons_align + md_cons_align (1); +#endif + + if (flag_mri) + stop = mri_comment_field (&stopc); + + count = get_absolute_expression (); + + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + { + as_bad (_("missing value")); + ignore_rest_of_line (); + if (flag_mri) + mri_comment_end (stop, stopc); + return; + } + + ++input_line_pointer; + + SKIP_WHITESPACE (); + + /* Skip any 0{letter} that may be present. Don't even check if the + * letter is legal. */ + if (input_line_pointer[0] == '0' + && ISALPHA (input_line_pointer[1])) + input_line_pointer += 2; + + /* Accept :xxxx, where the x's are hex digits, for a floating point + with the exact digits specified. */ + if (input_line_pointer[0] == ':') + { + flen = hex_float (float_type, temp); + if (flen < 0) + { + ignore_rest_of_line (); + if (flag_mri) + mri_comment_end (stop, stopc); + return; + } + } + else + { + char *err; + + err = md_atof (float_type, temp, &flen); + know (flen <= MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT); + know (err != NULL || flen > 0); + if (err) + { + as_bad (_("bad floating literal: %s"), err); + ignore_rest_of_line (); + if (flag_mri) + mri_comment_end (stop, stopc); + return; + } + } + + while (--count >= 0) + { + char *p; + + p = frag_more (flen); + memcpy (p, temp, (unsigned int) flen); + } + + demand_empty_rest_of_line (); + + if (flag_mri) + mri_comment_end (stop, stopc); +} + +/* Handle the .struct pseudo-op, as found in MIPS assemblers. */ + +void +s_struct (int ignore ATTRIBUTE_UNUSED) +{ + char *stop = NULL; + char stopc = 0; + + if (flag_mri) + stop = mri_comment_field (&stopc); + abs_section_offset = get_absolute_expression (); +#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) + /* The ELF backend needs to know that we are changing sections, so + that .previous works correctly. */ + if (IS_ELF) + obj_elf_section_change_hook (); +#endif + subseg_set (absolute_section, 0); + demand_empty_rest_of_line (); + if (flag_mri) + mri_comment_end (stop, stopc); +} + +void +s_text (int ignore ATTRIBUTE_UNUSED) +{ + int temp; + + temp = get_absolute_expression (); + subseg_set (text_section, (subsegT) temp); + demand_empty_rest_of_line (); +} + +/* .weakref x, y sets x as an alias to y that, as long as y is not + referenced directly, will cause y to become a weak symbol. */ +void +s_weakref (int ignore ATTRIBUTE_UNUSED) +{ + char *name; + symbolS *symbolP; + symbolS *symbolP2; + expressionS exp; + + if ((name = read_symbol_name ()) == NULL) + return; + + symbolP = symbol_find_or_make (name); + + if (S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP)) + { + if (!S_IS_VOLATILE (symbolP)) + { + as_bad (_("symbol `%s' is already defined"), name); + goto err_out; + } + symbolP = symbol_clone (symbolP, 1); + S_CLEAR_VOLATILE (symbolP); + } + + SKIP_WHITESPACE (); + + if (*input_line_pointer != ',') + { + as_bad (_("expected comma after \"%s\""), name); + goto err_out; + } + + input_line_pointer++; + + SKIP_WHITESPACE (); + free (name); + + if ((name = read_symbol_name ()) == NULL) + return; + + if ((symbolP2 = symbol_find_noref (name, 1)) == NULL + && (symbolP2 = md_undefined_symbol (name)) == NULL) + { + symbolP2 = symbol_find_or_make (name); + S_SET_WEAKREFD (symbolP2); + } + else + { + symbolS *symp = symbolP2; + + while (S_IS_WEAKREFR (symp) && symp != symbolP) + { + expressionS *expP = symbol_get_value_expression (symp); + + gas_assert (expP->X_op == O_symbol + && expP->X_add_number == 0); + symp = expP->X_add_symbol; + } + if (symp == symbolP) + { + char *loop; + + loop = concat (S_GET_NAME (symbolP), + " => ", S_GET_NAME (symbolP2), (const char *) NULL); + + symp = symbolP2; + while (symp != symbolP) + { + char *old_loop = loop; + + symp = symbol_get_value_expression (symp)->X_add_symbol; + loop = concat (loop, " => ", S_GET_NAME (symp), + (const char *) NULL); + free (old_loop); + } + + as_bad (_("%s: would close weakref loop: %s"), + S_GET_NAME (symbolP), loop); + + free (loop); + free (name); + ignore_rest_of_line (); + return; + } + + /* Short-circuiting instead of just checking here might speed + things up a tiny little bit, but loop error messages would + miss intermediate links. */ + /* symbolP2 = symp; */ + } + + memset (&exp, 0, sizeof (exp)); + exp.X_op = O_symbol; + exp.X_add_symbol = symbolP2; + + S_SET_SEGMENT (symbolP, undefined_section); + symbol_set_value_expression (symbolP, &exp); + symbol_set_frag (symbolP, &zero_address_frag); + S_SET_WEAKREFR (symbolP); + + demand_empty_rest_of_line (); + free (name); + return; + + err_out: + ignore_rest_of_line (); + free (name); + return; +} + + +/* Verify that we are at the end of a line. If not, issue an error and + skip to EOL. */ + +void +demand_empty_rest_of_line (void) +{ + SKIP_WHITESPACE (); + if (is_end_of_line[(unsigned char) *input_line_pointer]) + input_line_pointer++; + else + { + if (ISPRINT (*input_line_pointer)) + as_bad (_("junk at end of line, first unrecognized character is `%c'"), + *input_line_pointer); + else + as_bad (_("junk at end of line, first unrecognized character valued 0x%x"), + *input_line_pointer); + ignore_rest_of_line (); + } + + /* Return pointing just after end-of-line. */ + know (is_end_of_line[(unsigned char) input_line_pointer[-1]]); +} + +/* Silently advance to the end of line. Use this after already having + issued an error about something bad. */ + +void +ignore_rest_of_line (void) +{ + while (input_line_pointer < buffer_limit + && !is_end_of_line[(unsigned char) *input_line_pointer]) + input_line_pointer++; + + input_line_pointer++; + + /* Return pointing just after end-of-line. */ + know (is_end_of_line[(unsigned char) input_line_pointer[-1]]); +} + +/* Sets frag for given symbol to zero_address_frag, except when the + symbol frag is already set to a dummy listing frag. */ + +static void +set_zero_frag (symbolS *symbolP) +{ + if (symbol_get_frag (symbolP)->fr_type != rs_dummy) + symbol_set_frag (symbolP, &zero_address_frag); +} + +/* In: Pointer to a symbol. + Input_line_pointer->expression. + + Out: Input_line_pointer->just after any whitespace after expression. + Tried to set symbol to value of expression. + Will change symbols type, value, and frag; */ + +void +pseudo_set (symbolS *symbolP) +{ + expressionS exp; + segT seg; + + know (symbolP); /* NULL pointer is logic error. */ + + if (!S_IS_FORWARD_REF (symbolP)) + (void) expression (&exp); + else + (void) deferred_expression (&exp); + + if (exp.X_op == O_illegal) + as_bad (_("illegal expression")); + else if (exp.X_op == O_absent) + as_bad (_("missing expression")); + else if (exp.X_op == O_big) + { + if (exp.X_add_number > 0) + as_bad (_("bignum invalid")); + else + as_bad (_("floating point number invalid")); + } + else if (exp.X_op == O_subtract + && !S_IS_FORWARD_REF (symbolP) + && SEG_NORMAL (S_GET_SEGMENT (exp.X_add_symbol)) + && (symbol_get_frag (exp.X_add_symbol) + == symbol_get_frag (exp.X_op_symbol))) + { + exp.X_op = O_constant; + exp.X_add_number = (S_GET_VALUE (exp.X_add_symbol) + - S_GET_VALUE (exp.X_op_symbol)); + } + + if (symbol_section_p (symbolP)) + { + as_bad ("attempt to set value of section symbol"); + return; + } + + switch (exp.X_op) + { + case O_illegal: + case O_absent: + case O_big: + exp.X_add_number = 0; + /* Fall through. */ + case O_constant: + S_SET_SEGMENT (symbolP, absolute_section); + S_SET_VALUE (symbolP, (valueT) exp.X_add_number); + set_zero_frag (symbolP); + break; + + case O_register: +#ifndef TC_GLOBAL_REGISTER_SYMBOL_OK + if (S_IS_EXTERNAL (symbolP)) + { + as_bad ("can't equate global symbol `%s' with register name", + S_GET_NAME (symbolP)); + return; + } +#endif + S_SET_SEGMENT (symbolP, reg_section); + S_SET_VALUE (symbolP, (valueT) exp.X_add_number); + set_zero_frag (symbolP); + symbol_get_value_expression (symbolP)->X_op = O_register; + break; + + case O_symbol: + seg = S_GET_SEGMENT (exp.X_add_symbol); + /* For x=undef+const, create an expression symbol. + For x=x+const, just update x except when x is an undefined symbol + For x=defined+const, evaluate x. */ + if (symbolP == exp.X_add_symbol + && (seg != undefined_section + || !symbol_constant_p (symbolP))) + { + *symbol_X_add_number (symbolP) += exp.X_add_number; + break; + } + else if (!S_IS_FORWARD_REF (symbolP) && seg != undefined_section) + { + symbolS *s = exp.X_add_symbol; + + if (S_IS_COMMON (s)) + as_bad (_("`%s' can't be equated to common symbol '%s'"), + S_GET_NAME (symbolP), S_GET_NAME (s)); + + S_SET_SEGMENT (symbolP, seg); + S_SET_VALUE (symbolP, exp.X_add_number + S_GET_VALUE (s)); + symbol_set_frag (symbolP, symbol_get_frag (s)); + copy_symbol_attributes (symbolP, s); + break; + } + S_SET_SEGMENT (symbolP, undefined_section); + symbol_set_value_expression (symbolP, &exp); + copy_symbol_attributes (symbolP, exp.X_add_symbol); + set_zero_frag (symbolP); + break; + + default: + /* The value is some complex expression. */ + S_SET_SEGMENT (symbolP, expr_section); + symbol_set_value_expression (symbolP, &exp); + set_zero_frag (symbolP); + break; + } +} + +/* cons() + + CONStruct more frag of .bytes, or .words etc. + Should need_pass_2 be 1 then emit no frag(s). + This understands EXPRESSIONS. + + Bug (?) + + This has a split personality. We use expression() to read the + value. We can detect if the value won't fit in a byte or word. + But we can't detect if expression() discarded significant digits + in the case of a long. Not worth the crocks required to fix it. */ + +/* Select a parser for cons expressions. */ + +/* Some targets need to parse the expression in various fancy ways. + You can define TC_PARSE_CONS_EXPRESSION to do whatever you like + (for example, the HPPA does this). Otherwise, you can define + BITFIELD_CONS_EXPRESSIONS to permit bitfields to be specified, or + REPEAT_CONS_EXPRESSIONS to permit repeat counts. If none of these + are defined, which is the normal case, then only simple expressions + are permitted. */ + +#ifdef TC_M68K +static void +parse_mri_cons (expressionS *exp, unsigned int nbytes); +#endif + +#ifndef TC_PARSE_CONS_EXPRESSION +#ifdef BITFIELD_CONS_EXPRESSIONS +#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) parse_bitfield_cons (EXP, NBYTES) +static void +parse_bitfield_cons (expressionS *exp, unsigned int nbytes); +#endif +#ifdef REPEAT_CONS_EXPRESSIONS +#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) parse_repeat_cons (EXP, NBYTES) +static void +parse_repeat_cons (expressionS *exp, unsigned int nbytes); +#endif + +/* If we haven't gotten one yet, just call expression. */ +#ifndef TC_PARSE_CONS_EXPRESSION +#define TC_PARSE_CONS_EXPRESSION(EXP, NBYTES) expression (EXP) +#endif +#endif + +void +do_parse_cons_expression (expressionS *exp, + int nbytes ATTRIBUTE_UNUSED) +{ + TC_PARSE_CONS_EXPRESSION (exp, nbytes); +} + + +/* Worker to do .byte etc statements. + Clobbers input_line_pointer and checks end-of-line. */ + +static void +cons_worker (int nbytes, /* 1=.byte, 2=.word, 4=.long. */ + int rva) +{ + int c; + expressionS exp; + char *stop = NULL; + char stopc = 0; + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + + if (flag_mri) + stop = mri_comment_field (&stopc); + + if (is_it_end_of_statement ()) + { + demand_empty_rest_of_line (); + if (flag_mri) + mri_comment_end (stop, stopc); + return; + } + +#ifdef TC_ADDRESS_BYTES + if (nbytes == 0) + nbytes = TC_ADDRESS_BYTES (); +#endif + +#ifdef md_cons_align + md_cons_align (nbytes); +#endif + + c = 0; + do + { +#ifdef TC_M68K + if (flag_m68k_mri) + parse_mri_cons (&exp, (unsigned int) nbytes); + else +#endif + { + if (*input_line_pointer == '"') + { + as_bad (_("unexpected `\"' in expression")); + ignore_rest_of_line (); + return; + } + TC_PARSE_CONS_EXPRESSION (&exp, (unsigned int) nbytes); + } + + if (rva) + { + if (exp.X_op == O_symbol) + exp.X_op = O_symbol_rva; + else + as_fatal (_("rva without symbol")); + } + emit_expr (&exp, (unsigned int) nbytes); + ++c; + } + while (*input_line_pointer++ == ','); + + /* In MRI mode, after an odd number of bytes, we must align to an + even word boundary, unless the next instruction is a dc.b, ds.b + or dcb.b. */ + if (flag_mri && nbytes == 1 && (c & 1) != 0) + mri_pending_align = 1; + + input_line_pointer--; /* Put terminator back into stream. */ + + demand_empty_rest_of_line (); + + if (flag_mri) + mri_comment_end (stop, stopc); +} + +void +cons (int size) +{ + cons_worker (size, 0); +} + +void +s_rva (int size) +{ + cons_worker (size, 1); +} + +/* .reloc offset, reloc_name, symbol+addend. */ + +void +s_reloc (int ignore ATTRIBUTE_UNUSED) +{ + char *stop = NULL; + char stopc = 0; + expressionS exp; + char *r_name; + int c; + struct reloc_list *reloc; + + reloc = (struct reloc_list *) xmalloc (sizeof (*reloc)); + + if (flag_mri) + stop = mri_comment_field (&stopc); + + expression (&exp); + switch (exp.X_op) + { + case O_illegal: + case O_absent: + case O_big: + case O_register: + as_bad (_("missing or bad offset expression")); + goto err_out; + case O_constant: + exp.X_add_symbol = section_symbol (now_seg); + exp.X_op = O_symbol; + /* Fall thru */ + case O_symbol: + if (exp.X_add_number == 0) + { + reloc->u.a.offset_sym = exp.X_add_symbol; + break; + } + /* Fall thru */ + default: + reloc->u.a.offset_sym = make_expr_symbol (&exp); + break; + } + + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + { + as_bad (_("missing reloc type")); + goto err_out; + } + + ++input_line_pointer; + SKIP_WHITESPACE (); + r_name = input_line_pointer; + c = get_symbol_end (); + reloc->u.a.howto = bfd_reloc_name_lookup (stdoutput, r_name); + *input_line_pointer = c; + if (reloc->u.a.howto == NULL) + { + as_bad (_("unrecognized reloc type")); + goto err_out; + } + + exp.X_op = O_absent; + SKIP_WHITESPACE (); + if (*input_line_pointer == ',') + { + ++input_line_pointer; + expression (&exp); + } + switch (exp.X_op) + { + case O_illegal: + case O_big: + case O_register: + as_bad (_("bad reloc expression")); + err_out: + ignore_rest_of_line (); + free (reloc); + if (flag_mri) + mri_comment_end (stop, stopc); + return; + case O_absent: + reloc->u.a.sym = NULL; + reloc->u.a.addend = 0; + break; + case O_constant: + reloc->u.a.sym = NULL; + reloc->u.a.addend = exp.X_add_number; + break; + case O_symbol: + reloc->u.a.sym = exp.X_add_symbol; + reloc->u.a.addend = exp.X_add_number; + break; + default: + reloc->u.a.sym = make_expr_symbol (&exp); + reloc->u.a.addend = 0; + break; + } + + as_where (&reloc->file, &reloc->line); + reloc->next = reloc_list; + reloc_list = reloc; + + demand_empty_rest_of_line (); + if (flag_mri) + mri_comment_end (stop, stopc); +} + +/* Put the contents of expression EXP into the object file using + NBYTES bytes. If need_pass_2 is 1, this does nothing. */ + +void +emit_expr (expressionS *exp, unsigned int nbytes) +{ + operatorT op; + char *p; + valueT extra_digit = 0; + + /* Don't do anything if we are going to make another pass. */ + if (need_pass_2) + return; + + frag_grow (nbytes); + dot_value = frag_now_fix (); + dot_frag = frag_now; + +#ifndef NO_LISTING +#ifdef OBJ_ELF + /* When gcc emits DWARF 1 debugging pseudo-ops, a line number will + appear as a four byte positive constant in the .line section, + followed by a 2 byte 0xffff. Look for that case here. */ + { + static int dwarf_line = -1; + + if (strcmp (segment_name (now_seg), ".line") != 0) + dwarf_line = -1; + else if (dwarf_line >= 0 + && nbytes == 2 + && exp->X_op == O_constant + && (exp->X_add_number == -1 || exp->X_add_number == 0xffff)) + listing_source_line ((unsigned int) dwarf_line); + else if (nbytes == 4 + && exp->X_op == O_constant + && exp->X_add_number >= 0) + dwarf_line = exp->X_add_number; + else + dwarf_line = -1; + } + + /* When gcc emits DWARF 1 debugging pseudo-ops, a file name will + appear as a 2 byte TAG_compile_unit (0x11) followed by a 2 byte + AT_sibling (0x12) followed by a four byte address of the sibling + followed by a 2 byte AT_name (0x38) followed by the name of the + file. We look for that case here. */ + { + static int dwarf_file = 0; + + if (strcmp (segment_name (now_seg), ".debug") != 0) + dwarf_file = 0; + else if (dwarf_file == 0 + && nbytes == 2 + && exp->X_op == O_constant + && exp->X_add_number == 0x11) + dwarf_file = 1; + else if (dwarf_file == 1 + && nbytes == 2 + && exp->X_op == O_constant + && exp->X_add_number == 0x12) + dwarf_file = 2; + else if (dwarf_file == 2 + && nbytes == 4) + dwarf_file = 3; + else if (dwarf_file == 3 + && nbytes == 2 + && exp->X_op == O_constant + && exp->X_add_number == 0x38) + dwarf_file = 4; + else + dwarf_file = 0; + + /* The variable dwarf_file_string tells stringer that the string + may be the name of the source file. */ + if (dwarf_file == 4) + dwarf_file_string = 1; + else + dwarf_file_string = 0; + } +#endif +#endif + + if (check_eh_frame (exp, &nbytes)) + return; + + op = exp->X_op; + + /* Allow `.word 0' in the absolute section. */ + if (now_seg == absolute_section) + { + if (op != O_constant || exp->X_add_number != 0) + as_bad (_("attempt to store value in absolute section")); + abs_section_offset += nbytes; + return; + } + + /* Handle a negative bignum. */ + if (op == O_uminus + && exp->X_add_number == 0 + && symbol_get_value_expression (exp->X_add_symbol)->X_op == O_big + && symbol_get_value_expression (exp->X_add_symbol)->X_add_number > 0) + { + int i; + unsigned long carry; + + exp = symbol_get_value_expression (exp->X_add_symbol); + + /* Negate the bignum: one's complement each digit and add 1. */ + carry = 1; + for (i = 0; i < exp->X_add_number; i++) + { + unsigned long next; + + next = (((~(generic_bignum[i] & LITTLENUM_MASK)) + & LITTLENUM_MASK) + + carry); + generic_bignum[i] = next & LITTLENUM_MASK; + carry = next >> LITTLENUM_NUMBER_OF_BITS; + } + + /* We can ignore any carry out, because it will be handled by + extra_digit if it is needed. */ + + extra_digit = (valueT) -1; + op = O_big; + } + + if (op == O_absent || op == O_illegal) + { + as_warn (_("zero assumed for missing expression")); + exp->X_add_number = 0; + op = O_constant; + } + else if (op == O_big && exp->X_add_number <= 0) + { + as_bad (_("floating point number invalid")); + exp->X_add_number = 0; + op = O_constant; + } + else if (op == O_register) + { + as_warn (_("register value used as expression")); + op = O_constant; + } + + p = frag_more ((int) nbytes); + +#ifndef WORKING_DOT_WORD + /* If we have the difference of two symbols in a word, save it on + the broken_words list. See the code in write.c. */ + if (op == O_subtract && nbytes == 2) + { + struct broken_word *x; + + x = (struct broken_word *) xmalloc (sizeof (struct broken_word)); + x->next_broken_word = broken_words; + broken_words = x; + x->seg = now_seg; + x->subseg = now_subseg; + x->frag = frag_now; + x->word_goes_here = p; + x->dispfrag = 0; + x->add = exp->X_add_symbol; + x->sub = exp->X_op_symbol; + x->addnum = exp->X_add_number; + x->added = 0; + x->use_jump = 0; + new_broken_words++; + return; + } +#endif + + /* If we have an integer, but the number of bytes is too large to + pass to md_number_to_chars, handle it as a bignum. */ + if (op == O_constant && nbytes > sizeof (valueT)) + { + extra_digit = exp->X_unsigned ? 0 : -1; + convert_to_bignum (exp, !exp->X_unsigned); + op = O_big; + } + + if (op == O_constant) + { + valueT get; + valueT use; + valueT mask; + valueT hibit; + valueT unmask; + + /* JF << of >= number of bits in the object is undefined. In + particular SPARC (Sun 4) has problems. */ + if (nbytes >= sizeof (valueT)) + { + mask = 0; + if (nbytes > sizeof (valueT)) + hibit = 0; + else + hibit = (valueT) 1 << (nbytes * BITS_PER_CHAR - 1); + } + else + { + /* Don't store these bits. */ + mask = ~(valueT) 0 << (BITS_PER_CHAR * nbytes); + hibit = (valueT) 1 << (nbytes * BITS_PER_CHAR - 1); + } + + unmask = ~mask; /* Do store these bits. */ + +#ifdef NEVER + "Do this mod if you want every overflow check to assume SIGNED 2's complement data."; + mask = ~(unmask >> 1); /* Includes sign bit now. */ +#endif + + get = exp->X_add_number; + use = get & unmask; + if ((get & mask) != 0 + && ((get & mask) != mask + || (get & hibit) == 0)) + { /* Leading bits contain both 0s & 1s. */ +#if defined (BFD64) && BFD_HOST_64BIT_LONG_LONG +#ifndef __MSVCRT__ + as_warn (_("value 0x%llx truncated to 0x%llx"), + (unsigned long long) get, (unsigned long long) use); +#else + as_warn (_("value 0x%I64x truncated to 0x%I64x"), + (unsigned long long) get, (unsigned long long) use); +#endif +#else + as_warn (_("value 0x%lx truncated to 0x%lx"), + (unsigned long) get, (unsigned long) use); +#endif + } + /* Put bytes in right order. */ + md_number_to_chars (p, use, (int) nbytes); + } + else if (op == O_big) + { + unsigned int size; + LITTLENUM_TYPE *nums; + + size = exp->X_add_number * CHARS_PER_LITTLENUM; + if (nbytes < size) + { + int i = nbytes / CHARS_PER_LITTLENUM; + if (i != 0) + { + LITTLENUM_TYPE sign = 0; + if ((generic_bignum[--i] + & (1 << (LITTLENUM_NUMBER_OF_BITS - 1))) != 0) + sign = ~(LITTLENUM_TYPE) 0; + while (++i < exp->X_add_number) + if (generic_bignum[i] != sign) + break; + } + if (i < exp->X_add_number) + as_warn (_("bignum truncated to %d bytes"), nbytes); + size = nbytes; + } + + if (nbytes == 1) + { + md_number_to_chars (p, (valueT) generic_bignum[0], 1); + return; + } + know (nbytes % CHARS_PER_LITTLENUM == 0); + + if (target_big_endian) + { + while (nbytes > size) + { + md_number_to_chars (p, extra_digit, CHARS_PER_LITTLENUM); + nbytes -= CHARS_PER_LITTLENUM; + p += CHARS_PER_LITTLENUM; + } + + nums = generic_bignum + size / CHARS_PER_LITTLENUM; + while (size >= CHARS_PER_LITTLENUM) + { + --nums; + md_number_to_chars (p, (valueT) *nums, CHARS_PER_LITTLENUM); + size -= CHARS_PER_LITTLENUM; + p += CHARS_PER_LITTLENUM; + } + } + else + { + nums = generic_bignum; + while (size >= CHARS_PER_LITTLENUM) + { + md_number_to_chars (p, (valueT) *nums, CHARS_PER_LITTLENUM); + ++nums; + size -= CHARS_PER_LITTLENUM; + p += CHARS_PER_LITTLENUM; + nbytes -= CHARS_PER_LITTLENUM; + } + + while (nbytes >= CHARS_PER_LITTLENUM) + { + md_number_to_chars (p, extra_digit, CHARS_PER_LITTLENUM); + nbytes -= CHARS_PER_LITTLENUM; + p += CHARS_PER_LITTLENUM; + } + } + } + else + emit_expr_fix (exp, nbytes, frag_now, p); +} + +void +emit_expr_fix (expressionS *exp, unsigned int nbytes, fragS *frag, char *p) +{ + memset (p, 0, nbytes); + + /* Generate a fixS to record the symbol value. */ + +#ifdef TC_CONS_FIX_NEW + TC_CONS_FIX_NEW (frag, p - frag->fr_literal, nbytes, exp); +#else + { + bfd_reloc_code_real_type r; + + switch (nbytes) + { + case 1: + r = BFD_RELOC_8; + break; + case 2: + r = BFD_RELOC_16; + break; + case 3: + r = BFD_RELOC_24; + break; + case 4: + r = BFD_RELOC_32; + break; + case 8: + r = BFD_RELOC_64; + break; + default: + as_bad (_("unsupported BFD relocation size %u"), nbytes); + r = BFD_RELOC_32; + break; + } + fix_new_exp (frag, p - frag->fr_literal, (int) nbytes, exp, + 0, r); + } +#endif +} + +#ifdef BITFIELD_CONS_EXPRESSIONS + +/* i960 assemblers, (eg, asm960), allow bitfields after ".byte" as + w:x,y:z, where w and y are bitwidths and x and y are values. They + then pack them all together. We do a little better in that we allow + them in words, longs, etc. and we'll pack them in target byte order + for you. + + The rules are: pack least significant bit first, if a field doesn't + entirely fit, put it in the next unit. Overflowing the bitfield is + explicitly *not* even a warning. The bitwidth should be considered + a "mask". + + To use this function the tc-XXX.h file should define + BITFIELD_CONS_EXPRESSIONS. */ + +static void +parse_bitfield_cons (exp, nbytes) + expressionS *exp; + unsigned int nbytes; +{ + unsigned int bits_available = BITS_PER_CHAR * nbytes; + char *hold = input_line_pointer; + + (void) expression (exp); + + if (*input_line_pointer == ':') + { + /* Bitfields. */ + long value = 0; + + for (;;) + { + unsigned long width; + + if (*input_line_pointer != ':') + { + input_line_pointer = hold; + break; + } /* Next piece is not a bitfield. */ + + /* In the general case, we can't allow + full expressions with symbol + differences and such. The relocation + entries for symbols not defined in this + assembly would require arbitrary field + widths, positions, and masks which most + of our current object formats don't + support. + + In the specific case where a symbol + *is* defined in this assembly, we + *could* build fixups and track it, but + this could lead to confusion for the + backends. I'm lazy. I'll take any + SEG_ABSOLUTE. I think that means that + you can use a previous .set or + .equ type symbol. xoxorich. */ + + if (exp->X_op == O_absent) + { + as_warn (_("using a bit field width of zero")); + exp->X_add_number = 0; + exp->X_op = O_constant; + } /* Implied zero width bitfield. */ + + if (exp->X_op != O_constant) + { + *input_line_pointer = '\0'; + as_bad (_("field width \"%s\" too complex for a bitfield"), hold); + *input_line_pointer = ':'; + demand_empty_rest_of_line (); + return; + } /* Too complex. */ + + if ((width = exp->X_add_number) > (BITS_PER_CHAR * nbytes)) + { + as_warn (_("field width %lu too big to fit in %d bytes: truncated to %d bits"), + width, nbytes, (BITS_PER_CHAR * nbytes)); + width = BITS_PER_CHAR * nbytes; + } /* Too big. */ + + if (width > bits_available) + { + /* FIXME-SOMEDAY: backing up and reparsing is wasteful. */ + input_line_pointer = hold; + exp->X_add_number = value; + break; + } /* Won't fit. */ + + /* Skip ':'. */ + hold = ++input_line_pointer; + + (void) expression (exp); + if (exp->X_op != O_constant) + { + char cache = *input_line_pointer; + + *input_line_pointer = '\0'; + as_bad (_("field value \"%s\" too complex for a bitfield"), hold); + *input_line_pointer = cache; + demand_empty_rest_of_line (); + return; + } /* Too complex. */ + + value |= ((~(-1 << width) & exp->X_add_number) + << ((BITS_PER_CHAR * nbytes) - bits_available)); + + if ((bits_available -= width) == 0 + || is_it_end_of_statement () + || *input_line_pointer != ',') + { + break; + } /* All the bitfields we're gonna get. */ + + hold = ++input_line_pointer; + (void) expression (exp); + } + + exp->X_add_number = value; + exp->X_op = O_constant; + exp->X_unsigned = 1; + exp->X_extrabit = 0; + } +} + +#endif /* BITFIELD_CONS_EXPRESSIONS */ + +/* Handle an MRI style string expression. */ + +#ifdef TC_M68K +static void +parse_mri_cons (exp, nbytes) + expressionS *exp; + unsigned int nbytes; +{ + if (*input_line_pointer != '\'' + && (input_line_pointer[1] != '\'' + || (*input_line_pointer != 'A' + && *input_line_pointer != 'E'))) + TC_PARSE_CONS_EXPRESSION (exp, nbytes); + else + { + unsigned int scan; + unsigned int result = 0; + + /* An MRI style string. Cut into as many bytes as will fit into + a nbyte chunk, left justify if necessary, and separate with + commas so we can try again later. */ + if (*input_line_pointer == 'A') + ++input_line_pointer; + else if (*input_line_pointer == 'E') + { + as_bad (_("EBCDIC constants are not supported")); + ++input_line_pointer; + } + + input_line_pointer++; + for (scan = 0; scan < nbytes; scan++) + { + if (*input_line_pointer == '\'') + { + if (input_line_pointer[1] == '\'') + { + input_line_pointer++; + } + else + break; + } + result = (result << 8) | (*input_line_pointer++); + } + + /* Left justify. */ + while (scan < nbytes) + { + result <<= 8; + scan++; + } + + /* Create correct expression. */ + exp->X_op = O_constant; + exp->X_add_number = result; + + /* Fake it so that we can read the next char too. */ + if (input_line_pointer[0] != '\'' || + (input_line_pointer[0] == '\'' && input_line_pointer[1] == '\'')) + { + input_line_pointer -= 2; + input_line_pointer[0] = ','; + input_line_pointer[1] = '\''; + } + else + input_line_pointer++; + } +} +#endif /* TC_M68K */ + +#ifdef REPEAT_CONS_EXPRESSIONS + +/* Parse a repeat expression for cons. This is used by the MIPS + assembler. The format is NUMBER:COUNT; NUMBER appears in the + object file COUNT times. + + To use this for a target, define REPEAT_CONS_EXPRESSIONS. */ + +static void +parse_repeat_cons (exp, nbytes) + expressionS *exp; + unsigned int nbytes; +{ + expressionS count; + int i; + + expression (exp); + + if (*input_line_pointer != ':') + { + /* No repeat count. */ + return; + } + + ++input_line_pointer; + expression (&count); + if (count.X_op != O_constant + || count.X_add_number <= 0) + { + as_warn (_("unresolvable or nonpositive repeat count; using 1")); + return; + } + + /* The cons function is going to output this expression once. So we + output it count - 1 times. */ + for (i = count.X_add_number - 1; i > 0; i--) + emit_expr (exp, nbytes); +} + +#endif /* REPEAT_CONS_EXPRESSIONS */ + +/* Parse a floating point number represented as a hex constant. This + permits users to specify the exact bits they want in the floating + point number. */ + +static int +hex_float (int float_type, char *bytes) +{ + int length; + int i; + + switch (float_type) + { + case 'f': + case 'F': + case 's': + case 'S': + length = 4; + break; + + case 'd': + case 'D': + case 'r': + case 'R': + length = 8; + break; + + case 'x': + case 'X': + length = 12; + break; + + case 'p': + case 'P': + length = 12; + break; + + default: + as_bad (_("unknown floating type type '%c'"), float_type); + return -1; + } + + /* It would be nice if we could go through expression to parse the + hex constant, but if we get a bignum it's a pain to sort it into + the buffer correctly. */ + i = 0; + while (hex_p (*input_line_pointer) || *input_line_pointer == '_') + { + int d; + + /* The MRI assembler accepts arbitrary underscores strewn about + through the hex constant, so we ignore them as well. */ + if (*input_line_pointer == '_') + { + ++input_line_pointer; + continue; + } + + if (i >= length) + { + as_warn (_("floating point constant too large")); + return -1; + } + d = hex_value (*input_line_pointer) << 4; + ++input_line_pointer; + while (*input_line_pointer == '_') + ++input_line_pointer; + if (hex_p (*input_line_pointer)) + { + d += hex_value (*input_line_pointer); + ++input_line_pointer; + } + if (target_big_endian) + bytes[i] = d; + else + bytes[length - i - 1] = d; + ++i; + } + + if (i < length) + { + if (target_big_endian) + memset (bytes + i, 0, length - i); + else + memset (bytes, 0, length - i); + } + + return length; +} + +/* float_cons() + + CONStruct some more frag chars of .floats .ffloats etc. + Makes 0 or more new frags. + If need_pass_2 == 1, no frags are emitted. + This understands only floating literals, not expressions. Sorry. + + A floating constant is defined by atof_generic(), except it is preceded + by 0d 0f 0g or 0h. After observing the STRANGE way my BSD AS does its + reading, I decided to be incompatible. This always tries to give you + rounded bits to the precision of the pseudo-op. Former AS did premature + truncation, restored noisy bits instead of trailing 0s AND gave you + a choice of 2 flavours of noise according to which of 2 floating-point + scanners you directed AS to use. + + In: input_line_pointer->whitespace before, or '0' of flonum. */ + +void +float_cons (/* Clobbers input_line-pointer, checks end-of-line. */ + int float_type /* 'f':.ffloat ... 'F':.float ... */) +{ + char *p; + int length; /* Number of chars in an object. */ + char *err; /* Error from scanning floating literal. */ + char temp[MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT]; + + if (is_it_end_of_statement ()) + { + demand_empty_rest_of_line (); + return; + } + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + +#ifdef md_cons_align + md_cons_align (1); +#endif + + do + { + /* input_line_pointer->1st char of a flonum (we hope!). */ + SKIP_WHITESPACE (); + + /* Skip any 0{letter} that may be present. Don't even check if the + letter is legal. Someone may invent a "z" format and this routine + has no use for such information. Lusers beware: you get + diagnostics if your input is ill-conditioned. */ + if (input_line_pointer[0] == '0' + && ISALPHA (input_line_pointer[1])) + input_line_pointer += 2; + + /* Accept :xxxx, where the x's are hex digits, for a floating + point with the exact digits specified. */ + if (input_line_pointer[0] == ':') + { + ++input_line_pointer; + length = hex_float (float_type, temp); + if (length < 0) + { + ignore_rest_of_line (); + return; + } + } + else + { + err = md_atof (float_type, temp, &length); + know (length <= MAXIMUM_NUMBER_OF_CHARS_FOR_FLOAT); + know (err != NULL || length > 0); + if (err) + { + as_bad (_("bad floating literal: %s"), err); + ignore_rest_of_line (); + return; + } + } + + if (!need_pass_2) + { + int count; + + count = 1; + +#ifdef REPEAT_CONS_EXPRESSIONS + if (*input_line_pointer == ':') + { + expressionS count_exp; + + ++input_line_pointer; + expression (&count_exp); + + if (count_exp.X_op != O_constant + || count_exp.X_add_number <= 0) + as_warn (_("unresolvable or nonpositive repeat count; using 1")); + else + count = count_exp.X_add_number; + } +#endif + + while (--count >= 0) + { + p = frag_more (length); + memcpy (p, temp, (unsigned int) length); + } + } + SKIP_WHITESPACE (); + } + while (*input_line_pointer++ == ','); + + /* Put terminator back into stream. */ + --input_line_pointer; + demand_empty_rest_of_line (); +} + +/* Return the size of a LEB128 value. */ + +static inline int +sizeof_sleb128 (offsetT value) +{ + int size = 0; + unsigned byte; + + do + { + byte = (value & 0x7f); + /* Sadly, we cannot rely on typical arithmetic right shift behaviour. + Fortunately, we can structure things so that the extra work reduces + to a noop on systems that do things "properly". */ + value = (value >> 7) | ~(-(offsetT)1 >> 7); + size += 1; + } + while (!(((value == 0) && ((byte & 0x40) == 0)) + || ((value == -1) && ((byte & 0x40) != 0)))); + + return size; +} + +static inline int +sizeof_uleb128 (valueT value) +{ + int size = 0; + + do + { + value >>= 7; + size += 1; + } + while (value != 0); + + return size; +} + +int +sizeof_leb128 (valueT value, int sign) +{ + if (sign) + return sizeof_sleb128 ((offsetT) value); + else + return sizeof_uleb128 (value); +} + +/* Output a LEB128 value. */ + +static inline int +output_sleb128 (char *p, offsetT value) +{ + char *orig = p; + int more; + + do + { + unsigned byte = (value & 0x7f); + + /* Sadly, we cannot rely on typical arithmetic right shift behaviour. + Fortunately, we can structure things so that the extra work reduces + to a noop on systems that do things "properly". */ + value = (value >> 7) | ~(-(offsetT)1 >> 7); + + more = !((((value == 0) && ((byte & 0x40) == 0)) + || ((value == -1) && ((byte & 0x40) != 0)))); + if (more) + byte |= 0x80; + + *p++ = byte; + } + while (more); + + return p - orig; +} + +static inline int +output_uleb128 (char *p, valueT value) +{ + char *orig = p; + + do + { + unsigned byte = (value & 0x7f); + value >>= 7; + if (value != 0) + /* More bytes to follow. */ + byte |= 0x80; + + *p++ = byte; + } + while (value != 0); + + return p - orig; +} + +int +output_leb128 (char *p, valueT value, int sign) +{ + if (sign) + return output_sleb128 (p, (offsetT) value); + else + return output_uleb128 (p, value); +} + +/* Do the same for bignums. We combine sizeof with output here in that + we don't output for NULL values of P. It isn't really as critical as + for "normal" values that this be streamlined. */ + +static inline int +output_big_sleb128 (char *p, LITTLENUM_TYPE *bignum, int size) +{ + char *orig = p; + valueT val = 0; + int loaded = 0; + unsigned byte; + + /* Strip leading sign extensions off the bignum. */ + while (size > 1 + && bignum[size - 1] == LITTLENUM_MASK + && bignum[size - 2] > LITTLENUM_MASK / 2) + size--; + + do + { + /* OR in the next part of the littlenum. */ + val |= (*bignum << loaded); + loaded += LITTLENUM_NUMBER_OF_BITS; + size--; + bignum++; + + /* Add bytes until there are less than 7 bits left in VAL + or until every non-sign bit has been written. */ + do + { + byte = val & 0x7f; + loaded -= 7; + val >>= 7; + if (size > 0 + || val != ((byte & 0x40) == 0 ? 0 : ((valueT) 1 << loaded) - 1)) + byte |= 0x80; + + if (orig) + *p = byte; + p++; + } + while ((byte & 0x80) != 0 && loaded >= 7); + } + while (size > 0); + + /* Mop up any left-over bits (of which there will be less than 7). */ + if ((byte & 0x80) != 0) + { + /* Sign-extend VAL. */ + if (val & (1 << (loaded - 1))) + val |= ~0 << loaded; + if (orig) + *p = val & 0x7f; + p++; + } + + return p - orig; +} + +static inline int +output_big_uleb128 (char *p, LITTLENUM_TYPE *bignum, int size) +{ + char *orig = p; + valueT val = 0; + int loaded = 0; + unsigned byte; + + /* Strip leading zeros off the bignum. */ + /* XXX: Is this needed? */ + while (size > 0 && bignum[size - 1] == 0) + size--; + + do + { + if (loaded < 7 && size > 0) + { + val |= (*bignum << loaded); + loaded += 8 * CHARS_PER_LITTLENUM; + size--; + bignum++; + } + + byte = val & 0x7f; + loaded -= 7; + val >>= 7; + + if (size > 0 || val) + byte |= 0x80; + + if (orig) + *p = byte; + p++; + } + while (byte & 0x80); + + return p - orig; +} + +static int +output_big_leb128 (char *p, LITTLENUM_TYPE *bignum, int size, int sign) +{ + if (sign) + return output_big_sleb128 (p, bignum, size); + else + return output_big_uleb128 (p, bignum, size); +} + +/* Generate the appropriate fragments for a given expression to emit a + leb128 value. */ + +static void +emit_leb128_expr (expressionS *exp, int sign) +{ + operatorT op = exp->X_op; + unsigned int nbytes; + + if (op == O_absent || op == O_illegal) + { + as_warn (_("zero assumed for missing expression")); + exp->X_add_number = 0; + op = O_constant; + } + else if (op == O_big && exp->X_add_number <= 0) + { + as_bad (_("floating point number invalid")); + exp->X_add_number = 0; + op = O_constant; + } + else if (op == O_register) + { + as_warn (_("register value used as expression")); + op = O_constant; + } + else if (op == O_constant + && sign + && (exp->X_add_number < 0) == !exp->X_extrabit) + { + /* We're outputting a signed leb128 and the sign of X_add_number + doesn't reflect the sign of the original value. Convert EXP + to a correctly-extended bignum instead. */ + convert_to_bignum (exp, exp->X_extrabit); + op = O_big; + } + + /* Let check_eh_frame know that data is being emitted. nbytes == -1 is + a signal that this is leb128 data. It shouldn't optimize this away. */ + nbytes = (unsigned int) -1; + if (check_eh_frame (exp, &nbytes)) + abort (); + + /* Let the backend know that subsequent data may be byte aligned. */ +#ifdef md_cons_align + md_cons_align (1); +#endif + + if (op == O_constant) + { + /* If we've got a constant, emit the thing directly right now. */ + + valueT value = exp->X_add_number; + int size; + char *p; + + size = sizeof_leb128 (value, sign); + p = frag_more (size); + output_leb128 (p, value, sign); + } + else if (op == O_big) + { + /* O_big is a different sort of constant. */ + + int size; + char *p; + + size = output_big_leb128 (NULL, generic_bignum, exp->X_add_number, sign); + p = frag_more (size); + output_big_leb128 (p, generic_bignum, exp->X_add_number, sign); + } + else + { + /* Otherwise, we have to create a variable sized fragment and + resolve things later. */ + + frag_var (rs_leb128, sizeof_uleb128 (~(valueT) 0), 0, sign, + make_expr_symbol (exp), 0, (char *) NULL); + } +} + +/* Parse the .sleb128 and .uleb128 pseudos. */ + +void +s_leb128 (int sign) +{ + expressionS exp; + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + + do + { + expression (&exp); + emit_leb128_expr (&exp, sign); + } + while (*input_line_pointer++ == ','); + + input_line_pointer--; + demand_empty_rest_of_line (); +} + +static void +stringer_append_char (int c, int bitsize) +{ + if (!target_big_endian) + FRAG_APPEND_1_CHAR (c); + + switch (bitsize) + { + case 64: + FRAG_APPEND_1_CHAR (0); + FRAG_APPEND_1_CHAR (0); + FRAG_APPEND_1_CHAR (0); + FRAG_APPEND_1_CHAR (0); + /* Fall through. */ + case 32: + FRAG_APPEND_1_CHAR (0); + FRAG_APPEND_1_CHAR (0); + /* Fall through. */ + case 16: + FRAG_APPEND_1_CHAR (0); + /* Fall through. */ + case 8: + break; + default: + /* Called with invalid bitsize argument. */ + abort (); + break; + } + if (target_big_endian) + FRAG_APPEND_1_CHAR (c); +} + +/* Worker to do .ascii etc statements. + Reads 0 or more ',' separated, double-quoted strings. + Caller should have checked need_pass_2 is FALSE because we don't + check it. + Checks for end-of-line. + BITS_APPENDZERO says how many bits are in a target char. + The bottom bit is set if a NUL char should be appended to the strings. */ + +void +stringer (int bits_appendzero) +{ + const int bitsize = bits_appendzero & ~7; + const int append_zero = bits_appendzero & 1; + unsigned int c; +#if !defined(NO_LISTING) && defined (OBJ_ELF) + char *start; +#endif + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + +#ifdef md_cons_align + md_cons_align (1); +#endif + + /* The following awkward logic is to parse ZERO or more strings, + comma separated. Recall a string expression includes spaces + before the opening '\"' and spaces after the closing '\"'. + We fake a leading ',' if there is (supposed to be) + a 1st, expression. We keep demanding expressions for each ','. */ + if (is_it_end_of_statement ()) + { + c = 0; /* Skip loop. */ + ++input_line_pointer; /* Compensate for end of loop. */ + } + else + { + c = ','; /* Do loop. */ + } + /* If we have been switched into the abs_section then we + will not have an obstack onto which we can hang strings. */ + if (now_seg == absolute_section) + { + as_bad (_("strings must be placed into a section")); + c = 0; + ignore_rest_of_line (); + } + + while (c == ',' || c == '<' || c == '"') + { + SKIP_WHITESPACE (); + switch (*input_line_pointer) + { + case '\"': + ++input_line_pointer; /*->1st char of string. */ +#if !defined(NO_LISTING) && defined (OBJ_ELF) + start = input_line_pointer; +#endif + + while (is_a_char (c = next_char_of_string ())) + stringer_append_char (c, bitsize); + + if (append_zero) + stringer_append_char (0, bitsize); + + know (input_line_pointer[-1] == '\"'); + +#if !defined(NO_LISTING) && defined (OBJ_ELF) + /* In ELF, when gcc is emitting DWARF 1 debugging output, it + will emit .string with a filename in the .debug section + after a sequence of constants. See the comment in + emit_expr for the sequence. emit_expr will set + dwarf_file_string to non-zero if this string might be a + source file name. */ + if (strcmp (segment_name (now_seg), ".debug") != 0) + dwarf_file_string = 0; + else if (dwarf_file_string) + { + c = input_line_pointer[-1]; + input_line_pointer[-1] = '\0'; + listing_source_file (start); + input_line_pointer[-1] = c; + } +#endif + + break; + case '<': + input_line_pointer++; + c = get_single_number (); + stringer_append_char (c, bitsize); + if (*input_line_pointer != '>') + as_bad (_("expected ")); + + input_line_pointer++; + break; + case ',': + input_line_pointer++; + break; + } + SKIP_WHITESPACE (); + c = *input_line_pointer; + } + + demand_empty_rest_of_line (); +} + +/* FIXME-SOMEDAY: I had trouble here on characters with the + high bits set. We'll probably also have trouble with + multibyte chars, wide chars, etc. Also be careful about + returning values bigger than 1 byte. xoxorich. */ + +unsigned int +next_char_of_string (void) +{ + unsigned int c; + + c = *input_line_pointer++ & CHAR_MASK; + switch (c) + { + case '\"': + c = NOT_A_CHAR; + break; + + case '\n': + as_warn (_("unterminated string; newline inserted")); + bump_line_counters (); + break; + +#ifndef NO_STRING_ESCAPES + case '\\': + switch (c = *input_line_pointer++) + { + case 'b': + c = '\b'; + break; + + case 'f': + c = '\f'; + break; + + case 'n': + c = '\n'; + break; + + case 'r': + c = '\r'; + break; + + case 't': + c = '\t'; + break; + + case 'v': + c = '\013'; + break; + + case '\\': + case '"': + break; /* As itself. */ + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + long number; + int i; + + for (i = 0, number = 0; + ISDIGIT (c) && i < 3; + c = *input_line_pointer++, i++) + { + number = number * 8 + c - '0'; + } + + c = number & 0xff; + } + --input_line_pointer; + break; + + case 'x': + case 'X': + { + long number; + + number = 0; + c = *input_line_pointer++; + while (ISXDIGIT (c)) + { + if (ISDIGIT (c)) + number = number * 16 + c - '0'; + else if (ISUPPER (c)) + number = number * 16 + c - 'A' + 10; + else + number = number * 16 + c - 'a' + 10; + c = *input_line_pointer++; + } + c = number & 0xff; + --input_line_pointer; + } + break; + + case '\n': + /* To be compatible with BSD 4.2 as: give the luser a linefeed!! */ + as_warn (_("unterminated string; newline inserted")); + c = '\n'; + bump_line_counters (); + break; + + default: + +#ifdef ONLY_STANDARD_ESCAPES + as_bad (_("bad escaped character in string")); + c = '?'; +#endif /* ONLY_STANDARD_ESCAPES */ + + break; + } + break; +#endif /* ! defined (NO_STRING_ESCAPES) */ + + default: + break; + } + return (c); +} + +static segT +get_segmented_expression (expressionS *expP) +{ + segT retval; + + retval = expression (expP); + if (expP->X_op == O_illegal + || expP->X_op == O_absent + || expP->X_op == O_big) + { + as_bad (_("expected address expression")); + expP->X_op = O_constant; + expP->X_add_number = 0; + retval = absolute_section; + } + return retval; +} + +static segT +get_known_segmented_expression (expressionS *expP) +{ + segT retval = get_segmented_expression (expP); + + if (retval == undefined_section) + { + /* There is no easy way to extract the undefined symbol from the + expression. */ + if (expP->X_add_symbol != NULL + && S_GET_SEGMENT (expP->X_add_symbol) != expr_section) + as_warn (_("symbol \"%s\" undefined; zero assumed"), + S_GET_NAME (expP->X_add_symbol)); + else + as_warn (_("some symbol undefined; zero assumed")); + retval = absolute_section; + expP->X_op = O_constant; + expP->X_add_number = 0; + } + return retval; +} + +char /* Return terminator. */ +get_absolute_expression_and_terminator (long *val_pointer /* Return value of expression. */) +{ + /* FIXME: val_pointer should probably be offsetT *. */ + *val_pointer = (long) get_absolute_expression (); + return (*input_line_pointer++); +} + +/* Like demand_copy_string, but return NULL if the string contains any '\0's. + Give a warning if that happens. */ + +char * +demand_copy_C_string (int *len_pointer) +{ + char *s; + + if ((s = demand_copy_string (len_pointer)) != 0) + { + int len; + + for (len = *len_pointer; len > 0; len--) + { + if (*s == 0) + { + s = 0; + len = 1; + *len_pointer = 0; + as_bad (_("this string may not contain \'\\0\'")); + } + } + } + + return s; +} + +/* Demand string, but return a safe (=private) copy of the string. + Return NULL if we can't read a string here. */ + +char * +demand_copy_string (int *lenP) +{ + unsigned int c; + int len; + char *retval; + + len = 0; + SKIP_WHITESPACE (); + if (*input_line_pointer == '\"') + { + input_line_pointer++; /* Skip opening quote. */ + + while (is_a_char (c = next_char_of_string ())) + { + obstack_1grow (¬es, c); + len++; + } + /* JF this next line is so demand_copy_C_string will return a + null terminated string. */ + obstack_1grow (¬es, '\0'); + retval = (char *) obstack_finish (¬es); + } + else + { + as_bad (_("missing string")); + retval = NULL; + ignore_rest_of_line (); + } + *lenP = len; + return (retval); +} + +/* In: Input_line_pointer->next character. + + Do: Skip input_line_pointer over all whitespace. + + Out: 1 if input_line_pointer->end-of-line. */ + +int +is_it_end_of_statement (void) +{ + SKIP_WHITESPACE (); + return (is_end_of_line[(unsigned char) *input_line_pointer]); +} + +void +equals (char *sym_name, int reassign) +{ + char *stop = NULL; + char stopc = 0; + + input_line_pointer++; + if (*input_line_pointer == '=') + input_line_pointer++; + if (reassign < 0 && *input_line_pointer == '=') + input_line_pointer++; + + while (*input_line_pointer == ' ' || *input_line_pointer == '\t') + input_line_pointer++; + + if (flag_mri) + stop = mri_comment_field (&stopc); + + assign_symbol (sym_name, reassign >= 0 ? !reassign : reassign); + + if (flag_mri) + { + demand_empty_rest_of_line (); + mri_comment_end (stop, stopc); + } +} + +/* .incbin -- include a file verbatim at the current location. */ + +void +s_incbin (int x ATTRIBUTE_UNUSED) +{ + FILE * binfile; + char * path; + char * filename; + char * binfrag; + long skip = 0; + long count = 0; + long bytes; + int len; + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + +#ifdef md_cons_align + md_cons_align (1); +#endif + + SKIP_WHITESPACE (); + filename = demand_copy_string (& len); + if (filename == NULL) + return; + + SKIP_WHITESPACE (); + + /* Look for optional skip and count. */ + if (* input_line_pointer == ',') + { + ++ input_line_pointer; + skip = get_absolute_expression (); + + SKIP_WHITESPACE (); + + if (* input_line_pointer == ',') + { + ++ input_line_pointer; + + count = get_absolute_expression (); + if (count == 0) + as_warn (_(".incbin count zero, ignoring `%s'"), filename); + + SKIP_WHITESPACE (); + } + } + + demand_empty_rest_of_line (); + + /* Try opening absolute path first, then try include dirs. */ + binfile = fopen (filename, FOPEN_RB); + if (binfile == NULL) + { + int i; + + path = (char *) xmalloc ((unsigned long) len + include_dir_maxlen + 5); + + for (i = 0; i < include_dir_count; i++) + { + sprintf (path, "%s/%s", include_dirs[i], filename); + + binfile = fopen (path, FOPEN_RB); + if (binfile != NULL) + break; + } + + if (binfile == NULL) + as_bad (_("file not found: %s"), filename); + } + else + path = xstrdup (filename); + + if (binfile) + { + long file_len; + + register_dependency (path); + + /* Compute the length of the file. */ + if (fseek (binfile, 0, SEEK_END) != 0) + { + as_bad (_("seek to end of .incbin file failed `%s'"), path); + goto done; + } + file_len = ftell (binfile); + + /* If a count was not specified use the remainder of the file. */ + if (count == 0) + count = file_len - skip; + + if (skip < 0 || count < 0 || file_len < 0 || skip + count > file_len) + { + as_bad (_("skip (%ld) or count (%ld) invalid for file size (%ld)"), + skip, count, file_len); + goto done; + } + + if (fseek (binfile, skip, SEEK_SET) != 0) + { + as_bad (_("could not skip to %ld in file `%s'"), skip, path); + goto done; + } + + /* Allocate frag space and store file contents in it. */ + binfrag = frag_more (count); + + bytes = fread (binfrag, 1, count, binfile); + if (bytes < count) + as_warn (_("truncated file `%s', %ld of %ld bytes read"), + path, bytes, count); + } +done: + if (binfile != NULL) + fclose (binfile); + if (path) + free (path); +} + +/* .include -- include a file at this point. */ + +void +s_include (int arg ATTRIBUTE_UNUSED) +{ + char *filename; + int i; + FILE *try_file; + char *path; + + if (!flag_m68k_mri) + { + filename = demand_copy_string (&i); + if (filename == NULL) + { + /* demand_copy_string has already printed an error and + called ignore_rest_of_line. */ + return; + } + } + else + { + SKIP_WHITESPACE (); + i = 0; + while (!is_end_of_line[(unsigned char) *input_line_pointer] + && *input_line_pointer != ' ' + && *input_line_pointer != '\t') + { + obstack_1grow (¬es, *input_line_pointer); + ++input_line_pointer; + ++i; + } + + obstack_1grow (¬es, '\0'); + filename = (char *) obstack_finish (¬es); + while (!is_end_of_line[(unsigned char) *input_line_pointer]) + ++input_line_pointer; + } + + demand_empty_rest_of_line (); + path = (char *) xmalloc ((unsigned long) i + + include_dir_maxlen + 5 /* slop */ ); + + for (i = 0; i < include_dir_count; i++) + { + strcpy (path, include_dirs[i]); + strcat (path, "/"); + strcat (path, filename); + if (0 != (try_file = fopen (path, FOPEN_RT))) + { + fclose (try_file); + goto gotit; + } + } + + free (path); + path = filename; +gotit: + /* malloc Storage leak when file is found on path. FIXME-SOMEDAY. */ + register_dependency (path); + input_scrub_insert_file (path); +} + +void +add_include_dir (char *path) +{ + int i; + + if (include_dir_count == 0) + { + include_dirs = (char **) xmalloc (2 * sizeof (*include_dirs)); + include_dirs[0] = "."; /* Current dir. */ + include_dir_count = 2; + } + else + { + include_dir_count++; + include_dirs = + (char **) realloc (include_dirs, + include_dir_count * sizeof (*include_dirs)); + } + + include_dirs[include_dir_count - 1] = path; /* New one. */ + + i = strlen (path); + if (i > include_dir_maxlen) + include_dir_maxlen = i; +} + +/* Output debugging information to denote the source file. */ + +static void +generate_file_debug (void) +{ + if (debug_type == DEBUG_STABS) + stabs_generate_asm_file (); +} + +/* Output line number debugging information for the current source line. */ + +void +generate_lineno_debug (void) +{ + switch (debug_type) + { + case DEBUG_UNSPECIFIED: + case DEBUG_NONE: + case DEBUG_DWARF: + break; + case DEBUG_STABS: + stabs_generate_asm_lineno (); + break; + case DEBUG_ECOFF: + ecoff_generate_asm_lineno (); + break; + case DEBUG_DWARF2: + /* ??? We could here indicate to dwarf2dbg.c that something + has changed. However, since there is additional backend + support that is required (calling dwarf2_emit_insn), we + let dwarf2dbg.c call as_where on its own. */ + break; + } +} + +/* Output debugging information to mark a function entry point or end point. + END_P is zero for .func, and non-zero for .endfunc. */ + +void +s_func (int end_p) +{ + do_s_func (end_p, NULL); +} + +/* Subroutine of s_func so targets can choose a different default prefix. + If DEFAULT_PREFIX is NULL, use the target's "leading char". */ + +static void +do_s_func (int end_p, const char *default_prefix) +{ + /* Record the current function so that we can issue an error message for + misplaced .func,.endfunc, and also so that .endfunc needs no + arguments. */ + static char *current_name; + static char *current_label; + + if (end_p) + { + if (current_name == NULL) + { + as_bad (_("missing .func")); + ignore_rest_of_line (); + return; + } + + if (debug_type == DEBUG_STABS) + stabs_generate_asm_endfunc (current_name, current_label); + + current_name = current_label = NULL; + } + else /* ! end_p */ + { + char *name, *label; + char delim1, delim2; + + if (current_name != NULL) + { + as_bad (_(".endfunc missing for previous .func")); + ignore_rest_of_line (); + return; + } + + name = input_line_pointer; + delim1 = get_symbol_end (); + name = xstrdup (name); + *input_line_pointer = delim1; + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + { + if (default_prefix) + { + if (asprintf (&label, "%s%s", default_prefix, name) == -1) + as_fatal ("%s", xstrerror (errno)); + } + else + { + char leading_char = bfd_get_symbol_leading_char (stdoutput); + /* Missing entry point, use function's name with the leading + char prepended. */ + if (leading_char) + { + if (asprintf (&label, "%c%s", leading_char, name) == -1) + as_fatal ("%s", xstrerror (errno)); + } + else + label = name; + } + } + else + { + ++input_line_pointer; + SKIP_WHITESPACE (); + label = input_line_pointer; + delim2 = get_symbol_end (); + label = xstrdup (label); + *input_line_pointer = delim2; + } + + if (debug_type == DEBUG_STABS) + stabs_generate_asm_func (name, label); + + current_name = name; + current_label = label; + } + + demand_empty_rest_of_line (); +} + +#ifdef HANDLE_BUNDLE + +void +s_bundle_align_mode (int arg ATTRIBUTE_UNUSED) +{ + unsigned int align = get_absolute_expression (); + SKIP_WHITESPACE (); + demand_empty_rest_of_line (); + + if (align > (unsigned int) TC_ALIGN_LIMIT) + as_fatal (_(".bundle_align_mode alignment too large (maximum %u)"), + (unsigned int) TC_ALIGN_LIMIT); + + if (bundle_lock_frag != NULL) + { + as_bad (_("cannot change .bundle_align_mode inside .bundle_lock")); + return; + } + + bundle_align_p2 = align; +} + +void +s_bundle_lock (int arg ATTRIBUTE_UNUSED) +{ + demand_empty_rest_of_line (); + + if (bundle_align_p2 == 0) + { + as_bad (_(".bundle_lock is meaningless without .bundle_align_mode")); + return; + } + + if (bundle_lock_depth == 0) + { + bundle_lock_frchain = frchain_now; + bundle_lock_frag = start_bundle (); + } + ++bundle_lock_depth; +} + +void +s_bundle_unlock (int arg ATTRIBUTE_UNUSED) +{ + unsigned int size; + + demand_empty_rest_of_line (); + + if (bundle_lock_frag == NULL) + { + as_bad (_(".bundle_unlock without preceding .bundle_lock")); + return; + } + + gas_assert (bundle_align_p2 > 0); + + gas_assert (bundle_lock_depth > 0); + if (--bundle_lock_depth > 0) + return; + + size = pending_bundle_size (bundle_lock_frag); + + if (size > (1U << bundle_align_p2)) + as_bad (_(".bundle_lock sequence is %u bytes, but bundle size only %u"), + size, 1 << bundle_align_p2); + else + finish_bundle (bundle_lock_frag, size); + + bundle_lock_frag = NULL; + bundle_lock_frchain = NULL; +} + +#endif /* HANDLE_BUNDLE */ + +void +s_ignore (int arg ATTRIBUTE_UNUSED) +{ + ignore_rest_of_line (); +} + +void +read_print_statistics (FILE *file) +{ + hash_print_statistics (file, "pseudo-op table", po_hash); +} + +/* Inserts the given line into the input stream. + + This call avoids macro/conditionals nesting checking, since the contents of + the line are assumed to replace the contents of a line already scanned. + + An appropriate use of this function would be substitution of input lines when + called by md_start_line_hook(). The given line is assumed to already be + properly scrubbed. */ + +void +input_scrub_insert_line (const char *line) +{ + sb newline; + size_t len = strlen (line); + sb_build (&newline, len); + sb_add_buffer (&newline, line, len); + input_scrub_include_sb (&newline, input_line_pointer, 0); + sb_kill (&newline); + buffer_limit = input_scrub_next_buffer (&input_line_pointer); +} + +/* Insert a file into the input stream; the path must resolve to an actual + file; no include path searching or dependency registering is performed. */ + +void +input_scrub_insert_file (char *path) +{ + input_scrub_include_file (path, input_line_pointer); + buffer_limit = input_scrub_next_buffer (&input_line_pointer); +} + +/* Find the end of a line, considering quotation and escaping of quotes. */ + +#if !defined(TC_SINGLE_QUOTE_STRINGS) && defined(SINGLE_QUOTE_STRINGS) +# define TC_SINGLE_QUOTE_STRINGS 1 +#endif + +static char * +_find_end_of_line (char *s, int mri_string, int insn ATTRIBUTE_UNUSED, + int in_macro) +{ + char inquote = '\0'; + int inescape = 0; + + while (!is_end_of_line[(unsigned char) *s] + || (inquote && !ISCNTRL (*s)) + || (inquote == '\'' && flag_mri) +#ifdef TC_EOL_IN_INSN + || (insn && TC_EOL_IN_INSN (s)) +#endif + /* PR 6926: When we are parsing the body of a macro the sequence + \@ is special - it refers to the invocation count. If the @ + character happens to be registered as a line-separator character + by the target, then the is_end_of_line[] test above will have + returned true, but we need to ignore the line separating + semantics in this particular case. */ + || (in_macro && inescape && *s == '@') + ) + { + if (mri_string && *s == '\'') + inquote ^= *s; + else if (inescape) + inescape = 0; + else if (*s == '\\') + inescape = 1; + else if (!inquote + ? *s == '"' +#ifdef TC_SINGLE_QUOTE_STRINGS + || (TC_SINGLE_QUOTE_STRINGS && *s == '\'') +#endif + : *s == inquote) + inquote ^= *s; + ++s; + } + if (inquote) + as_warn (_("missing closing `%c'"), inquote); + if (inescape) + as_warn (_("stray `\\'")); + return s; +} + +char * +find_end_of_line (char *s, int mri_string) +{ + return _find_end_of_line (s, mri_string, 0, 0); +} diff --git a/contrib/toolchain/binutils/gas/read.h b/contrib/toolchain/binutils/gas/read.h new file mode 100644 index 0000000000..81b958ac14 --- /dev/null +++ b/contrib/toolchain/binutils/gas/read.h @@ -0,0 +1,193 @@ +/* read.h - of read.c + Copyright 1986, 1990, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, + 2000, 2001, 2002, 2003, 2004, 2005, 2007, 2008, 2009, 2012 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +extern char *input_line_pointer; /* -> char we are parsing now. */ + +/* Define to make whitespace be allowed in many syntactically + unnecessary places. Normally undefined. For compatibility with + ancient GNU cc. */ +/* #undef PERMIT_WHITESPACE */ +#define PERMIT_WHITESPACE + +#ifdef PERMIT_WHITESPACE +#define SKIP_WHITESPACE() \ + ((*input_line_pointer == ' ') ? ++input_line_pointer : 0) +#else +#define SKIP_WHITESPACE() know(*input_line_pointer != ' ' ) +#endif + +#define LEX_NAME (1) /* may continue a name */ +#define LEX_BEGIN_NAME (2) /* may begin a name */ +#define LEX_END_NAME (4) /* ends a name */ + +#define is_name_beginner(c) \ + ( lex_type[(unsigned char) (c)] & LEX_BEGIN_NAME ) +#define is_part_of_name(c) \ + ( lex_type[(unsigned char) (c)] & LEX_NAME ) +#define is_name_ender(c) \ + ( lex_type[(unsigned char) (c)] & LEX_END_NAME ) + +#ifndef is_a_char +#define CHAR_MASK (0xff) +#define NOT_A_CHAR (CHAR_MASK+1) +#define is_a_char(c) (((unsigned) (c)) <= CHAR_MASK) +#endif /* is_a_char() */ + +extern char lex_type[]; +extern char is_end_of_line[]; + +extern int is_it_end_of_statement (void); +extern char *find_end_of_line (char *, int); + +extern int target_big_endian; + +/* These are initialized by the CPU specific target files (tc-*.c). */ +extern const char comment_chars[]; +extern const char line_comment_chars[]; +extern const char line_separator_chars[]; + +/* Table of -I directories. */ +extern char **include_dirs; +extern int include_dir_count; +extern int include_dir_maxlen; + +/* The offset in the absolute section. */ +extern addressT abs_section_offset; + +/* The label on a line, used by some of the pseudo-ops. */ +extern symbolS *line_label; + +/* This is used to support MRI common sections. */ +extern symbolS *mri_common_symbol; + +/* True if a stabs line debug statement is currently being emitted. */ +extern int outputting_stabs_line_debug; + +/* Possible arguments to .linkonce. */ +enum linkonce_type { + LINKONCE_UNSET = 0, + LINKONCE_DISCARD, + LINKONCE_ONE_ONLY, + LINKONCE_SAME_SIZE, + LINKONCE_SAME_CONTENTS +}; + +#ifndef TC_CASE_SENSITIVE +extern char original_case_string[]; +#endif + +extern void pop_insert (const pseudo_typeS *); +extern unsigned int get_stab_string_offset + (const char *string, const char *stabstr_secname); +extern void aout_process_stab (int, const char *, int, int, int); +extern char *demand_copy_string (int *lenP); +extern char *demand_copy_C_string (int *len_pointer); +extern char get_absolute_expression_and_terminator (long *val_pointer); +extern offsetT get_absolute_expression (void); +extern unsigned int next_char_of_string (void); +extern void s_mri_sect (char *); +extern char *mri_comment_field (char *); +extern void mri_comment_end (char *, int); +extern void add_include_dir (char *path); +extern void cons (int nbytes); +extern void demand_empty_rest_of_line (void); +extern void emit_expr (expressionS *exp, unsigned int nbytes); +extern void emit_expr_fix (expressionS *, unsigned int, fragS *, char *); +extern void equals (char *sym_name, int reassign); +extern void float_cons (int float_type); +extern void ignore_rest_of_line (void); +#define discard_rest_of_line ignore_rest_of_line +extern int output_leb128 (char *, valueT, int sign); +extern void pseudo_set (symbolS * symbolP); +extern void read_a_source_file (char *name); +extern void read_begin (void); +extern void read_print_statistics (FILE *); +extern int sizeof_leb128 (valueT, int sign); +extern void stabs_generate_asm_file (void); +extern void stabs_generate_asm_lineno (void); +extern void stabs_generate_asm_func (const char *, const char *); +extern void stabs_generate_asm_endfunc (const char *, const char *); +extern void do_repeat (int,const char *,const char *); +extern void do_repeat_with_expander (int, const char *, const char *, const char *); +extern void end_repeat (int); +extern void do_parse_cons_expression (expressionS *, int); + +extern void generate_lineno_debug (void); + +extern void s_abort (int) ATTRIBUTE_NORETURN; +extern void s_align_bytes (int arg); +extern void s_align_ptwo (int); +extern void bss_alloc (symbolS *, addressT, int); +extern offsetT parse_align (int); +extern symbolS *s_comm_internal (int, symbolS *(*) (int, symbolS *, addressT)); +extern symbolS *s_lcomm_internal (int, symbolS *, addressT); +extern void s_app_file_string (char *, int); +extern void s_app_file (int); +extern void s_app_line (int); +extern void s_bundle_align_mode (int); +extern void s_bundle_lock (int); +extern void s_bundle_unlock (int); +extern void s_comm (int); +extern void s_data (int); +extern void s_desc (int); +extern void s_else (int arg); +extern void s_elseif (int arg); +extern void s_end (int arg); +extern void s_endif (int arg); +extern void s_err (int); +extern void s_errwarn (int); +extern void s_fail (int); +extern void s_fill (int); +extern void s_float_space (int mult); +extern void s_func (int); +extern void s_globl (int arg); +extern void s_if (int arg); +extern void s_ifb (int arg); +extern void s_ifc (int arg); +extern void s_ifdef (int arg); +extern void s_ifeqs (int arg); +extern void s_ignore (int arg); +extern void s_include (int arg); +extern void s_irp (int arg); +extern void s_lcomm (int needs_align); +extern void s_lcomm_bytes (int needs_align); +extern void s_leb128 (int sign); +extern void s_linkonce (int); +extern void s_lsym (int); +extern void s_macro (int); +extern void s_mexit (int); +extern void s_mri (int); +extern void s_mri_common (int); +extern void s_org (int); +extern void s_print (int); +extern void s_purgem (int); +extern void s_rept (int); +extern void s_set (int); +extern void s_space (int mult); +extern void s_stab (int what); +extern void s_struct (int); +extern void s_text (int); +extern void stringer (int append_zero); +extern void s_xstab (int what); +extern void s_rva (int); +extern void s_incbin (int); +extern void s_weakref (int); diff --git a/contrib/toolchain/binutils/gas/remap.c b/contrib/toolchain/binutils/gas/remap.c new file mode 100644 index 0000000000..ae9b9ddaa9 --- /dev/null +++ b/contrib/toolchain/binutils/gas/remap.c @@ -0,0 +1,91 @@ +/* Remap file names for debug info for GNU assembler. + Copyright 2007 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" +#include "filenames.h" + +/* Structure recording the mapping from source file and directory + names at compile time to those to be embedded in debug + information. */ +typedef struct debug_prefix_map +{ + const char *old_prefix; + const char *new_prefix; + size_t old_len; + size_t new_len; + struct debug_prefix_map *next; +} debug_prefix_map; + +/* Linked list of such structures. */ +debug_prefix_map *debug_prefix_maps; + + +/* Record a debug file prefix mapping. ARG is the argument to + -fdebug-prefix-map and must be of the form OLD=NEW. */ + +void +add_debug_prefix_map (const char *arg) +{ + debug_prefix_map *map; + const char *p; + char *o; + + p = strchr (arg, '='); + if (!p) + { + as_fatal (_("invalid argument '%s' to -fdebug-prefix-map"), arg); + return; + } + map = (struct debug_prefix_map *) xmalloc (sizeof (debug_prefix_map)); + o = xstrdup (arg); + map->old_prefix = o; + map->old_len = p - arg; + o[map->old_len] = 0; + p++; + map->new_prefix = xstrdup (p); + map->new_len = strlen (p); + map->next = debug_prefix_maps; + debug_prefix_maps = map; +} + +/* Perform user-specified mapping of debug filename prefixes. Returns + a newly allocated buffer containing the name corresponding to FILENAME. + It is the caller's responsibility to free the buffer. */ + +const char * +remap_debug_filename (const char *filename) +{ + debug_prefix_map *map; + char *s; + const char *name; + size_t name_len; + + for (map = debug_prefix_maps; map; map = map->next) + if (filename_ncmp (filename, map->old_prefix, map->old_len) == 0) + break; + if (!map) + return xstrdup (filename); + name = filename + map->old_len; + name_len = strlen (name) + 1; + s = (char *) alloca (name_len + map->new_len); + memcpy (s, map->new_prefix, map->new_len); + memcpy (s + map->new_len, name, name_len); + return xstrdup (s); +} diff --git a/contrib/toolchain/binutils/gas/sb.c b/contrib/toolchain/binutils/gas/sb.c new file mode 100644 index 0000000000..7710cbc0c8 --- /dev/null +++ b/contrib/toolchain/binutils/gas/sb.c @@ -0,0 +1,237 @@ +/* sb.c - string buffer manipulation routines + Copyright 1994, 1995, 2000, 2003, 2005, 2006, 2007, 2009, 2012 + Free Software Foundation, Inc. + + Written by Steve and Judy Chamberlain of Cygnus Support, + sac@cygnus.com + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" +#include "sb.h" + +#ifdef HAVE_LIMITS_H +#include +#endif +#ifndef CHAR_BIT +#define CHAR_BIT 8 +#endif + +/* These routines are about manipulating strings. + + They are managed in things called `sb's which is an abbreviation + for string buffers. An sb has to be created, things can be glued + on to it, and at the end of it's life it should be freed. The + contents should never be pointed at whilst it is still growing, + since it could be moved at any time + + eg: + sb_new (&foo); + sb_grow... (&foo,...); + use foo->ptr[*]; + sb_kill (&foo); */ + +/* Buffers start at INIT_ALLOC size, and roughly double each time we + go over the current allocation. MALLOC_OVERHEAD is a guess at the + system malloc overhead. We aim to not waste any memory in the + underlying page/chunk allocated by the system malloc. */ +#define MALLOC_OVERHEAD (2 * sizeof (size_t)) +#define INIT_ALLOC (64 - MALLOC_OVERHEAD - 1) + +static void sb_check (sb *, size_t); + +/* Initializes an sb. */ + +void +sb_build (sb *ptr, size_t size) +{ + ptr->ptr = xmalloc (size + 1); + ptr->max = size; + ptr->len = 0; +} + +void +sb_new (sb *ptr) +{ + sb_build (ptr, INIT_ALLOC); +} + +/* Deallocate the sb at ptr. */ + +void +sb_kill (sb *ptr) +{ + free (ptr->ptr); +} + +/* Add the sb at s to the end of the sb at ptr. */ + +void +sb_add_sb (sb *ptr, sb *s) +{ + sb_check (ptr, s->len); + memcpy (ptr->ptr + ptr->len, s->ptr, s->len); + ptr->len += s->len; +} + +/* Helper for sb_scrub_and_add_sb. */ + +static sb *sb_to_scrub; +static char *scrub_position; +static size_t +scrub_from_sb (char *buf, size_t buflen) +{ + size_t copy; + copy = sb_to_scrub->len - (scrub_position - sb_to_scrub->ptr); + if (copy > buflen) + copy = buflen; + memcpy (buf, scrub_position, copy); + scrub_position += copy; + return copy; +} + +/* Run the sb at s through do_scrub_chars and add the result to the sb + at ptr. */ + +void +sb_scrub_and_add_sb (sb *ptr, sb *s) +{ + sb_to_scrub = s; + scrub_position = s->ptr; + + sb_check (ptr, s->len); + ptr->len += do_scrub_chars (scrub_from_sb, ptr->ptr + ptr->len, s->len); + + sb_to_scrub = 0; + scrub_position = 0; +} + +/* Make sure that the sb at ptr has room for another len characters, + and grow it if it doesn't. */ + +static void +sb_check (sb *ptr, size_t len) +{ + size_t want = ptr->len + len; + + if (want > ptr->max) + { + size_t max; + + want += MALLOC_OVERHEAD + 1; + if ((ssize_t) want < 0) + as_fatal ("string buffer overflow"); +#if GCC_VERSION >= 3004 + max = (size_t) 1 << (CHAR_BIT * sizeof (want) + - (sizeof (want) <= sizeof (long) + ? __builtin_clzl ((long) want) + : __builtin_clzll ((long long) want))); +#else + max = 128; + while (want > max) + max <<= 1; +#endif + max -= MALLOC_OVERHEAD + 1; + ptr->max = max; + ptr->ptr = xrealloc (ptr->ptr, max + 1); + } +} + +/* Make the sb at ptr point back to the beginning. */ + +void +sb_reset (sb *ptr) +{ + ptr->len = 0; +} + +/* Add character c to the end of the sb at ptr. */ + +void +sb_add_char (sb *ptr, size_t c) +{ + sb_check (ptr, 1); + ptr->ptr[ptr->len++] = c; +} + +/* Add null terminated string s to the end of sb at ptr. */ + +void +sb_add_string (sb *ptr, const char *s) +{ + size_t len = strlen (s); + sb_check (ptr, len); + memcpy (ptr->ptr + ptr->len, s, len); + ptr->len += len; +} + +/* Add string at s of length len to sb at ptr */ + +void +sb_add_buffer (sb *ptr, const char *s, size_t len) +{ + sb_check (ptr, len); + memcpy (ptr->ptr + ptr->len, s, len); + ptr->len += len; +} + +/* Write terminating NUL and return string. */ + +char * +sb_terminate (sb *in) +{ + in->ptr[in->len] = 0; + return in->ptr; +} + +/* Start at the index idx into the string in sb at ptr and skip + whitespace. return the index of the first non whitespace character. */ + +size_t +sb_skip_white (size_t idx, sb *ptr) +{ + while (idx < ptr->len + && (ptr->ptr[idx] == ' ' + || ptr->ptr[idx] == '\t')) + idx++; + return idx; +} + +/* Start at the index idx into the sb at ptr. skips whitespace, + a comma and any following whitespace. returns the index of the + next character. */ + +size_t +sb_skip_comma (size_t idx, sb *ptr) +{ + while (idx < ptr->len + && (ptr->ptr[idx] == ' ' + || ptr->ptr[idx] == '\t')) + idx++; + + if (idx < ptr->len + && ptr->ptr[idx] == ',') + idx++; + + while (idx < ptr->len + && (ptr->ptr[idx] == ' ' + || ptr->ptr[idx] == '\t')) + idx++; + + return idx; +} diff --git a/contrib/toolchain/binutils/gas/sb.h b/contrib/toolchain/binutils/gas/sb.h new file mode 100644 index 0000000000..ea010ee34e --- /dev/null +++ b/contrib/toolchain/binutils/gas/sb.h @@ -0,0 +1,71 @@ +/* sb.h - header file for string buffer manipulation routines + Copyright 1994, 1995, 2000, 2003, 2005, 2006, 2007, 2012 + Free Software Foundation, Inc. + + Written by Steve and Judy Chamberlain of Cygnus Support, + sac@cygnus.com + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef SB_H + +#define SB_H + +/* String blocks + + I had a couple of choices when deciding upon this data structure. + gas uses null terminated strings for all its internal work. This + often means that parts of the program that want to examine + substrings have to manipulate the data in the string to do the + right thing (a common operation is to single out a bit of text by + saving away the character after it, nulling it out, operating on + the substring and then replacing the character which was under the + null). This is a pain and I remember a load of problems that I had with + code in gas which almost got this right. Also, it's harder to grow and + allocate null terminated strings efficiently. + + Obstacks provide all the functionality needed, but are too + complicated, hence the sb. + + An sb is allocated by the caller. */ + +typedef struct sb +{ + char *ptr; /* Points to the current block. */ + size_t len; /* How much is used. */ + size_t max; /* The maximum length. */ +} +sb; + +extern void sb_new (sb *); +extern void sb_build (sb *, size_t); +extern void sb_kill (sb *); +extern void sb_add_sb (sb *, sb *); +extern void sb_scrub_and_add_sb (sb *, sb *); +extern void sb_reset (sb *); +extern void sb_add_char (sb *, size_t); +extern void sb_add_string (sb *, const char *); +extern void sb_add_buffer (sb *, const char *, size_t); +extern char *sb_terminate (sb *); +extern size_t sb_skip_white (size_t, sb *); +extern size_t sb_skip_comma (size_t, sb *); + +/* Actually in input-scrub.c. */ +extern void input_scrub_include_sb (sb *, char *, int); + +#endif /* SB_H */ diff --git a/contrib/toolchain/binutils/gas/stabs.c b/contrib/toolchain/binutils/gas/stabs.c new file mode 100644 index 0000000000..e0594fa5d4 --- /dev/null +++ b/contrib/toolchain/binutils/gas/stabs.c @@ -0,0 +1,710 @@ +/* Generic stabs parsing for gas. + Copyright 1989, 1990, 1991, 1993, 1995, 1996, 1997, 1998, 2000, 2001 + 2002, 2003, 2004, 2005, 2007, 2009 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 3, + or (at your option) any later version. + + GAS is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See + the 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#include "as.h" +#include "filenames.h" +#include "obstack.h" +#include "subsegs.h" +#include "ecoff.h" + +/* We need this, despite the apparent object format dependency, since + it defines stab types, which all object formats can use now. */ + +#include "aout/stab_gnu.h" + +/* Holds whether the assembler is generating stabs line debugging + information or not. Potentially used by md_cleanup function. */ + +int outputting_stabs_line_debug = 0; + +static void s_stab_generic (int, char *, char *); +static void generate_asm_file (int, char *); + +/* Allow backends to override the names used for the stab sections. */ +#ifndef STAB_SECTION_NAME +#define STAB_SECTION_NAME ".stab" +#endif + +#ifndef STAB_STRING_SECTION_NAME +#define STAB_STRING_SECTION_NAME ".stabstr" +#endif + +/* Non-zero if we're in the middle of a .func function, in which case + stabs_generate_asm_lineno emits function relative line number stabs. + Otherwise it emits line number stabs with absolute addresses. Note that + both cases only apply to assembler code assembled with -gstabs. */ +static int in_dot_func_p; + +/* Label at start of current function if in_dot_func_p != 0. */ +static const char *current_function_label; + +/* + * Handle .stabX directives, which used to be open-coded. + * So much creeping featurism overloaded the semantics that we decided + * to put all .stabX thinking in one place. Here. + * + * We try to make any .stabX directive legal. Other people's AS will often + * do assembly-time consistency checks: eg assigning meaning to n_type bits + * and "protecting" you from setting them to certain values. (They also zero + * certain bits before emitting symbols. Tut tut.) + * + * If an expression is not absolute we either gripe or use the relocation + * information. Other people's assemblers silently forget information they + * don't need and invent information they need that you didn't supply. + */ + +/* + * Build a string dictionary entry for a .stabX symbol. + * The symbol is added to the .str section. + */ + +#ifndef SEPARATE_STAB_SECTIONS +#define SEPARATE_STAB_SECTIONS 0 +#endif + +unsigned int +get_stab_string_offset (const char *string, const char *stabstr_secname) +{ + unsigned int length; + unsigned int retval; + segT save_seg; + subsegT save_subseg; + segT seg; + char *p; + + if (! SEPARATE_STAB_SECTIONS) + abort (); + + length = strlen (string); + + save_seg = now_seg; + save_subseg = now_subseg; + + /* Create the stab string section. */ + seg = subseg_new (stabstr_secname, 0); + + retval = seg_info (seg)->stabu.stab_string_size; + if (retval <= 0) + { + /* Make sure the first string is empty. */ + p = frag_more (1); + *p = 0; + retval = seg_info (seg)->stabu.stab_string_size = 1; + bfd_set_section_flags (stdoutput, seg, SEC_READONLY | SEC_DEBUGGING); + if (seg->name == stabstr_secname) + seg->name = xstrdup (stabstr_secname); + } + + if (length > 0) + { /* Ordinary case. */ + p = frag_more (length + 1); + strcpy (p, string); + + seg_info (seg)->stabu.stab_string_size += length + 1; + } + else + retval = 0; + + subseg_set (save_seg, save_subseg); + + return retval; +} + +#ifdef AOUT_STABS +#ifndef OBJ_PROCESS_STAB +#define OBJ_PROCESS_STAB(SEG,W,S,T,O,D) aout_process_stab(W,S,T,O,D) +#endif + +/* Here instead of obj-aout.c because other formats use it too. */ +void +aout_process_stab (what, string, type, other, desc) + int what; + const char *string; + int type, other, desc; +{ + /* Put the stab information in the symbol table. */ + symbolS *symbol; + + /* Create the symbol now, but only insert it into the symbol chain + after any symbols mentioned in the value expression get into the + symbol chain. This is to avoid "continuation symbols" (where one + ends in "\" and the debug info is continued in the next .stabs + directive) from being separated by other random symbols. */ + symbol = symbol_create (string, undefined_section, 0, + &zero_address_frag); + if (what == 's' || what == 'n') + { + /* Pick up the value from the input line. */ + pseudo_set (symbol); + } + else + { + /* .stabd sets the name to NULL. Why? */ + S_SET_NAME (symbol, NULL); + symbol_set_frag (symbol, frag_now); + S_SET_VALUE (symbol, (valueT) frag_now_fix ()); + } + + symbol_append (symbol, symbol_lastP, &symbol_rootP, &symbol_lastP); + + symbol_get_bfdsym (symbol)->flags |= BSF_DEBUGGING; + + S_SET_TYPE (symbol, type); + S_SET_OTHER (symbol, other); + S_SET_DESC (symbol, desc); +} +#endif + +/* This can handle different kinds of stabs (s,n,d) and different + kinds of stab sections. */ + +static void +s_stab_generic (int what, char *stab_secname, char *stabstr_secname) +{ + long longint; + char *string, *saved_string_obstack_end; + int type; + int other; + int desc; + + /* The general format is: + .stabs "STRING",TYPE,OTHER,DESC,VALUE + .stabn TYPE,OTHER,DESC,VALUE + .stabd TYPE,OTHER,DESC + At this point input_line_pointer points after the pseudo-op and + any trailing whitespace. The argument what is one of 's', 'n' or + 'd' indicating which type of .stab this is. */ + + if (what != 's') + { + string = ""; + saved_string_obstack_end = 0; + } + else + { + int length; + + string = demand_copy_C_string (&length); + /* FIXME: We should probably find some other temporary storage + for string, rather than leaking memory if someone else + happens to use the notes obstack. */ + saved_string_obstack_end = notes.next_free; + SKIP_WHITESPACE (); + if (*input_line_pointer == ',') + input_line_pointer++; + else + { + as_warn (_(".stab%c: missing comma"), what); + ignore_rest_of_line (); + return; + } + } + + if (get_absolute_expression_and_terminator (&longint) != ',') + { + as_warn (_(".stab%c: missing comma"), what); + ignore_rest_of_line (); + return; + } + type = longint; + + if (get_absolute_expression_and_terminator (&longint) != ',') + { + as_warn (_(".stab%c: missing comma"), what); + ignore_rest_of_line (); + return; + } + other = longint; + + desc = get_absolute_expression (); + + if ((desc > 0xffff) || (desc < -0x8000)) + /* This could happen for example with a source file with a huge + number of lines. The only cure is to use a different debug + format, probably DWARF. */ + as_warn (_(".stab%c: description field '%x' too big, try a different debug format"), + what, desc); + + if (what == 's' || what == 'n') + { + if (*input_line_pointer != ',') + { + as_warn (_(".stab%c: missing comma"), what); + ignore_rest_of_line (); + return; + } + input_line_pointer++; + SKIP_WHITESPACE (); + } + +#ifdef TC_PPC +#ifdef OBJ_ELF + /* Solaris on PowerPC has decided that .stabd can take 4 arguments, so if we were + given 4 arguments, make it a .stabn */ + else if (what == 'd') + { + char *save_location = input_line_pointer; + + SKIP_WHITESPACE (); + if (*input_line_pointer == ',') + { + input_line_pointer++; + what = 'n'; + } + else + input_line_pointer = save_location; + } +#endif /* OBJ_ELF */ +#endif /* TC_PPC */ + +#ifndef NO_LISTING + if (listing) + { + switch (type) + { + case N_SLINE: + listing_source_line ((unsigned int) desc); + break; + case N_SO: + case N_SOL: + listing_source_file (string); + break; + } + } +#endif /* ! NO_LISTING */ + + /* We have now gathered the type, other, and desc information. For + .stabs or .stabn, input_line_pointer is now pointing at the + value. */ + + if (SEPARATE_STAB_SECTIONS) + /* Output the stab information in a separate section. This is used + at least for COFF and ELF. */ + { + segT saved_seg = now_seg; + subsegT saved_subseg = now_subseg; + fragS *saved_frag = frag_now; + valueT dot; + segT seg; + unsigned int stroff; + char *p; + + static segT cached_sec; + static char *cached_secname; + + dot = frag_now_fix (); + +#ifdef md_flush_pending_output + md_flush_pending_output (); +#endif + + if (cached_secname && !strcmp (cached_secname, stab_secname)) + { + seg = cached_sec; + subseg_set (seg, 0); + } + else + { + seg = subseg_new (stab_secname, 0); + if (cached_secname) + free (cached_secname); + cached_secname = xstrdup (stab_secname); + cached_sec = seg; + } + + if (! seg_info (seg)->hadone) + { + bfd_set_section_flags (stdoutput, seg, + SEC_READONLY | SEC_RELOC | SEC_DEBUGGING); +#ifdef INIT_STAB_SECTION + INIT_STAB_SECTION (seg); +#endif + seg_info (seg)->hadone = 1; + } + + stroff = get_stab_string_offset (string, stabstr_secname); + if (what == 's') + { + /* Release the string, if nobody else has used the obstack. */ + if (saved_string_obstack_end == notes.next_free) + obstack_free (¬es, string); + } + + /* At least for now, stabs in a special stab section are always + output as 12 byte blocks of information. */ + p = frag_more (8); + md_number_to_chars (p, (valueT) stroff, 4); + md_number_to_chars (p + 4, (valueT) type, 1); + md_number_to_chars (p + 5, (valueT) other, 1); + md_number_to_chars (p + 6, (valueT) desc, 2); + + if (what == 's' || what == 'n') + { + /* Pick up the value from the input line. */ + cons (4); + input_line_pointer--; + } + else + { + symbolS *symbol; + expressionS exp; + + /* Arrange for a value representing the current location. */ + symbol = symbol_temp_new (saved_seg, dot, saved_frag); + + exp.X_op = O_symbol; + exp.X_add_symbol = symbol; + exp.X_add_number = 0; + + emit_expr (&exp, 4); + } + +#ifdef OBJ_PROCESS_STAB + OBJ_PROCESS_STAB (seg, what, string, type, other, desc); +#endif + + subseg_set (saved_seg, saved_subseg); + } + else + { +#ifdef OBJ_PROCESS_STAB + OBJ_PROCESS_STAB (0, what, string, type, other, desc); +#else + abort (); +#endif + } + + demand_empty_rest_of_line (); +} + +/* Regular stab directive. */ + +void +s_stab (int what) +{ + s_stab_generic (what, STAB_SECTION_NAME, STAB_STRING_SECTION_NAME); +} + +/* "Extended stabs", used in Solaris only now. */ + +void +s_xstab (int what) +{ + int length; + char *stab_secname, *stabstr_secname; + static char *saved_secname, *saved_strsecname; + + /* @@ MEMORY LEAK: This allocates a copy of the string, but in most + cases it will be the same string, so we could release the storage + back to the obstack it came from. */ + stab_secname = demand_copy_C_string (&length); + SKIP_WHITESPACE (); + if (*input_line_pointer == ',') + input_line_pointer++; + else + { + as_bad (_("comma missing in .xstabs")); + ignore_rest_of_line (); + return; + } + + /* To get the name of the stab string section, simply add "str" to + the stab section name. */ + if (saved_secname == 0 || strcmp (saved_secname, stab_secname)) + { + stabstr_secname = (char *) xmalloc (strlen (stab_secname) + 4); + strcpy (stabstr_secname, stab_secname); + strcat (stabstr_secname, "str"); + if (saved_secname) + { + free (saved_secname); + free (saved_strsecname); + } + saved_secname = stab_secname; + saved_strsecname = stabstr_secname; + } + s_stab_generic (what, saved_secname, saved_strsecname); +} + +#ifdef S_SET_DESC + +/* Frob invented at RMS' request. Set the n_desc of a symbol. */ + +void +s_desc (ignore) + int ignore ATTRIBUTE_UNUSED; +{ + char *name; + char c; + char *p; + symbolS *symbolP; + int temp; + + name = input_line_pointer; + c = get_symbol_end (); + p = input_line_pointer; + *p = c; + SKIP_WHITESPACE (); + if (*input_line_pointer != ',') + { + *p = 0; + as_bad (_("expected comma after \"%s\""), name); + *p = c; + ignore_rest_of_line (); + } + else + { + input_line_pointer++; + temp = get_absolute_expression (); + *p = 0; + symbolP = symbol_find_or_make (name); + *p = c; + S_SET_DESC (symbolP, temp); + } + demand_empty_rest_of_line (); +} /* s_desc() */ + +#endif /* defined (S_SET_DESC) */ + +/* Generate stabs debugging information to denote the main source file. */ + +void +stabs_generate_asm_file (void) +{ + char *file; + unsigned int lineno; + + as_where (&file, &lineno); + if (use_gnu_debug_info_extensions) + { + const char *dir; + char *dir2; + + dir = remap_debug_filename (getpwd ()); + dir2 = (char *) alloca (strlen (dir) + 2); + sprintf (dir2, "%s%s", dir, "/"); + generate_asm_file (N_SO, dir2); + xfree ((char *) dir); + } + generate_asm_file (N_SO, file); +} + +/* Generate stabs debugging information to denote the source file. + TYPE is one of N_SO, N_SOL. */ + +static void +generate_asm_file (int type, char *file) +{ + static char *last_file; + static int label_count; + char *hold; + char sym[30]; + char *buf; + char *tmp = file; + char *file_endp = file + strlen (file); + char *bufp; + + if (last_file != NULL + && filename_cmp (last_file, file) == 0) + return; + + /* Rather than try to do this in some efficient fashion, we just + generate a string and then parse it again. That lets us use the + existing stabs hook, which expect to see a string, rather than + inventing new ones. */ + hold = input_line_pointer; + + sprintf (sym, "%sF%d", FAKE_LABEL_NAME, label_count); + ++label_count; + + /* Allocate enough space for the file name (possibly extended with + doubled up backslashes), the symbol name, and the other characters + that make up a stabs file directive. */ + bufp = buf = (char *) xmalloc (2 * strlen (file) + strlen (sym) + 12); + + *bufp++ = '"'; + + while (tmp < file_endp) + { + char *bslash = strchr (tmp, '\\'); + size_t len = (bslash) ? (size_t) (bslash - tmp + 1) : strlen (tmp); + + /* Double all backslashes, since demand_copy_C_string (used by + s_stab to extract the part in quotes) will try to replace them as + escape sequences. backslash may appear in a filespec. */ + strncpy (bufp, tmp, len); + + tmp += len; + bufp += len; + + if (bslash != NULL) + *bufp++ = '\\'; + } + + sprintf (bufp, "\",%d,0,0,%s\n", type, sym); + + input_line_pointer = buf; + s_stab ('s'); + colon (sym); + + if (last_file != NULL) + free (last_file); + last_file = xstrdup (file); + + free (buf); + + input_line_pointer = hold; +} + +/* Generate stabs debugging information for the current line. This is + used to produce debugging information for an assembler file. */ + +void +stabs_generate_asm_lineno (void) +{ + static int label_count; + char *hold; + char *file; + unsigned int lineno; + char *buf; + char sym[30]; + /* Remember the last file/line and avoid duplicates. */ + static unsigned int prev_lineno = -1; + static char *prev_file = NULL; + + /* Rather than try to do this in some efficient fashion, we just + generate a string and then parse it again. That lets us use the + existing stabs hook, which expect to see a string, rather than + inventing new ones. */ + + hold = input_line_pointer; + + as_where (&file, &lineno); + + /* Don't emit sequences of stabs for the same line. */ + if (prev_file == NULL) + { + /* First time thru. */ + prev_file = xstrdup (file); + prev_lineno = lineno; + } + else if (lineno == prev_lineno + && filename_cmp (file, prev_file) == 0) + { + /* Same file/line as last time. */ + return; + } + else + { + /* Remember file/line for next time. */ + prev_lineno = lineno; + if (filename_cmp (file, prev_file) != 0) + { + free (prev_file); + prev_file = xstrdup (file); + } + } + + /* Let the world know that we are in the middle of generating a + piece of stabs line debugging information. */ + outputting_stabs_line_debug = 1; + + generate_asm_file (N_SOL, file); + + sprintf (sym, "%sL%d", FAKE_LABEL_NAME, label_count); + ++label_count; + + if (in_dot_func_p) + { + buf = (char *) alloca (100 + strlen (current_function_label)); + sprintf (buf, "%d,0,%d,%s-%s\n", N_SLINE, lineno, + sym, current_function_label); + } + else + { + buf = (char *) alloca (100); + sprintf (buf, "%d,0,%d,%s\n", N_SLINE, lineno, sym); + } + input_line_pointer = buf; + s_stab ('n'); + colon (sym); + + input_line_pointer = hold; + outputting_stabs_line_debug = 0; +} + +/* Emit a function stab. + All assembler functions are assumed to have return type `void'. */ + +void +stabs_generate_asm_func (const char *funcname, const char *startlabname) +{ + static int void_emitted_p; + char *hold = input_line_pointer; + char *buf; + char *file; + unsigned int lineno; + + if (! void_emitted_p) + { + input_line_pointer = "\"void:t1=1\",128,0,0,0"; + s_stab ('s'); + void_emitted_p = 1; + } + + as_where (&file, &lineno); + if (asprintf (&buf, "\"%s:F1\",%d,0,%d,%s", + funcname, N_FUN, lineno + 1, startlabname) == -1) + as_fatal ("%s", xstrerror (errno)); + input_line_pointer = buf; + s_stab ('s'); + free (buf); + + input_line_pointer = hold; + current_function_label = xstrdup (startlabname); + in_dot_func_p = 1; +} + +/* Emit a stab to record the end of a function. */ + +void +stabs_generate_asm_endfunc (const char *funcname ATTRIBUTE_UNUSED, + const char *startlabname) +{ + static int label_count; + char *hold = input_line_pointer; + char *buf; + char sym[30]; + + sprintf (sym, "%sendfunc%d", FAKE_LABEL_NAME, label_count); + ++label_count; + colon (sym); + + if (asprintf (&buf, "\"\",%d,0,0,%s-%s", N_FUN, sym, startlabname) == -1) + as_fatal ("%s", xstrerror (errno)); + input_line_pointer = buf; + s_stab ('s'); + free (buf); + + input_line_pointer = hold; + in_dot_func_p = 0; + current_function_label = NULL; +} diff --git a/contrib/toolchain/binutils/gas/struc-symbol.h b/contrib/toolchain/binutils/gas/struc-symbol.h new file mode 100644 index 0000000000..1dbb9d2c35 --- /dev/null +++ b/contrib/toolchain/binutils/gas/struc-symbol.h @@ -0,0 +1,159 @@ +/* struct_symbol.h - Internal symbol structure + Copyright 1987, 1992, 1993, 1994, 1995, 1998, 1999, 2000, 2001, 2005, + 2007, 2008, 2009 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef __struc_symbol_h__ +#define __struc_symbol_h__ + +struct symbol_flags +{ + /* Wether the symbol is a local_symbol. */ + unsigned int sy_local_symbol : 1; + + /* Wether symbol has been written. */ + unsigned int sy_written : 1; + + /* Whether symbol value has been completely resolved (used during + final pass over symbol table). */ + unsigned int sy_resolved : 1; + + /* Whether the symbol value is currently being resolved (used to + detect loops in symbol dependencies). */ + unsigned int sy_resolving : 1; + + /* Whether the symbol value is used in a reloc. This is used to + ensure that symbols used in relocs are written out, even if they + are local and would otherwise not be. */ + unsigned int sy_used_in_reloc : 1; + + /* Whether the symbol is used as an operand or in an expression. + NOTE: Not all the backends keep this information accurate; + backends which use this bit are responsible for setting it when + a symbol is used in backend routines. */ + unsigned int sy_used : 1; + + /* Whether the symbol can be re-defined. */ + unsigned int sy_volatile : 1; + + /* Whether the symbol is a forward reference. */ + unsigned int sy_forward_ref : 1; + + /* This is set if the symbol is defined in an MRI common section. + We handle such sections as single common symbols, so symbols + defined within them must be treated specially by the relocation + routines. */ + unsigned int sy_mri_common : 1; + + /* This is set if the symbol is set with a .weakref directive. */ + unsigned int sy_weakrefr : 1; + + /* This is set when the symbol is referenced as part of a .weakref + directive, but only if the symbol was not in the symbol table + before. It is cleared as soon as any direct reference to the + symbol is present. */ + unsigned int sy_weakrefd : 1; +}; + +/* The information we keep for a symbol. Note that the symbol table + holds pointers both to this and to local_symbol structures. See + below. */ + +struct symbol +{ + /* Symbol flags. */ + struct symbol_flags sy_flags; + + /* BFD symbol */ + asymbol *bsym; + + /* The value of the symbol. */ + expressionS sy_value; + + /* Forwards and (optionally) backwards chain pointers. */ + struct symbol *sy_next; + struct symbol *sy_previous; + + /* Pointer to the frag this symbol is attached to, if any. + Otherwise, NULL. */ + struct frag *sy_frag; + +#ifdef OBJ_SYMFIELD_TYPE + OBJ_SYMFIELD_TYPE sy_obj; +#endif + +#ifdef TC_SYMFIELD_TYPE + TC_SYMFIELD_TYPE sy_tc; +#endif + +#ifdef TARGET_SYMBOL_FIELDS + TARGET_SYMBOL_FIELDS +#endif +}; + +/* A pointer in the symbol may point to either a complete symbol + (struct symbol above) or to a local symbol (struct local_symbol + defined here). The symbol code can detect the case by examining + the first field. It is always NULL for a local symbol. + + We do this because we ordinarily only need a small amount of + information for a local symbol. The symbol table takes up a lot of + space, and storing less information for a local symbol can make a + big difference in assembler memory usage when assembling a large + file. */ + +struct local_symbol +{ + /* Symbol flags. Only sy_local_symbol and sy_resolved are relevant. */ + struct symbol_flags lsy_flags; + + /* The symbol section. This also serves as a flag. If this is + reg_section, then this symbol has been converted into a regular + symbol, and lsy_sym points to it. */ + segT lsy_section; + + /* The symbol name. */ + const char *lsy_name; + + /* The symbol frag or the real symbol, depending upon the value in + lsy_section. */ + union + { + fragS *lsy_frag; + symbolS *lsy_sym; + } u; + + /* The value of the symbol. */ + valueT lsy_value; + +#ifdef TC_LOCAL_SYMFIELD_TYPE + TC_LOCAL_SYMFIELD_TYPE lsy_tc; +#endif +}; + +#define local_symbol_converted_p(l) ((l)->lsy_section == reg_section) +#define local_symbol_mark_converted(l) ((l)->lsy_section = reg_section) +#define local_symbol_resolved_p(l) ((l)->lsy_flags.sy_resolved) +#define local_symbol_mark_resolved(l) ((l)->lsy_flags.sy_resolved = 1) +#define local_symbol_get_frag(l) ((l)->u.lsy_frag) +#define local_symbol_set_frag(l, f) ((l)->u.lsy_frag = (f)) +#define local_symbol_get_real_symbol(l) ((l)->u.lsy_sym) +#define local_symbol_set_real_symbol(l, s) ((l)->u.lsy_sym = (s)) + +#endif /* __struc_symbol_h__ */ diff --git a/contrib/toolchain/binutils/gas/subsegs.c b/contrib/toolchain/binutils/gas/subsegs.c new file mode 100644 index 0000000000..69f837b2a2 --- /dev/null +++ b/contrib/toolchain/binutils/gas/subsegs.c @@ -0,0 +1,330 @@ +/* subsegs.c - subsegments - + Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2009 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* Segments & sub-segments. */ + +#include "as.h" + +#include "subsegs.h" +#include "obstack.h" + +frchainS *frchain_now; + +static struct obstack frchains; + +static fragS dummy_frag; + + +void +subsegs_begin (void) +{ + obstack_begin (&frchains, chunksize); +#if __GNUC__ >= 2 + obstack_alignment_mask (&frchains) = __alignof__ (frchainS) - 1; +#endif + + frchain_now = NULL; /* Warn new_subseg() that we are booting. */ + frag_now = &dummy_frag; +} + +/* + * subseg_change() + * + * Change the subsegment we are in, BUT DO NOT MAKE A NEW FRAG for the + * subsegment. If we are already in the correct subsegment, change nothing. + * This is used eg as a worker for subseg_set [which does make a new frag_now] + * and for changing segments after we have read the source. We construct eg + * fixSs even after the source file is read, so we do have to keep the + * segment context correct. + */ +void +subseg_change (register segT seg, register int subseg) +{ + segment_info_type *seginfo = seg_info (seg); + now_seg = seg; + now_subseg = subseg; + + if (! seginfo) + { + seginfo = (segment_info_type *) xcalloc (1, sizeof (*seginfo)); + seginfo->bfd_section = seg; + bfd_set_section_userdata (stdoutput, seg, seginfo); + } +} + +static void +subseg_set_rest (segT seg, subsegT subseg) +{ + frchainS *frcP; /* crawl frchain chain */ + frchainS **lastPP; /* address of last pointer */ + frchainS *newP; /* address of new frchain */ + segment_info_type *seginfo; + + mri_common_symbol = NULL; + + if (frag_now && frchain_now) + frchain_now->frch_frag_now = frag_now; + + gas_assert (frchain_now == 0 + || frchain_now->frch_last == frag_now); + + subseg_change (seg, (int) subseg); + + seginfo = seg_info (seg); + + /* Attempt to find or make a frchain for that subsection. + We keep the list sorted by subsection number. */ + for (frcP = *(lastPP = &seginfo->frchainP); + frcP != NULL; + frcP = *(lastPP = &frcP->frch_next)) + if (frcP->frch_subseg >= subseg) + break; + + if (frcP == NULL || frcP->frch_subseg != subseg) + { + /* This should be the only code that creates a frchainS. */ + + newP = (frchainS *) obstack_alloc (&frchains, sizeof (frchainS)); + newP->frch_subseg = subseg; + newP->fix_root = NULL; + newP->fix_tail = NULL; + obstack_begin (&newP->frch_obstack, chunksize); +#if __GNUC__ >= 2 + obstack_alignment_mask (&newP->frch_obstack) = __alignof__ (fragS) - 1; +#endif + newP->frch_frag_now = frag_alloc (&newP->frch_obstack); + newP->frch_frag_now->fr_type = rs_fill; + newP->frch_cfi_data = NULL; + + newP->frch_root = newP->frch_last = newP->frch_frag_now; + + *lastPP = newP; + newP->frch_next = frcP; + frcP = newP; + } + + frchain_now = frcP; + frag_now = frcP->frch_frag_now; + + gas_assert (frchain_now->frch_last == frag_now); +} + +/* + * subseg_set(segT, subsegT) + * + * If you attempt to change to the current subsegment, nothing happens. + * + * In: segT, subsegT code for new subsegment. + * frag_now -> incomplete frag for current subsegment. + * If frag_now==NULL, then there is no old, incomplete frag, so + * the old frag is not closed off. + * + * Out: now_subseg, now_seg updated. + * Frchain_now points to the (possibly new) struct frchain for this + * sub-segment. + */ + +segT +subseg_get (const char *segname, int force_new) +{ + segT secptr; + segment_info_type *seginfo; + const char *now_seg_name = (now_seg + ? bfd_get_section_name (stdoutput, now_seg) + : 0); + + if (!force_new + && now_seg_name + && (now_seg_name == segname + || !strcmp (now_seg_name, segname))) + return now_seg; + + if (!force_new) + secptr = bfd_make_section_old_way (stdoutput, segname); + else + secptr = bfd_make_section_anyway (stdoutput, segname); + + seginfo = seg_info (secptr); + if (! seginfo) + { + secptr->output_section = secptr; + seginfo = (segment_info_type *) xcalloc (1, sizeof (*seginfo)); + seginfo->bfd_section = secptr; + bfd_set_section_userdata (stdoutput, secptr, seginfo); + } + return secptr; +} + +segT +subseg_new (const char *segname, subsegT subseg) +{ + segT secptr; + + secptr = subseg_get (segname, 0); + subseg_set_rest (secptr, subseg); + return secptr; +} + +/* Like subseg_new, except a new section is always created, even if + a section with that name already exists. */ +segT +subseg_force_new (const char *segname, subsegT subseg) +{ + segT secptr; + + secptr = subseg_get (segname, 1); + subseg_set_rest (secptr, subseg); + return secptr; +} + +void +subseg_set (segT secptr, subsegT subseg) +{ + if (! (secptr == now_seg && subseg == now_subseg)) + subseg_set_rest (secptr, subseg); + mri_common_symbol = NULL; +} + +#ifndef obj_sec_sym_ok_for_reloc +#define obj_sec_sym_ok_for_reloc(SEC) 0 +#endif + +symbolS * +section_symbol (segT sec) +{ + segment_info_type *seginfo = seg_info (sec); + symbolS *s; + + if (seginfo == 0) + abort (); + if (seginfo->sym) + return seginfo->sym; + +#ifndef EMIT_SECTION_SYMBOLS +#define EMIT_SECTION_SYMBOLS 1 +#endif + + if (! EMIT_SECTION_SYMBOLS || symbol_table_frozen) + { + /* Here we know it won't be going into the symbol table. */ + s = symbol_create (sec->symbol->name, sec, 0, &zero_address_frag); + } + else + { + segT seg; + s = symbol_find (sec->symbol->name); + /* We have to make sure it is the right symbol when we + have multiple sections with the same section name. */ + if (s == NULL + || ((seg = S_GET_SEGMENT (s)) != sec + && seg != undefined_section)) + s = symbol_new (sec->symbol->name, sec, 0, &zero_address_frag); + else if (seg == undefined_section) + { + S_SET_SEGMENT (s, sec); + symbol_set_frag (s, &zero_address_frag); + } + } + + S_CLEAR_EXTERNAL (s); + + /* Use the BFD section symbol, if possible. */ + if (obj_sec_sym_ok_for_reloc (sec)) + symbol_set_bfdsym (s, sec->symbol); + else + symbol_get_bfdsym (s)->flags |= BSF_SECTION_SYM; + + seginfo->sym = s; + return s; +} + +/* Return whether the specified segment is thought to hold text. */ + +int +subseg_text_p (segT sec) +{ + return (bfd_get_section_flags (stdoutput, sec) & SEC_CODE) != 0; +} + +/* Return non zero if SEC has at least one byte of data. It is + possible that we'll return zero even on a non-empty section because + we don't know all the fragment types, and it is possible that an + fr_fix == 0 one still contributes data. Think of this as + seg_definitely_not_empty_p. */ + +int +seg_not_empty_p (segT sec ATTRIBUTE_UNUSED) +{ + segment_info_type *seginfo = seg_info (sec); + frchainS *chain; + fragS *frag; + + if (!seginfo) + return 0; + + for (chain = seginfo->frchainP; chain; chain = chain->frch_next) + { + for (frag = chain->frch_root; frag; frag = frag->fr_next) + if (frag->fr_fix) + return 1; + if (obstack_next_free (&chain->frch_obstack) + != chain->frch_last->fr_literal) + return 1; + } + return 0; +} + +void +subsegs_print_statistics (FILE *file) +{ + frchainS *frchp; + asection *s; + + fprintf (file, "frag chains:\n"); + for (s = stdoutput->sections; s; s = s->next) + { + segment_info_type *seginfo; + + /* Skip gas-internal sections. */ + if (segment_name (s)[0] == '*') + continue; + + seginfo = seg_info (s); + if (!seginfo) + continue; + + for (frchp = seginfo->frchainP; frchp; frchp = frchp->frch_next) + { + int count = 0; + fragS *fragp; + + for (fragp = frchp->frch_root; fragp; fragp = fragp->fr_next) + count++; + + fprintf (file, "\n"); + fprintf (file, "\t%p %-10s\t%10d frags\n", (void *) frchp, + segment_name (s), count); + } + } +} + +/* end of subsegs.c */ diff --git a/contrib/toolchain/binutils/gas/subsegs.h b/contrib/toolchain/binutils/gas/subsegs.h new file mode 100644 index 0000000000..1e5f6199cf --- /dev/null +++ b/contrib/toolchain/binutils/gas/subsegs.h @@ -0,0 +1,117 @@ +/* subsegs.h -> subsegs.c + Copyright 1987, 1992, 1993, 1994, 1995, 1996, 1998, 2000, 2003, 2005, + 2006, 2007, 2009 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* + * For every sub-segment the user mentions in the ASsembler program, + * we make one struct frchain. Each sub-segment has exactly one struct frchain + * and vice versa. + * + * Struct frchain's are forward chained (in ascending order of sub-segment + * code number). The chain runs through frch_next of each subsegment. + * This makes it hard to find a subsegment's frags + * if programmer uses a lot of them. Most programs only use text0 and + * data0, so they don't suffer. At least this way: + * (1) There are no "arbitrary" restrictions on how many subsegments + * can be programmed; + * (2) Subsegments' frchain-s are (later) chained together in the order in + * which they are emitted for object file viz text then data. + * + * From each struct frchain dangles a chain of struct frags. The frags + * represent code fragments, for that sub-segment, forward chained. + */ + +#include "obstack.h" + +struct frch_cfi_data; + +struct frchain /* control building of a frag chain */ +{ /* FRCH = FRagment CHain control */ + struct frag *frch_root; /* 1st struct frag in chain, or NULL */ + struct frag *frch_last; /* last struct frag in chain, or NULL */ + struct frchain *frch_next; /* next in chain of struct frchain-s */ + subsegT frch_subseg; /* subsegment number of this chain */ + fixS *fix_root; /* Root of fixups for this subsegment. */ + fixS *fix_tail; /* Last fixup for this subsegment. */ + struct obstack frch_obstack; /* for objects in this frag chain */ + fragS *frch_frag_now; /* frag_now for this subsegment */ + struct frch_cfi_data *frch_cfi_data; +}; + +typedef struct frchain frchainS; + +/* Frchain we are assembling into now. That is, the current segment's + frag chain, even if it contains no (complete) frags. */ +extern frchainS *frchain_now; + +typedef struct segment_info_struct { + frchainS *frchainP; + unsigned int hadone : 1; + + /* This field is set if this is a .bss section which does not really + have any contents. Once upon a time a .bss section did not have + any frags, but that is no longer true. This field prevent the + SEC_HAS_CONTENTS flag from being set for the section even if + there are frags. */ + unsigned int bss : 1; + + int user_stuff; + + /* Fixups for this segment. This is only valid after the frchains + are run together. */ + fixS *fix_root; + fixS *fix_tail; + + symbolS *dot; + + struct lineno_list *lineno_list_head; + struct lineno_list *lineno_list_tail; + + /* Which BFD section does this gas segment correspond to? */ + asection *bfd_section; + + /* NULL, or pointer to the gas symbol that is the section symbol for + this section. sym->bsym and bfd_section->symbol should be the same. */ + symbolS *sym; + + union { + /* Current size of section holding stabs strings. */ + unsigned long stab_string_size; + /* Initial frag for ELF. */ + char *p; + } + stabu; + +#ifdef NEED_LITERAL_POOL + unsigned long literal_pool_size; +#endif + +#ifdef TC_SEGMENT_INFO_TYPE + TC_SEGMENT_INFO_TYPE tc_segment_info_data; +#endif +} segment_info_type; + + +#define seg_info(sec) \ + ((segment_info_type *) bfd_get_section_userdata (stdoutput, sec)) + +extern symbolS *section_symbol (segT); + +extern void subsegs_print_statistics (FILE *); diff --git a/contrib/toolchain/binutils/gas/symbols.c b/contrib/toolchain/binutils/gas/symbols.c new file mode 100644 index 0000000000..67fc84bb7e --- /dev/null +++ b/contrib/toolchain/binutils/gas/symbols.c @@ -0,0 +1,3252 @@ +/* symbols.c -symbol table- + Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, + 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, + 2011, 2012 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* #define DEBUG_SYMS / * to debug symbol list maintenance. */ + +#include "as.h" +#include "safe-ctype.h" +#include "obstack.h" /* For "symbols.h" */ +#include "subsegs.h" +#include "struc-symbol.h" + +/* This is non-zero if symbols are case sensitive, which is the + default. */ +int symbols_case_sensitive = 1; + +#ifndef WORKING_DOT_WORD +extern int new_broken_words; +#endif + +/* symbol-name => struct symbol pointer */ +static struct hash_control *sy_hash; + +/* Table of local symbols. */ +static struct hash_control *local_hash; + +/* Below are commented in "symbols.h". */ +symbolS *symbol_rootP; +symbolS *symbol_lastP; +symbolS abs_symbol; +symbolS dot_symbol; + +#ifdef DEBUG_SYMS +#define debug_verify_symchain verify_symbol_chain +#else +#define debug_verify_symchain(root, last) ((void) 0) +#endif + +#define DOLLAR_LABEL_CHAR '\001' +#define LOCAL_LABEL_CHAR '\002' + +#ifndef TC_LABEL_IS_LOCAL +#define TC_LABEL_IS_LOCAL(name) 0 +#endif + +struct obstack notes; +#ifdef TE_PE +/* The name of an external symbol which is + used to make weak PE symbol names unique. */ +const char * an_external_name; +#endif + +static char *save_symbol_name (const char *); +static void fb_label_init (void); +static long dollar_label_instance (long); +static long fb_label_instance (long); + +static void print_binary (FILE *, const char *, expressionS *); + +/* Return a pointer to a new symbol. Die if we can't make a new + symbol. Fill in the symbol's values. Add symbol to end of symbol + chain. + + This function should be called in the general case of creating a + symbol. However, if the output file symbol table has already been + set, and you are certain that this symbol won't be wanted in the + output file, you can call symbol_create. */ + +symbolS * +symbol_new (const char *name, segT segment, valueT valu, fragS *frag) +{ + symbolS *symbolP = symbol_create (name, segment, valu, frag); + + /* Link to end of symbol chain. */ + { + extern int symbol_table_frozen; + if (symbol_table_frozen) + abort (); + } + symbol_append (symbolP, symbol_lastP, &symbol_rootP, &symbol_lastP); + + return symbolP; +} + +/* Save a symbol name on a permanent obstack, and convert it according + to the object file format. */ + +static char * +save_symbol_name (const char *name) +{ + unsigned int name_length; + char *ret; + + name_length = strlen (name) + 1; /* +1 for \0. */ + obstack_grow (¬es, name, name_length); + ret = (char *) obstack_finish (¬es); + +#ifdef tc_canonicalize_symbol_name + ret = tc_canonicalize_symbol_name (ret); +#endif + + if (! symbols_case_sensitive) + { + char *s; + + for (s = ret; *s != '\0'; s++) + *s = TOUPPER (*s); + } + + return ret; +} + +symbolS * +symbol_create (const char *name, /* It is copied, the caller can destroy/modify. */ + segT segment, /* Segment identifier (SEG_). */ + valueT valu, /* Symbol value. */ + fragS *frag /* Associated fragment. */) +{ + char *preserved_copy_of_name; + symbolS *symbolP; + + preserved_copy_of_name = save_symbol_name (name); + + symbolP = (symbolS *) obstack_alloc (¬es, sizeof (symbolS)); + + /* symbol must be born in some fixed state. This seems as good as any. */ + memset (symbolP, 0, sizeof (symbolS)); + + symbolP->bsym = bfd_make_empty_symbol (stdoutput); + if (symbolP->bsym == NULL) + as_fatal ("bfd_make_empty_symbol: %s", bfd_errmsg (bfd_get_error ())); + S_SET_NAME (symbolP, preserved_copy_of_name); + + S_SET_SEGMENT (symbolP, segment); + S_SET_VALUE (symbolP, valu); + symbol_clear_list_pointers (symbolP); + + symbolP->sy_frag = frag; + + obj_symbol_new_hook (symbolP); + +#ifdef tc_symbol_new_hook + tc_symbol_new_hook (symbolP); +#endif + + return symbolP; +} + + +/* Local symbol support. If we can get away with it, we keep only a + small amount of information for local symbols. */ + +static symbolS *local_symbol_convert (struct local_symbol *); + +/* Used for statistics. */ + +static unsigned long local_symbol_count; +static unsigned long local_symbol_conversion_count; + +/* This macro is called with a symbol argument passed by reference. + It returns whether this is a local symbol. If necessary, it + changes its argument to the real symbol. */ + +#define LOCAL_SYMBOL_CHECK(s) \ + (s->sy_flags.sy_local_symbol \ + ? (local_symbol_converted_p ((struct local_symbol *) s) \ + ? (s = local_symbol_get_real_symbol ((struct local_symbol *) s), \ + 0) \ + : 1) \ + : 0) + +/* Create a local symbol and insert it into the local hash table. */ + +struct local_symbol * +local_symbol_make (const char *name, segT section, valueT val, fragS *frag) +{ + char *name_copy; + struct local_symbol *ret; + + ++local_symbol_count; + + name_copy = save_symbol_name (name); + + ret = (struct local_symbol *) obstack_alloc (¬es, sizeof *ret); + ret->lsy_flags.sy_local_symbol = 1; + ret->lsy_flags.sy_resolved = 0; + ret->lsy_name = name_copy; + ret->lsy_section = section; + local_symbol_set_frag (ret, frag); + ret->lsy_value = val; + + hash_jam (local_hash, name_copy, (void *) ret); + + return ret; +} + +/* Convert a local symbol into a real symbol. Note that we do not + reclaim the space used by the local symbol. */ + +static symbolS * +local_symbol_convert (struct local_symbol *locsym) +{ + symbolS *ret; + + gas_assert (locsym->lsy_flags.sy_local_symbol); + if (local_symbol_converted_p (locsym)) + return local_symbol_get_real_symbol (locsym); + + ++local_symbol_conversion_count; + + ret = symbol_new (locsym->lsy_name, locsym->lsy_section, locsym->lsy_value, + local_symbol_get_frag (locsym)); + + if (local_symbol_resolved_p (locsym)) + ret->sy_flags.sy_resolved = 1; + + /* Local symbols are always either defined or used. */ + ret->sy_flags.sy_used = 1; + +#ifdef TC_LOCAL_SYMFIELD_CONVERT + TC_LOCAL_SYMFIELD_CONVERT (locsym, ret); +#endif + + symbol_table_insert (ret); + + local_symbol_mark_converted (locsym); + local_symbol_set_real_symbol (locsym, ret); + + hash_jam (local_hash, locsym->lsy_name, NULL); + + return ret; +} + +static void +define_sym_at_dot (symbolS *symbolP) +{ + symbolP->sy_frag = frag_now; + S_SET_VALUE (symbolP, (valueT) frag_now_fix ()); + S_SET_SEGMENT (symbolP, now_seg); +} + +/* We have just seen ":". + Creates a struct symbol unless it already exists. + + Gripes if we are redefining a symbol incompatibly (and ignores it). */ + +symbolS * +colon (/* Just seen "x:" - rattle symbols & frags. */ + const char *sym_name /* Symbol name, as a cannonical string. */ + /* We copy this string: OK to alter later. */) +{ + register symbolS *symbolP; /* Symbol we are working with. */ + + /* Sun local labels go out of scope whenever a non-local symbol is + defined. */ + if (LOCAL_LABELS_DOLLAR + && !bfd_is_local_label_name (stdoutput, sym_name)) + dollar_label_clear (); + +#ifndef WORKING_DOT_WORD + if (new_broken_words) + { + struct broken_word *a; + int possible_bytes; + fragS *frag_tmp; + char *frag_opcode; + + if (now_seg == absolute_section) + { + as_bad (_("cannot define symbol `%s' in absolute section"), sym_name); + return NULL; + } + + possible_bytes = (md_short_jump_size + + new_broken_words * md_long_jump_size); + + frag_tmp = frag_now; + frag_opcode = frag_var (rs_broken_word, + possible_bytes, + possible_bytes, + (relax_substateT) 0, + (symbolS *) broken_words, + (offsetT) 0, + NULL); + + /* We want to store the pointer to where to insert the jump + table in the fr_opcode of the rs_broken_word frag. This + requires a little hackery. */ + while (frag_tmp + && (frag_tmp->fr_type != rs_broken_word + || frag_tmp->fr_opcode)) + frag_tmp = frag_tmp->fr_next; + know (frag_tmp); + frag_tmp->fr_opcode = frag_opcode; + new_broken_words = 0; + + for (a = broken_words; a && a->dispfrag == 0; a = a->next_broken_word) + a->dispfrag = frag_tmp; + } +#endif /* WORKING_DOT_WORD */ + +#ifdef obj_frob_colon + obj_frob_colon (sym_name); +#endif + + if ((symbolP = symbol_find (sym_name)) != 0) + { + S_CLEAR_WEAKREFR (symbolP); +#ifdef RESOLVE_SYMBOL_REDEFINITION + if (RESOLVE_SYMBOL_REDEFINITION (symbolP)) + return symbolP; +#endif + /* Now check for undefined symbols. */ + if (LOCAL_SYMBOL_CHECK (symbolP)) + { + struct local_symbol *locsym = (struct local_symbol *) symbolP; + + if (locsym->lsy_section != undefined_section + && (local_symbol_get_frag (locsym) != frag_now + || locsym->lsy_section != now_seg + || locsym->lsy_value != frag_now_fix ())) + { + as_bad (_("symbol `%s' is already defined"), sym_name); + return symbolP; + } + + locsym->lsy_section = now_seg; + local_symbol_set_frag (locsym, frag_now); + locsym->lsy_value = frag_now_fix (); + } + else if (!(S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP)) + || S_IS_COMMON (symbolP) + || S_IS_VOLATILE (symbolP)) + { + if (S_IS_VOLATILE (symbolP)) + { + symbolP = symbol_clone (symbolP, 1); + S_SET_VALUE (symbolP, 0); + S_CLEAR_VOLATILE (symbolP); + } + if (S_GET_VALUE (symbolP) == 0) + { + define_sym_at_dot (symbolP); +#ifdef N_UNDF + know (N_UNDF == 0); +#endif /* if we have one, it better be zero. */ + + } + else + { + /* There are still several cases to check: + + A .comm/.lcomm symbol being redefined as initialized + data is OK + + A .comm/.lcomm symbol being redefined with a larger + size is also OK + + This only used to be allowed on VMS gas, but Sun cc + on the sparc also depends on it. */ + + if (((!S_IS_DEBUG (symbolP) + && (!S_IS_DEFINED (symbolP) || S_IS_COMMON (symbolP)) + && S_IS_EXTERNAL (symbolP)) + || S_GET_SEGMENT (symbolP) == bss_section) + && (now_seg == data_section + || now_seg == bss_section + || now_seg == S_GET_SEGMENT (symbolP))) + { + /* Select which of the 2 cases this is. */ + if (now_seg != data_section) + { + /* New .comm for prev .comm symbol. + + If the new size is larger we just change its + value. If the new size is smaller, we ignore + this symbol. */ + if (S_GET_VALUE (symbolP) + < ((unsigned) frag_now_fix ())) + { + S_SET_VALUE (symbolP, (valueT) frag_now_fix ()); + } + } + else + { + /* It is a .comm/.lcomm being converted to initialized + data. */ + define_sym_at_dot (symbolP); + } + } + else + { +#if (!defined (OBJ_AOUT) && !defined (OBJ_MAYBE_AOUT) \ + && !defined (OBJ_BOUT) && !defined (OBJ_MAYBE_BOUT)) + static const char *od_buf = ""; +#else + char od_buf[100]; + od_buf[0] = '\0'; + if (OUTPUT_FLAVOR == bfd_target_aout_flavour) + sprintf (od_buf, "%d.%d.", + S_GET_OTHER (symbolP), + S_GET_DESC (symbolP)); +#endif + as_bad (_("symbol `%s' is already defined as \"%s\"/%s%ld"), + sym_name, + segment_name (S_GET_SEGMENT (symbolP)), + od_buf, + (long) S_GET_VALUE (symbolP)); + } + } /* if the undefined symbol has no value */ + } + else + { + /* Don't blow up if the definition is the same. */ + if (!(frag_now == symbolP->sy_frag + && S_GET_VALUE (symbolP) == frag_now_fix () + && S_GET_SEGMENT (symbolP) == now_seg)) + { + as_bad (_("symbol `%s' is already defined"), sym_name); + symbolP = symbol_clone (symbolP, 0); + define_sym_at_dot (symbolP); + } + } + + } + else if (! flag_keep_locals && bfd_is_local_label_name (stdoutput, sym_name)) + { + symbolP = (symbolS *) local_symbol_make (sym_name, now_seg, + (valueT) frag_now_fix (), + frag_now); + } + else + { + symbolP = symbol_new (sym_name, now_seg, (valueT) frag_now_fix (), + frag_now); + + symbol_table_insert (symbolP); + } + + if (mri_common_symbol != NULL) + { + /* This symbol is actually being defined within an MRI common + section. This requires special handling. */ + if (LOCAL_SYMBOL_CHECK (symbolP)) + symbolP = local_symbol_convert ((struct local_symbol *) symbolP); + symbolP->sy_value.X_op = O_symbol; + symbolP->sy_value.X_add_symbol = mri_common_symbol; + symbolP->sy_value.X_add_number = S_GET_VALUE (mri_common_symbol); + symbolP->sy_frag = &zero_address_frag; + S_SET_SEGMENT (symbolP, expr_section); + symbolP->sy_flags.sy_mri_common = 1; + } + +#ifdef tc_frob_label + tc_frob_label (symbolP); +#endif +#ifdef obj_frob_label + obj_frob_label (symbolP); +#endif + + return symbolP; +} + +/* Die if we can't insert the symbol. */ + +void +symbol_table_insert (symbolS *symbolP) +{ + register const char *error_string; + + know (symbolP); + know (S_GET_NAME (symbolP)); + + if (LOCAL_SYMBOL_CHECK (symbolP)) + { + error_string = hash_jam (local_hash, S_GET_NAME (symbolP), + (void *) symbolP); + if (error_string != NULL) + as_fatal (_("inserting \"%s\" into symbol table failed: %s"), + S_GET_NAME (symbolP), error_string); + return; + } + + if ((error_string = hash_jam (sy_hash, S_GET_NAME (symbolP), (void *) symbolP))) + { + as_fatal (_("inserting \"%s\" into symbol table failed: %s"), + S_GET_NAME (symbolP), error_string); + } /* on error */ +} + +/* If a symbol name does not exist, create it as undefined, and insert + it into the symbol table. Return a pointer to it. */ + +symbolS * +symbol_find_or_make (const char *name) +{ + register symbolS *symbolP; + + symbolP = symbol_find (name); + + if (symbolP == NULL) + { + if (! flag_keep_locals && bfd_is_local_label_name (stdoutput, name)) + { + symbolP = md_undefined_symbol ((char *) name); + if (symbolP != NULL) + return symbolP; + + symbolP = (symbolS *) local_symbol_make (name, undefined_section, + (valueT) 0, + &zero_address_frag); + return symbolP; + } + + symbolP = symbol_make (name); + + symbol_table_insert (symbolP); + } /* if symbol wasn't found */ + + return (symbolP); +} + +symbolS * +symbol_make (const char *name) +{ + symbolS *symbolP; + + /* Let the machine description default it, e.g. for register names. */ + symbolP = md_undefined_symbol ((char *) name); + + if (!symbolP) + symbolP = symbol_new (name, undefined_section, (valueT) 0, &zero_address_frag); + + return (symbolP); +} + +symbolS * +symbol_clone (symbolS *orgsymP, int replace) +{ + symbolS *newsymP; + asymbol *bsymorg, *bsymnew; + + /* Make sure we never clone the dot special symbol. */ + gas_assert (orgsymP != &dot_symbol); + + /* Running local_symbol_convert on a clone that's not the one currently + in local_hash would incorrectly replace the hash entry. Thus the + symbol must be converted here. Note that the rest of the function + depends on not encountering an unconverted symbol. */ + if (LOCAL_SYMBOL_CHECK (orgsymP)) + orgsymP = local_symbol_convert ((struct local_symbol *) orgsymP); + bsymorg = orgsymP->bsym; + + newsymP = (symbolS *) obstack_alloc (¬es, sizeof (*newsymP)); + *newsymP = *orgsymP; + bsymnew = bfd_make_empty_symbol (bfd_asymbol_bfd (bsymorg)); + if (bsymnew == NULL) + as_fatal ("bfd_make_empty_symbol: %s", bfd_errmsg (bfd_get_error ())); + newsymP->bsym = bsymnew; + bsymnew->name = bsymorg->name; + bsymnew->flags = bsymorg->flags & ~BSF_SECTION_SYM; + bsymnew->section = bsymorg->section; + bfd_copy_private_symbol_data (bfd_asymbol_bfd (bsymorg), bsymorg, + bfd_asymbol_bfd (bsymnew), bsymnew); + +#ifdef obj_symbol_clone_hook + obj_symbol_clone_hook (newsymP, orgsymP); +#endif + +#ifdef tc_symbol_clone_hook + tc_symbol_clone_hook (newsymP, orgsymP); +#endif + + if (replace) + { + if (symbol_rootP == orgsymP) + symbol_rootP = newsymP; + else if (orgsymP->sy_previous) + { + orgsymP->sy_previous->sy_next = newsymP; + orgsymP->sy_previous = NULL; + } + if (symbol_lastP == orgsymP) + symbol_lastP = newsymP; + else if (orgsymP->sy_next) + orgsymP->sy_next->sy_previous = newsymP; + + /* Symbols that won't be output can't be external. */ + S_CLEAR_EXTERNAL (orgsymP); + orgsymP->sy_previous = orgsymP->sy_next = orgsymP; + debug_verify_symchain (symbol_rootP, symbol_lastP); + + symbol_table_insert (newsymP); + } + else + { + /* Symbols that won't be output can't be external. */ + S_CLEAR_EXTERNAL (newsymP); + newsymP->sy_previous = newsymP->sy_next = newsymP; + } + + return newsymP; +} + +/* Referenced symbols, if they are forward references, need to be cloned + (without replacing the original) so that the value of the referenced + symbols at the point of use . */ + +#undef symbol_clone_if_forward_ref +symbolS * +symbol_clone_if_forward_ref (symbolS *symbolP, int is_forward) +{ + if (symbolP && !LOCAL_SYMBOL_CHECK (symbolP)) + { + symbolS *add_symbol = symbolP->sy_value.X_add_symbol; + symbolS *op_symbol = symbolP->sy_value.X_op_symbol; + + if (symbolP->sy_flags.sy_forward_ref) + is_forward = 1; + + if (is_forward) + { + /* assign_symbol() clones volatile symbols; pre-existing expressions + hold references to the original instance, but want the current + value. Just repeat the lookup. */ + if (add_symbol && S_IS_VOLATILE (add_symbol)) + add_symbol = symbol_find_exact (S_GET_NAME (add_symbol)); + if (op_symbol && S_IS_VOLATILE (op_symbol)) + op_symbol = symbol_find_exact (S_GET_NAME (op_symbol)); + } + + /* Re-using sy_resolving here, as this routine cannot get called from + symbol resolution code. */ + if ((symbolP->bsym->section == expr_section + || symbolP->sy_flags.sy_forward_ref) + && !symbolP->sy_flags.sy_resolving) + { + symbolP->sy_flags.sy_resolving = 1; + add_symbol = symbol_clone_if_forward_ref (add_symbol, is_forward); + op_symbol = symbol_clone_if_forward_ref (op_symbol, is_forward); + symbolP->sy_flags.sy_resolving = 0; + } + + if (symbolP->sy_flags.sy_forward_ref + || add_symbol != symbolP->sy_value.X_add_symbol + || op_symbol != symbolP->sy_value.X_op_symbol) + { + if (symbolP != &dot_symbol) + { + symbolP = symbol_clone (symbolP, 0); + symbolP->sy_flags.sy_resolving = 0; + } + else + { + symbolP = symbol_temp_new_now (); +#ifdef tc_new_dot_label + tc_new_dot_label (symbolP); +#endif + } + } + + symbolP->sy_value.X_add_symbol = add_symbol; + symbolP->sy_value.X_op_symbol = op_symbol; + } + + return symbolP; +} + +symbolS * +symbol_temp_new (segT seg, valueT ofs, fragS *frag) +{ + return symbol_new (FAKE_LABEL_NAME, seg, ofs, frag); +} + +symbolS * +symbol_temp_new_now (void) +{ + return symbol_temp_new (now_seg, frag_now_fix (), frag_now); +} + +symbolS * +symbol_temp_make (void) +{ + return symbol_make (FAKE_LABEL_NAME); +} + +/* Implement symbol table lookup. + In: A symbol's name as a string: '\0' can't be part of a symbol name. + Out: NULL if the name was not in the symbol table, else the address + of a struct symbol associated with that name. */ + +symbolS * +symbol_find_exact (const char *name) +{ + return symbol_find_exact_noref (name, 0); +} + +symbolS * +symbol_find_exact_noref (const char *name, int noref) +{ + struct local_symbol *locsym; + symbolS* sym; + + locsym = (struct local_symbol *) hash_find (local_hash, name); + if (locsym != NULL) + return (symbolS *) locsym; + + sym = ((symbolS *) hash_find (sy_hash, name)); + + /* Any references to the symbol, except for the reference in + .weakref, must clear this flag, such that the symbol does not + turn into a weak symbol. Note that we don't have to handle the + local_symbol case, since a weakrefd is always promoted out of the + local_symbol table when it is turned into a weak symbol. */ + if (sym && ! noref) + S_CLEAR_WEAKREFD (sym); + + return sym; +} + +symbolS * +symbol_find (const char *name) +{ + return symbol_find_noref (name, 0); +} + +symbolS * +symbol_find_noref (const char *name, int noref) +{ +#ifdef tc_canonicalize_symbol_name + { + char *copy; + size_t len = strlen (name) + 1; + + copy = (char *) alloca (len); + memcpy (copy, name, len); + name = tc_canonicalize_symbol_name (copy); + } +#endif + + if (! symbols_case_sensitive) + { + char *copy; + const char *orig; + unsigned char c; + + orig = name; + name = copy = (char *) alloca (strlen (name) + 1); + + while ((c = *orig++) != '\0') + { + *copy++ = TOUPPER (c); + } + *copy = '\0'; + } + + return symbol_find_exact_noref (name, noref); +} + +/* Once upon a time, symbols were kept in a singly linked list. At + least coff needs to be able to rearrange them from time to time, for + which a doubly linked list is much more convenient. Loic did these + as macros which seemed dangerous to me so they're now functions. + xoxorich. */ + +/* Link symbol ADDME after symbol TARGET in the chain. */ + +void +symbol_append (symbolS *addme, symbolS *target, + symbolS **rootPP, symbolS **lastPP) +{ + if (LOCAL_SYMBOL_CHECK (addme)) + abort (); + if (target != NULL && LOCAL_SYMBOL_CHECK (target)) + abort (); + + if (target == NULL) + { + know (*rootPP == NULL); + know (*lastPP == NULL); + addme->sy_next = NULL; + addme->sy_previous = NULL; + *rootPP = addme; + *lastPP = addme; + return; + } /* if the list is empty */ + + if (target->sy_next != NULL) + { + target->sy_next->sy_previous = addme; + } + else + { + know (*lastPP == target); + *lastPP = addme; + } /* if we have a next */ + + addme->sy_next = target->sy_next; + target->sy_next = addme; + addme->sy_previous = target; + + debug_verify_symchain (symbol_rootP, symbol_lastP); +} + +/* Set the chain pointers of SYMBOL to null. */ + +void +symbol_clear_list_pointers (symbolS *symbolP) +{ + if (LOCAL_SYMBOL_CHECK (symbolP)) + abort (); + symbolP->sy_next = NULL; + symbolP->sy_previous = NULL; +} + +/* Remove SYMBOLP from the list. */ + +void +symbol_remove (symbolS *symbolP, symbolS **rootPP, symbolS **lastPP) +{ + if (LOCAL_SYMBOL_CHECK (symbolP)) + abort (); + + if (symbolP == *rootPP) + { + *rootPP = symbolP->sy_next; + } /* if it was the root */ + + if (symbolP == *lastPP) + { + *lastPP = symbolP->sy_previous; + } /* if it was the tail */ + + if (symbolP->sy_next != NULL) + { + symbolP->sy_next->sy_previous = symbolP->sy_previous; + } /* if not last */ + + if (symbolP->sy_previous != NULL) + { + symbolP->sy_previous->sy_next = symbolP->sy_next; + } /* if not first */ + + debug_verify_symchain (*rootPP, *lastPP); +} + +/* Link symbol ADDME before symbol TARGET in the chain. */ + +void +symbol_insert (symbolS *addme, symbolS *target, + symbolS **rootPP, symbolS **lastPP ATTRIBUTE_UNUSED) +{ + if (LOCAL_SYMBOL_CHECK (addme)) + abort (); + if (LOCAL_SYMBOL_CHECK (target)) + abort (); + + if (target->sy_previous != NULL) + { + target->sy_previous->sy_next = addme; + } + else + { + know (*rootPP == target); + *rootPP = addme; + } /* if not first */ + + addme->sy_previous = target->sy_previous; + target->sy_previous = addme; + addme->sy_next = target; + + debug_verify_symchain (*rootPP, *lastPP); +} + +void +verify_symbol_chain (symbolS *rootP, symbolS *lastP) +{ + symbolS *symbolP = rootP; + + if (symbolP == NULL) + return; + + for (; symbol_next (symbolP) != NULL; symbolP = symbol_next (symbolP)) + { + gas_assert (symbolP->bsym != NULL); + gas_assert (symbolP->sy_flags.sy_local_symbol == 0); + gas_assert (symbolP->sy_next->sy_previous == symbolP); + } + + gas_assert (lastP == symbolP); +} + +#ifdef OBJ_COMPLEX_RELC + +static int +use_complex_relocs_for (symbolS * symp) +{ + switch (symp->sy_value.X_op) + { + case O_constant: + return 0; + + case O_symbol: + case O_symbol_rva: + case O_uminus: + case O_bit_not: + case O_logical_not: + if ( (S_IS_COMMON (symp->sy_value.X_add_symbol) + || S_IS_LOCAL (symp->sy_value.X_add_symbol)) + && + (S_IS_DEFINED (symp->sy_value.X_add_symbol) + && S_GET_SEGMENT (symp->sy_value.X_add_symbol) != expr_section)) + return 0; + break; + + case O_multiply: + case O_divide: + case O_modulus: + case O_left_shift: + case O_right_shift: + case O_bit_inclusive_or: + case O_bit_or_not: + case O_bit_exclusive_or: + case O_bit_and: + case O_add: + case O_subtract: + case O_eq: + case O_ne: + case O_lt: + case O_le: + case O_ge: + case O_gt: + case O_logical_and: + case O_logical_or: + + if ( (S_IS_COMMON (symp->sy_value.X_add_symbol) + || S_IS_LOCAL (symp->sy_value.X_add_symbol)) + && + (S_IS_COMMON (symp->sy_value.X_op_symbol) + || S_IS_LOCAL (symp->sy_value.X_op_symbol)) + + && S_IS_DEFINED (symp->sy_value.X_add_symbol) + && S_IS_DEFINED (symp->sy_value.X_op_symbol) + && S_GET_SEGMENT (symp->sy_value.X_add_symbol) != expr_section + && S_GET_SEGMENT (symp->sy_value.X_op_symbol) != expr_section) + return 0; + break; + + default: + break; + } + return 1; +} +#endif + +static void +report_op_error (symbolS *symp, symbolS *left, operatorT op, symbolS *right) +{ + char *file; + unsigned int line; + segT seg_left = left ? S_GET_SEGMENT (left) : 0; + segT seg_right = S_GET_SEGMENT (right); + const char *opname; + + switch (op) + { + default: + abort (); + return; + + case O_uminus: opname = "-"; break; + case O_bit_not: opname = "~"; break; + case O_logical_not: opname = "!"; break; + case O_multiply: opname = "*"; break; + case O_divide: opname = "/"; break; + case O_modulus: opname = "%"; break; + case O_left_shift: opname = "<<"; break; + case O_right_shift: opname = ">>"; break; + case O_bit_inclusive_or: opname = "|"; break; + case O_bit_or_not: opname = "|~"; break; + case O_bit_exclusive_or: opname = "^"; break; + case O_bit_and: opname = "&"; break; + case O_add: opname = "+"; break; + case O_subtract: opname = "-"; break; + case O_eq: opname = "=="; break; + case O_ne: opname = "!="; break; + case O_lt: opname = "<"; break; + case O_le: opname = "<="; break; + case O_ge: opname = ">="; break; + case O_gt: opname = ">"; break; + case O_logical_and: opname = "&&"; break; + case O_logical_or: opname = "||"; break; + } + + if (expr_symbol_where (symp, &file, &line)) + { + if (left) + as_bad_where (file, line, + _("invalid operands (%s and %s sections) for `%s'"), + seg_left->name, seg_right->name, opname); + else + as_bad_where (file, line, + _("invalid operand (%s section) for `%s'"), + seg_right->name, opname); + } + else + { + const char *sname = S_GET_NAME (symp); + + if (left) + as_bad (_("invalid operands (%s and %s sections) for `%s' when setting `%s'"), + seg_left->name, seg_right->name, opname, sname); + else + as_bad (_("invalid operand (%s section) for `%s' when setting `%s'"), + seg_right->name, opname, sname); + } +} + +/* Resolve the value of a symbol. This is called during the final + pass over the symbol table to resolve any symbols with complex + values. */ + +valueT +resolve_symbol_value (symbolS *symp) +{ + int resolved; + valueT final_val = 0; + segT final_seg; + + if (LOCAL_SYMBOL_CHECK (symp)) + { + struct local_symbol *locsym = (struct local_symbol *) symp; + + final_val = locsym->lsy_value; + if (local_symbol_resolved_p (locsym)) + return final_val; + + final_val += local_symbol_get_frag (locsym)->fr_address / OCTETS_PER_BYTE; + + if (finalize_syms) + { + locsym->lsy_value = final_val; + local_symbol_mark_resolved (locsym); + } + + return final_val; + } + + if (symp->sy_flags.sy_resolved) + { + if (symp->sy_value.X_op == O_constant) + return (valueT) symp->sy_value.X_add_number; + else + return 0; + } + + resolved = 0; + final_seg = S_GET_SEGMENT (symp); + + if (symp->sy_flags.sy_resolving) + { + if (finalize_syms) + as_bad (_("symbol definition loop encountered at `%s'"), + S_GET_NAME (symp)); + final_val = 0; + resolved = 1; + } +#ifdef OBJ_COMPLEX_RELC + else if (final_seg == expr_section + && use_complex_relocs_for (symp)) + { + symbolS * relc_symbol = NULL; + char * relc_symbol_name = NULL; + + relc_symbol_name = symbol_relc_make_expr (& symp->sy_value); + + /* For debugging, print out conversion input & output. */ +#ifdef DEBUG_SYMS + print_expr (& symp->sy_value); + if (relc_symbol_name) + fprintf (stderr, "-> relc symbol: %s\n", relc_symbol_name); +#endif + + if (relc_symbol_name != NULL) + relc_symbol = symbol_new (relc_symbol_name, undefined_section, + 0, & zero_address_frag); + + if (relc_symbol == NULL) + { + as_bad (_("cannot convert expression symbol %s to complex relocation"), + S_GET_NAME (symp)); + resolved = 0; + } + else + { + symbol_table_insert (relc_symbol); + + /* S_CLEAR_EXTERNAL (relc_symbol); */ + if (symp->bsym->flags & BSF_SRELC) + relc_symbol->bsym->flags |= BSF_SRELC; + else + relc_symbol->bsym->flags |= BSF_RELC; + /* symp->bsym->flags |= BSF_RELC; */ + copy_symbol_attributes (symp, relc_symbol); + symp->sy_value.X_op = O_symbol; + symp->sy_value.X_add_symbol = relc_symbol; + symp->sy_value.X_add_number = 0; + resolved = 1; + } + + final_seg = undefined_section; + goto exit_dont_set_value; + } +#endif + else + { + symbolS *add_symbol, *op_symbol; + offsetT left, right; + segT seg_left, seg_right; + operatorT op; + int move_seg_ok; + + symp->sy_flags.sy_resolving = 1; + + /* Help out with CSE. */ + add_symbol = symp->sy_value.X_add_symbol; + op_symbol = symp->sy_value.X_op_symbol; + final_val = symp->sy_value.X_add_number; + op = symp->sy_value.X_op; + + switch (op) + { + default: + BAD_CASE (op); + break; + + case O_absent: + final_val = 0; + /* Fall through. */ + + case O_constant: + final_val += symp->sy_frag->fr_address / OCTETS_PER_BYTE; + if (final_seg == expr_section) + final_seg = absolute_section; + /* Fall through. */ + + case O_register: + resolved = 1; + break; + + case O_symbol: + case O_symbol_rva: + left = resolve_symbol_value (add_symbol); + seg_left = S_GET_SEGMENT (add_symbol); + if (finalize_syms) + symp->sy_value.X_op_symbol = NULL; + + do_symbol: + if (S_IS_WEAKREFR (symp)) + { + gas_assert (final_val == 0); + if (S_IS_WEAKREFR (add_symbol)) + { + gas_assert (add_symbol->sy_value.X_op == O_symbol + && add_symbol->sy_value.X_add_number == 0); + add_symbol = add_symbol->sy_value.X_add_symbol; + gas_assert (! S_IS_WEAKREFR (add_symbol)); + symp->sy_value.X_add_symbol = add_symbol; + } + } + + if (symp->sy_flags.sy_mri_common) + { + /* This is a symbol inside an MRI common section. The + relocation routines are going to handle it specially. + Don't change the value. */ + resolved = symbol_resolved_p (add_symbol); + break; + } + + if (finalize_syms && final_val == 0) + { + if (LOCAL_SYMBOL_CHECK (add_symbol)) + add_symbol = local_symbol_convert ((struct local_symbol *) + add_symbol); + copy_symbol_attributes (symp, add_symbol); + } + + /* If we have equated this symbol to an undefined or common + symbol, keep X_op set to O_symbol, and don't change + X_add_number. This permits the routine which writes out + relocation to detect this case, and convert the + relocation to be against the symbol to which this symbol + is equated. */ + if (! S_IS_DEFINED (add_symbol) +#if defined (OBJ_COFF) && defined (TE_PE) + || S_IS_WEAK (add_symbol) +#endif + || S_IS_COMMON (add_symbol)) + { + if (finalize_syms) + { + symp->sy_value.X_op = O_symbol; + symp->sy_value.X_add_symbol = add_symbol; + symp->sy_value.X_add_number = final_val; + /* Use X_op_symbol as a flag. */ + symp->sy_value.X_op_symbol = add_symbol; + } + final_seg = seg_left; + final_val = 0; + resolved = symbol_resolved_p (add_symbol); + symp->sy_flags.sy_resolving = 0; + goto exit_dont_set_value; + } + else if (finalize_syms + && ((final_seg == expr_section && seg_left != expr_section) + || symbol_shadow_p (symp))) + { + /* If the symbol is an expression symbol, do similarly + as for undefined and common syms above. Handles + "sym +/- expr" where "expr" cannot be evaluated + immediately, and we want relocations to be against + "sym", eg. because it is weak. */ + symp->sy_value.X_op = O_symbol; + symp->sy_value.X_add_symbol = add_symbol; + symp->sy_value.X_add_number = final_val; + symp->sy_value.X_op_symbol = add_symbol; + final_seg = seg_left; + final_val += symp->sy_frag->fr_address + left; + resolved = symbol_resolved_p (add_symbol); + symp->sy_flags.sy_resolving = 0; + goto exit_dont_set_value; + } + else + { + final_val += symp->sy_frag->fr_address + left; + if (final_seg == expr_section || final_seg == undefined_section) + final_seg = seg_left; + } + + resolved = symbol_resolved_p (add_symbol); + if (S_IS_WEAKREFR (symp)) + goto exit_dont_set_value; + break; + + case O_uminus: + case O_bit_not: + case O_logical_not: + left = resolve_symbol_value (add_symbol); + seg_left = S_GET_SEGMENT (add_symbol); + + /* By reducing these to the relevant dyadic operator, we get + !S -> S == 0 permitted on anything, + -S -> 0 - S only permitted on absolute + ~S -> S ^ ~0 only permitted on absolute */ + if (op != O_logical_not && seg_left != absolute_section + && finalize_syms) + report_op_error (symp, NULL, op, add_symbol); + + if (final_seg == expr_section || final_seg == undefined_section) + final_seg = absolute_section; + + if (op == O_uminus) + left = -left; + else if (op == O_logical_not) + left = !left; + else + left = ~left; + + final_val += left + symp->sy_frag->fr_address; + + resolved = symbol_resolved_p (add_symbol); + break; + + case O_multiply: + case O_divide: + case O_modulus: + case O_left_shift: + case O_right_shift: + case O_bit_inclusive_or: + case O_bit_or_not: + case O_bit_exclusive_or: + case O_bit_and: + case O_add: + case O_subtract: + case O_eq: + case O_ne: + case O_lt: + case O_le: + case O_ge: + case O_gt: + case O_logical_and: + case O_logical_or: + left = resolve_symbol_value (add_symbol); + right = resolve_symbol_value (op_symbol); + seg_left = S_GET_SEGMENT (add_symbol); + seg_right = S_GET_SEGMENT (op_symbol); + + /* Simplify addition or subtraction of a constant by folding the + constant into X_add_number. */ + if (op == O_add) + { + if (seg_right == absolute_section) + { + final_val += right; + goto do_symbol; + } + else if (seg_left == absolute_section) + { + final_val += left; + add_symbol = op_symbol; + left = right; + seg_left = seg_right; + goto do_symbol; + } + } + else if (op == O_subtract) + { + if (seg_right == absolute_section) + { + final_val -= right; + goto do_symbol; + } + } + + move_seg_ok = 1; + /* Equality and non-equality tests are permitted on anything. + Subtraction, and other comparison operators are permitted if + both operands are in the same section. Otherwise, both + operands must be absolute. We already handled the case of + addition or subtraction of a constant above. This will + probably need to be changed for an object file format which + supports arbitrary expressions, such as IEEE-695. */ + if (!(seg_left == absolute_section + && seg_right == absolute_section) + && !(op == O_eq || op == O_ne) + && !((op == O_subtract + || op == O_lt || op == O_le || op == O_ge || op == O_gt) + && seg_left == seg_right + && (seg_left != undefined_section + || add_symbol == op_symbol))) + { + /* Don't emit messages unless we're finalizing the symbol value, + otherwise we may get the same message multiple times. */ + if (finalize_syms) + report_op_error (symp, add_symbol, op, op_symbol); + /* However do not move the symbol into the absolute section + if it cannot currently be resolved - this would confuse + other parts of the assembler into believing that the + expression had been evaluated to zero. */ + else + move_seg_ok = 0; + } + + if (move_seg_ok + && (final_seg == expr_section || final_seg == undefined_section)) + final_seg = absolute_section; + + /* Check for division by zero. */ + if ((op == O_divide || op == O_modulus) && right == 0) + { + /* If seg_right is not absolute_section, then we've + already issued a warning about using a bad symbol. */ + if (seg_right == absolute_section && finalize_syms) + { + char *file; + unsigned int line; + + if (expr_symbol_where (symp, &file, &line)) + as_bad_where (file, line, _("division by zero")); + else + as_bad (_("division by zero when setting `%s'"), + S_GET_NAME (symp)); + } + + right = 1; + } + + switch (symp->sy_value.X_op) + { + case O_multiply: left *= right; break; + case O_divide: left /= right; break; + case O_modulus: left %= right; break; + case O_left_shift: left <<= right; break; + case O_right_shift: left >>= right; break; + case O_bit_inclusive_or: left |= right; break; + case O_bit_or_not: left |= ~right; break; + case O_bit_exclusive_or: left ^= right; break; + case O_bit_and: left &= right; break; + case O_add: left += right; break; + case O_subtract: left -= right; break; + case O_eq: + case O_ne: + left = (left == right && seg_left == seg_right + && (seg_left != undefined_section + || add_symbol == op_symbol) + ? ~ (offsetT) 0 : 0); + if (symp->sy_value.X_op == O_ne) + left = ~left; + break; + case O_lt: left = left < right ? ~ (offsetT) 0 : 0; break; + case O_le: left = left <= right ? ~ (offsetT) 0 : 0; break; + case O_ge: left = left >= right ? ~ (offsetT) 0 : 0; break; + case O_gt: left = left > right ? ~ (offsetT) 0 : 0; break; + case O_logical_and: left = left && right; break; + case O_logical_or: left = left || right; break; + default: abort (); + } + + final_val += symp->sy_frag->fr_address + left; + if (final_seg == expr_section || final_seg == undefined_section) + { + if (seg_left == undefined_section + || seg_right == undefined_section) + final_seg = undefined_section; + else if (seg_left == absolute_section) + final_seg = seg_right; + else + final_seg = seg_left; + } + resolved = (symbol_resolved_p (add_symbol) + && symbol_resolved_p (op_symbol)); + break; + + case O_big: + case O_illegal: + /* Give an error (below) if not in expr_section. We don't + want to worry about expr_section symbols, because they + are fictional (they are created as part of expression + resolution), and any problems may not actually mean + anything. */ + break; + } + + symp->sy_flags.sy_resolving = 0; + } + + if (finalize_syms) + S_SET_VALUE (symp, final_val); + +exit_dont_set_value: + /* Always set the segment, even if not finalizing the value. + The segment is used to determine whether a symbol is defined. */ + S_SET_SEGMENT (symp, final_seg); + + /* Don't worry if we can't resolve an expr_section symbol. */ + if (finalize_syms) + { + if (resolved) + symp->sy_flags.sy_resolved = 1; + else if (S_GET_SEGMENT (symp) != expr_section) + { + as_bad (_("can't resolve value for symbol `%s'"), + S_GET_NAME (symp)); + symp->sy_flags.sy_resolved = 1; + } + } + + return final_val; +} + +static void resolve_local_symbol (const char *, void *); + +/* A static function passed to hash_traverse. */ + +static void +resolve_local_symbol (const char *key ATTRIBUTE_UNUSED, void *value) +{ + if (value != NULL) + resolve_symbol_value ((symbolS *) value); +} + +/* Resolve all local symbols. */ + +void +resolve_local_symbol_values (void) +{ + hash_traverse (local_hash, resolve_local_symbol); +} + +/* Obtain the current value of a symbol without changing any + sub-expressions used. */ + +int +snapshot_symbol (symbolS **symbolPP, valueT *valueP, segT *segP, fragS **fragPP) +{ + symbolS *symbolP = *symbolPP; + + if (LOCAL_SYMBOL_CHECK (symbolP)) + { + struct local_symbol *locsym = (struct local_symbol *) symbolP; + + *valueP = locsym->lsy_value; + *segP = locsym->lsy_section; + *fragPP = local_symbol_get_frag (locsym); + } + else + { + expressionS exp = symbolP->sy_value; + + if (!symbolP->sy_flags.sy_resolved && exp.X_op != O_illegal) + { + int resolved; + + if (symbolP->sy_flags.sy_resolving) + return 0; + symbolP->sy_flags.sy_resolving = 1; + resolved = resolve_expression (&exp); + symbolP->sy_flags.sy_resolving = 0; + if (!resolved) + return 0; + + switch (exp.X_op) + { + case O_constant: + case O_register: + if (!symbol_equated_p (symbolP)) + break; + /* Fall thru. */ + case O_symbol: + case O_symbol_rva: + symbolP = exp.X_add_symbol; + break; + default: + return 0; + } + } + + *symbolPP = symbolP; + *valueP = exp.X_add_number; + *segP = symbolP->bsym->section; + *fragPP = symbolP->sy_frag; + + if (*segP == expr_section) + switch (exp.X_op) + { + case O_constant: *segP = absolute_section; break; + case O_register: *segP = reg_section; break; + default: break; + } + } + + return 1; +} + +/* Dollar labels look like a number followed by a dollar sign. Eg, "42$". + They are *really* local. That is, they go out of scope whenever we see a + label that isn't local. Also, like fb labels, there can be multiple + instances of a dollar label. Therefor, we name encode each instance with + the instance number, keep a list of defined symbols separate from the real + symbol table, and we treat these buggers as a sparse array. */ + +static long *dollar_labels; +static long *dollar_label_instances; +static char *dollar_label_defines; +static unsigned long dollar_label_count; +static unsigned long dollar_label_max; + +int +dollar_label_defined (long label) +{ + long *i; + + know ((dollar_labels != NULL) || (dollar_label_count == 0)); + + for (i = dollar_labels; i < dollar_labels + dollar_label_count; ++i) + if (*i == label) + return dollar_label_defines[i - dollar_labels]; + + /* If we get here, label isn't defined. */ + return 0; +} + +static long +dollar_label_instance (long label) +{ + long *i; + + know ((dollar_labels != NULL) || (dollar_label_count == 0)); + + for (i = dollar_labels; i < dollar_labels + dollar_label_count; ++i) + if (*i == label) + return (dollar_label_instances[i - dollar_labels]); + + /* If we get here, we haven't seen the label before. + Therefore its instance count is zero. */ + return 0; +} + +void +dollar_label_clear (void) +{ + memset (dollar_label_defines, '\0', (unsigned int) dollar_label_count); +} + +#define DOLLAR_LABEL_BUMP_BY 10 + +void +define_dollar_label (long label) +{ + long *i; + + for (i = dollar_labels; i < dollar_labels + dollar_label_count; ++i) + if (*i == label) + { + ++dollar_label_instances[i - dollar_labels]; + dollar_label_defines[i - dollar_labels] = 1; + return; + } + + /* If we get to here, we don't have label listed yet. */ + + if (dollar_labels == NULL) + { + dollar_labels = (long *) xmalloc (DOLLAR_LABEL_BUMP_BY * sizeof (long)); + dollar_label_instances = (long *) xmalloc (DOLLAR_LABEL_BUMP_BY * sizeof (long)); + dollar_label_defines = (char *) xmalloc (DOLLAR_LABEL_BUMP_BY); + dollar_label_max = DOLLAR_LABEL_BUMP_BY; + dollar_label_count = 0; + } + else if (dollar_label_count == dollar_label_max) + { + dollar_label_max += DOLLAR_LABEL_BUMP_BY; + dollar_labels = (long *) xrealloc ((char *) dollar_labels, + dollar_label_max * sizeof (long)); + dollar_label_instances = (long *) xrealloc ((char *) dollar_label_instances, + dollar_label_max * sizeof (long)); + dollar_label_defines = (char *) xrealloc (dollar_label_defines, dollar_label_max); + } /* if we needed to grow */ + + dollar_labels[dollar_label_count] = label; + dollar_label_instances[dollar_label_count] = 1; + dollar_label_defines[dollar_label_count] = 1; + ++dollar_label_count; +} + +/* Caller must copy returned name: we re-use the area for the next name. + + The mth occurence of label n: is turned into the symbol "Ln^Am" + where n is the label number and m is the instance number. "L" makes + it a label discarded unless debugging and "^A"('\1') ensures no + ordinary symbol SHOULD get the same name as a local label + symbol. The first "4:" is "L4^A1" - the m numbers begin at 1. + + fb labels get the same treatment, except that ^B is used in place + of ^A. */ + +char * /* Return local label name. */ +dollar_label_name (register long n, /* we just saw "n$:" : n a number. */ + register int augend /* 0 for current instance, 1 for new instance. */) +{ + long i; + /* Returned to caller, then copied. Used for created names ("4f"). */ + static char symbol_name_build[24]; + register char *p; + register char *q; + char symbol_name_temporary[20]; /* Build up a number, BACKWARDS. */ + + know (n >= 0); + know (augend == 0 || augend == 1); + p = symbol_name_build; +#ifdef LOCAL_LABEL_PREFIX + *p++ = LOCAL_LABEL_PREFIX; +#endif + *p++ = 'L'; + + /* Next code just does sprintf( {}, "%d", n); */ + /* Label number. */ + q = symbol_name_temporary; + for (*q++ = 0, i = n; i; ++q) + { + *q = i % 10 + '0'; + i /= 10; + } + while ((*p = *--q) != '\0') + ++p; + + *p++ = DOLLAR_LABEL_CHAR; /* ^A */ + + /* Instance number. */ + q = symbol_name_temporary; + for (*q++ = 0, i = dollar_label_instance (n) + augend; i; ++q) + { + *q = i % 10 + '0'; + i /= 10; + } + while ((*p++ = *--q) != '\0'); + + /* The label, as a '\0' ended string, starts at symbol_name_build. */ + return symbol_name_build; +} + +/* Somebody else's idea of local labels. They are made by "n:" where n + is any decimal digit. Refer to them with + "nb" for previous (backward) n: + or "nf" for next (forward) n:. + + We do a little better and let n be any number, not just a single digit, but + since the other guy's assembler only does ten, we treat the first ten + specially. + + Like someone else's assembler, we have one set of local label counters for + entire assembly, not one set per (sub)segment like in most assemblers. This + implies that one can refer to a label in another segment, and indeed some + crufty compilers have done just that. + + Since there could be a LOT of these things, treat them as a sparse + array. */ + +#define FB_LABEL_SPECIAL (10) + +static long fb_low_counter[FB_LABEL_SPECIAL]; +static long *fb_labels; +static long *fb_label_instances; +static long fb_label_count; +static long fb_label_max; + +/* This must be more than FB_LABEL_SPECIAL. */ +#define FB_LABEL_BUMP_BY (FB_LABEL_SPECIAL + 6) + +static void +fb_label_init (void) +{ + memset ((void *) fb_low_counter, '\0', sizeof (fb_low_counter)); +} + +/* Add one to the instance number of this fb label. */ + +void +fb_label_instance_inc (long label) +{ + long *i; + + if ((unsigned long) label < FB_LABEL_SPECIAL) + { + ++fb_low_counter[label]; + return; + } + + if (fb_labels != NULL) + { + for (i = fb_labels + FB_LABEL_SPECIAL; + i < fb_labels + fb_label_count; ++i) + { + if (*i == label) + { + ++fb_label_instances[i - fb_labels]; + return; + } /* if we find it */ + } /* for each existing label */ + } + + /* If we get to here, we don't have label listed yet. */ + + if (fb_labels == NULL) + { + fb_labels = (long *) xmalloc (FB_LABEL_BUMP_BY * sizeof (long)); + fb_label_instances = (long *) xmalloc (FB_LABEL_BUMP_BY * sizeof (long)); + fb_label_max = FB_LABEL_BUMP_BY; + fb_label_count = FB_LABEL_SPECIAL; + + } + else if (fb_label_count == fb_label_max) + { + fb_label_max += FB_LABEL_BUMP_BY; + fb_labels = (long *) xrealloc ((char *) fb_labels, + fb_label_max * sizeof (long)); + fb_label_instances = (long *) xrealloc ((char *) fb_label_instances, + fb_label_max * sizeof (long)); + } /* if we needed to grow */ + + fb_labels[fb_label_count] = label; + fb_label_instances[fb_label_count] = 1; + ++fb_label_count; +} + +static long +fb_label_instance (long label) +{ + long *i; + + if ((unsigned long) label < FB_LABEL_SPECIAL) + { + return (fb_low_counter[label]); + } + + if (fb_labels != NULL) + { + for (i = fb_labels + FB_LABEL_SPECIAL; + i < fb_labels + fb_label_count; ++i) + { + if (*i == label) + { + return (fb_label_instances[i - fb_labels]); + } /* if we find it */ + } /* for each existing label */ + } + + /* We didn't find the label, so this must be a reference to the + first instance. */ + return 0; +} + +/* Caller must copy returned name: we re-use the area for the next name. + + The mth occurence of label n: is turned into the symbol "Ln^Bm" + where n is the label number and m is the instance number. "L" makes + it a label discarded unless debugging and "^B"('\2') ensures no + ordinary symbol SHOULD get the same name as a local label + symbol. The first "4:" is "L4^B1" - the m numbers begin at 1. + + dollar labels get the same treatment, except that ^A is used in + place of ^B. */ + +char * /* Return local label name. */ +fb_label_name (long n, /* We just saw "n:", "nf" or "nb" : n a number. */ + long augend /* 0 for nb, 1 for n:, nf. */) +{ + long i; + /* Returned to caller, then copied. Used for created names ("4f"). */ + static char symbol_name_build[24]; + register char *p; + register char *q; + char symbol_name_temporary[20]; /* Build up a number, BACKWARDS. */ + + know (n >= 0); +#ifdef TC_MMIX + know ((unsigned long) augend <= 2 /* See mmix_fb_label. */); +#else + know ((unsigned long) augend <= 1); +#endif + p = symbol_name_build; +#ifdef LOCAL_LABEL_PREFIX + *p++ = LOCAL_LABEL_PREFIX; +#endif + *p++ = 'L'; + + /* Next code just does sprintf( {}, "%d", n); */ + /* Label number. */ + q = symbol_name_temporary; + for (*q++ = 0, i = n; i; ++q) + { + *q = i % 10 + '0'; + i /= 10; + } + while ((*p = *--q) != '\0') + ++p; + + *p++ = LOCAL_LABEL_CHAR; /* ^B */ + + /* Instance number. */ + q = symbol_name_temporary; + for (*q++ = 0, i = fb_label_instance (n) + augend; i; ++q) + { + *q = i % 10 + '0'; + i /= 10; + } + while ((*p++ = *--q) != '\0'); + + /* The label, as a '\0' ended string, starts at symbol_name_build. */ + return (symbol_name_build); +} + +/* Decode name that may have been generated by foo_label_name() above. + If the name wasn't generated by foo_label_name(), then return it + unaltered. This is used for error messages. */ + +char * +decode_local_label_name (char *s) +{ + char *p; + char *symbol_decode; + int label_number; + int instance_number; + char *type; + const char *message_format; + int lindex = 0; + +#ifdef LOCAL_LABEL_PREFIX + if (s[lindex] == LOCAL_LABEL_PREFIX) + ++lindex; +#endif + + if (s[lindex] != 'L') + return s; + + for (label_number = 0, p = s + lindex + 1; ISDIGIT (*p); ++p) + label_number = (10 * label_number) + *p - '0'; + + if (*p == DOLLAR_LABEL_CHAR) + type = "dollar"; + else if (*p == LOCAL_LABEL_CHAR) + type = "fb"; + else + return s; + + for (instance_number = 0, p++; ISDIGIT (*p); ++p) + instance_number = (10 * instance_number) + *p - '0'; + + message_format = _("\"%d\" (instance number %d of a %s label)"); + symbol_decode = (char *) obstack_alloc (¬es, strlen (message_format) + 30); + sprintf (symbol_decode, message_format, label_number, instance_number, type); + + return symbol_decode; +} + +/* Get the value of a symbol. */ + +valueT +S_GET_VALUE (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return resolve_symbol_value (s); + + if (!s->sy_flags.sy_resolved) + { + valueT val = resolve_symbol_value (s); + if (!finalize_syms) + return val; + } + if (S_IS_WEAKREFR (s)) + return S_GET_VALUE (s->sy_value.X_add_symbol); + + if (s->sy_value.X_op != O_constant) + { + if (! s->sy_flags.sy_resolved + || s->sy_value.X_op != O_symbol + || (S_IS_DEFINED (s) && ! S_IS_COMMON (s))) + as_bad (_("attempt to get value of unresolved symbol `%s'"), + S_GET_NAME (s)); + } + return (valueT) s->sy_value.X_add_number; +} + +/* Set the value of a symbol. */ + +void +S_SET_VALUE (symbolS *s, valueT val) +{ + if (LOCAL_SYMBOL_CHECK (s)) + { + ((struct local_symbol *) s)->lsy_value = val; + return; + } + + s->sy_value.X_op = O_constant; + s->sy_value.X_add_number = (offsetT) val; + s->sy_value.X_unsigned = 0; + S_CLEAR_WEAKREFR (s); +} + +void +copy_symbol_attributes (symbolS *dest, symbolS *src) +{ + if (LOCAL_SYMBOL_CHECK (dest)) + dest = local_symbol_convert ((struct local_symbol *) dest); + if (LOCAL_SYMBOL_CHECK (src)) + src = local_symbol_convert ((struct local_symbol *) src); + + /* In an expression, transfer the settings of these flags. + The user can override later, of course. */ +#define COPIED_SYMFLAGS (BSF_FUNCTION | BSF_OBJECT \ + | BSF_GNU_INDIRECT_FUNCTION) + dest->bsym->flags |= src->bsym->flags & COPIED_SYMFLAGS; + +#ifdef OBJ_COPY_SYMBOL_ATTRIBUTES + OBJ_COPY_SYMBOL_ATTRIBUTES (dest, src); +#endif + +#ifdef TC_COPY_SYMBOL_ATTRIBUTES + TC_COPY_SYMBOL_ATTRIBUTES (dest, src); +#endif +} + +int +S_IS_FUNCTION (symbolS *s) +{ + flagword flags; + + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + + flags = s->bsym->flags; + + return (flags & BSF_FUNCTION) != 0; +} + +int +S_IS_EXTERNAL (symbolS *s) +{ + flagword flags; + + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + + flags = s->bsym->flags; + + /* Sanity check. */ + if ((flags & BSF_LOCAL) && (flags & BSF_GLOBAL)) + abort (); + + return (flags & BSF_GLOBAL) != 0; +} + +int +S_IS_WEAK (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + /* Conceptually, a weakrefr is weak if the referenced symbol is. We + could probably handle a WEAKREFR as always weak though. E.g., if + the referenced symbol has lost its weak status, there's no reason + to keep handling the weakrefr as if it was weak. */ + if (S_IS_WEAKREFR (s)) + return S_IS_WEAK (s->sy_value.X_add_symbol); + return (s->bsym->flags & BSF_WEAK) != 0; +} + +int +S_IS_WEAKREFR (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + return s->sy_flags.sy_weakrefr != 0; +} + +int +S_IS_WEAKREFD (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + return s->sy_flags.sy_weakrefd != 0; +} + +int +S_IS_COMMON (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + return bfd_is_com_section (s->bsym->section); +} + +int +S_IS_DEFINED (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return ((struct local_symbol *) s)->lsy_section != undefined_section; + return s->bsym->section != undefined_section; +} + + +#ifndef EXTERN_FORCE_RELOC +#define EXTERN_FORCE_RELOC IS_ELF +#endif + +/* Return true for symbols that should not be reduced to section + symbols or eliminated from expressions, because they may be + overridden by the linker. */ +int +S_FORCE_RELOC (symbolS *s, int strict) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return ((struct local_symbol *) s)->lsy_section == undefined_section; + + return ((strict + && ((s->bsym->flags & BSF_WEAK) != 0 + || (EXTERN_FORCE_RELOC + && (s->bsym->flags & BSF_GLOBAL) != 0))) + || (s->bsym->flags & BSF_GNU_INDIRECT_FUNCTION) != 0 + || s->bsym->section == undefined_section + || bfd_is_com_section (s->bsym->section)); +} + +int +S_IS_DEBUG (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + if (s->bsym->flags & BSF_DEBUGGING) + return 1; + return 0; +} + +int +S_IS_LOCAL (symbolS *s) +{ + flagword flags; + const char *name; + + if (LOCAL_SYMBOL_CHECK (s)) + return 1; + + flags = s->bsym->flags; + + /* Sanity check. */ + if ((flags & BSF_LOCAL) && (flags & BSF_GLOBAL)) + abort (); + + if (bfd_get_section (s->bsym) == reg_section) + return 1; + + if (flag_strip_local_absolute + /* Keep BSF_FILE symbols in order to allow debuggers to identify + the source file even when the object file is stripped. */ + && (flags & (BSF_GLOBAL | BSF_FILE)) == 0 + && bfd_get_section (s->bsym) == absolute_section) + return 1; + + name = S_GET_NAME (s); + return (name != NULL + && ! S_IS_DEBUG (s) + && (strchr (name, DOLLAR_LABEL_CHAR) + || strchr (name, LOCAL_LABEL_CHAR) + || TC_LABEL_IS_LOCAL (name) + || (! flag_keep_locals + && (bfd_is_local_label (stdoutput, s->bsym) + || (flag_mri + && name[0] == '?' + && name[1] == '?'))))); +} + +int +S_IS_STABD (symbolS *s) +{ + return S_GET_NAME (s) == 0; +} + +int +S_CAN_BE_REDEFINED (const symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return (local_symbol_get_frag ((struct local_symbol *) s) + == &predefined_address_frag); + /* Permit register names to be redefined. */ + return s->bsym->section == reg_section; +} + +int +S_IS_VOLATILE (const symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + return s->sy_flags.sy_volatile; +} + +int +S_IS_FORWARD_REF (const symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + return s->sy_flags.sy_forward_ref; +} + +const char * +S_GET_NAME (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return ((struct local_symbol *) s)->lsy_name; + return s->bsym->name; +} + +segT +S_GET_SEGMENT (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return ((struct local_symbol *) s)->lsy_section; + return s->bsym->section; +} + +void +S_SET_SEGMENT (symbolS *s, segT seg) +{ + /* Don't reassign section symbols. The direct reason is to prevent seg + faults assigning back to const global symbols such as *ABS*, but it + shouldn't happen anyway. */ + + if (LOCAL_SYMBOL_CHECK (s)) + { + if (seg == reg_section) + s = local_symbol_convert ((struct local_symbol *) s); + else + { + ((struct local_symbol *) s)->lsy_section = seg; + return; + } + } + + if (s->bsym->flags & BSF_SECTION_SYM) + { + if (s->bsym->section != seg) + abort (); + } + else + s->bsym->section = seg; +} + +void +S_SET_EXTERNAL (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + if ((s->bsym->flags & BSF_WEAK) != 0) + { + /* Let .weak override .global. */ + return; + } + if (s->bsym->flags & BSF_SECTION_SYM) + { + char * file; + unsigned int line; + + /* Do not reassign section symbols. */ + as_where (& file, & line); + as_warn_where (file, line, + _("section symbols are already global")); + return; + } +#ifndef TC_GLOBAL_REGISTER_SYMBOL_OK + if (S_GET_SEGMENT (s) == reg_section) + { + as_bad ("can't make register symbol `%s' global", + S_GET_NAME (s)); + return; + } +#endif + s->bsym->flags |= BSF_GLOBAL; + s->bsym->flags &= ~(BSF_LOCAL | BSF_WEAK); + +#ifdef TE_PE + if (! an_external_name && S_GET_NAME(s)[0] != '.') + an_external_name = S_GET_NAME (s); +#endif +} + +void +S_CLEAR_EXTERNAL (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return; + if ((s->bsym->flags & BSF_WEAK) != 0) + { + /* Let .weak override. */ + return; + } + s->bsym->flags |= BSF_LOCAL; + s->bsym->flags &= ~(BSF_GLOBAL | BSF_WEAK); +} + +void +S_SET_WEAK (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); +#ifdef obj_set_weak_hook + obj_set_weak_hook (s); +#endif + s->bsym->flags |= BSF_WEAK; + s->bsym->flags &= ~(BSF_GLOBAL | BSF_LOCAL); +} + +void +S_SET_WEAKREFR (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + s->sy_flags.sy_weakrefr = 1; + /* If the alias was already used, make sure we mark the target as + used as well, otherwise it might be dropped from the symbol + table. This may have unintended side effects if the alias is + later redirected to another symbol, such as keeping the unused + previous target in the symbol table. Since it will be weak, it's + not a big deal. */ + if (s->sy_flags.sy_used) + symbol_mark_used (s->sy_value.X_add_symbol); +} + +void +S_CLEAR_WEAKREFR (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return; + s->sy_flags.sy_weakrefr = 0; +} + +void +S_SET_WEAKREFD (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + s->sy_flags.sy_weakrefd = 1; + S_SET_WEAK (s); +} + +void +S_CLEAR_WEAKREFD (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return; + if (s->sy_flags.sy_weakrefd) + { + s->sy_flags.sy_weakrefd = 0; + /* If a weakref target symbol is weak, then it was never + referenced directly before, not even in a .global directive, + so decay it to local. If it remains undefined, it will be + later turned into a global, like any other undefined + symbol. */ + if (s->bsym->flags & BSF_WEAK) + { +#ifdef obj_clear_weak_hook + obj_clear_weak_hook (s); +#endif + s->bsym->flags &= ~BSF_WEAK; + s->bsym->flags |= BSF_LOCAL; + } + } +} + +void +S_SET_THREAD_LOCAL (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + if (bfd_is_com_section (s->bsym->section) + && (s->bsym->flags & BSF_THREAD_LOCAL) != 0) + return; + s->bsym->flags |= BSF_THREAD_LOCAL; + if ((s->bsym->flags & BSF_FUNCTION) != 0) + as_bad (_("Accessing function `%s' as thread-local object"), + S_GET_NAME (s)); + else if (! bfd_is_und_section (s->bsym->section) + && (s->bsym->section->flags & SEC_THREAD_LOCAL) == 0) + as_bad (_("Accessing `%s' as thread-local object"), + S_GET_NAME (s)); +} + +void +S_SET_NAME (symbolS *s, const char *name) +{ + if (LOCAL_SYMBOL_CHECK (s)) + { + ((struct local_symbol *) s)->lsy_name = name; + return; + } + s->bsym->name = name; +} + +void +S_SET_VOLATILE (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + s->sy_flags.sy_volatile = 1; +} + +void +S_CLEAR_VOLATILE (symbolS *s) +{ + if (!LOCAL_SYMBOL_CHECK (s)) + s->sy_flags.sy_volatile = 0; +} + +void +S_SET_FORWARD_REF (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + s->sy_flags.sy_forward_ref = 1; +} + +/* Return the previous symbol in a chain. */ + +symbolS * +symbol_previous (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + abort (); + return s->sy_previous; +} + +/* Return the next symbol in a chain. */ + +symbolS * +symbol_next (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + abort (); + return s->sy_next; +} + +/* Return a pointer to the value of a symbol as an expression. */ + +expressionS * +symbol_get_value_expression (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + return &s->sy_value; +} + +/* Set the value of a symbol to an expression. */ + +void +symbol_set_value_expression (symbolS *s, const expressionS *exp) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + s->sy_value = *exp; + S_CLEAR_WEAKREFR (s); +} + +/* Return whether 2 symbols are the same. */ + +int +symbol_same_p (symbolS *s1, symbolS *s2) +{ + if (s1->sy_flags.sy_local_symbol + && local_symbol_converted_p ((struct local_symbol *) s1)) + s1 = local_symbol_get_real_symbol ((struct local_symbol *) s1); + if (s2->sy_flags.sy_local_symbol + && local_symbol_converted_p ((struct local_symbol *) s2)) + s2 = local_symbol_get_real_symbol ((struct local_symbol *) s2); + return s1 == s2; +} + +/* Return a pointer to the X_add_number component of a symbol. */ + +offsetT * +symbol_X_add_number (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return (offsetT *) &((struct local_symbol *) s)->lsy_value; + + return &s->sy_value.X_add_number; +} + +/* Set the value of SYM to the current position in the current segment. */ + +void +symbol_set_value_now (symbolS *sym) +{ + S_SET_SEGMENT (sym, now_seg); + S_SET_VALUE (sym, frag_now_fix ()); + symbol_set_frag (sym, frag_now); +} + +/* Set the frag of a symbol. */ + +void +symbol_set_frag (symbolS *s, fragS *f) +{ + if (LOCAL_SYMBOL_CHECK (s)) + { + local_symbol_set_frag ((struct local_symbol *) s, f); + return; + } + s->sy_frag = f; + S_CLEAR_WEAKREFR (s); +} + +/* Return the frag of a symbol. */ + +fragS * +symbol_get_frag (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return local_symbol_get_frag ((struct local_symbol *) s); + return s->sy_frag; +} + +/* Mark a symbol as having been used. */ + +void +symbol_mark_used (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return; + s->sy_flags.sy_used = 1; + if (S_IS_WEAKREFR (s)) + symbol_mark_used (s->sy_value.X_add_symbol); +} + +/* Clear the mark of whether a symbol has been used. */ + +void +symbol_clear_used (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + s->sy_flags.sy_used = 0; +} + +/* Return whether a symbol has been used. */ + +int +symbol_used_p (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 1; + return s->sy_flags.sy_used; +} + +/* Mark a symbol as having been used in a reloc. */ + +void +symbol_mark_used_in_reloc (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + s->sy_flags.sy_used_in_reloc = 1; +} + +/* Clear the mark of whether a symbol has been used in a reloc. */ + +void +symbol_clear_used_in_reloc (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return; + s->sy_flags.sy_used_in_reloc = 0; +} + +/* Return whether a symbol has been used in a reloc. */ + +int +symbol_used_in_reloc_p (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + return s->sy_flags.sy_used_in_reloc; +} + +/* Mark a symbol as an MRI common symbol. */ + +void +symbol_mark_mri_common (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + s->sy_flags.sy_mri_common = 1; +} + +/* Clear the mark of whether a symbol is an MRI common symbol. */ + +void +symbol_clear_mri_common (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return; + s->sy_flags.sy_mri_common = 0; +} + +/* Return whether a symbol is an MRI common symbol. */ + +int +symbol_mri_common_p (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + return s->sy_flags.sy_mri_common; +} + +/* Mark a symbol as having been written. */ + +void +symbol_mark_written (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return; + s->sy_flags.sy_written = 1; +} + +/* Clear the mark of whether a symbol has been written. */ + +void +symbol_clear_written (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return; + s->sy_flags.sy_written = 0; +} + +/* Return whether a symbol has been written. */ + +int +symbol_written_p (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + return s->sy_flags.sy_written; +} + +/* Mark a symbol has having been resolved. */ + +void +symbol_mark_resolved (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + { + local_symbol_mark_resolved ((struct local_symbol *) s); + return; + } + s->sy_flags.sy_resolved = 1; +} + +/* Return whether a symbol has been resolved. */ + +int +symbol_resolved_p (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return local_symbol_resolved_p ((struct local_symbol *) s); + return s->sy_flags.sy_resolved; +} + +/* Return whether a symbol is a section symbol. */ + +int +symbol_section_p (symbolS *s ATTRIBUTE_UNUSED) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + return (s->bsym->flags & BSF_SECTION_SYM) != 0; +} + +/* Return whether a symbol is equated to another symbol. */ + +int +symbol_equated_p (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + return s->sy_value.X_op == O_symbol; +} + +/* Return whether a symbol is equated to another symbol, and should be + treated specially when writing out relocs. */ + +int +symbol_equated_reloc_p (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + /* X_op_symbol, normally not used for O_symbol, is set by + resolve_symbol_value to flag expression syms that have been + equated. */ + return (s->sy_value.X_op == O_symbol +#if defined (OBJ_COFF) && defined (TE_PE) + && ! S_IS_WEAK (s) +#endif + && ((s->sy_flags.sy_resolved && s->sy_value.X_op_symbol != NULL) + || ! S_IS_DEFINED (s) + || S_IS_COMMON (s))); +} + +/* Return whether a symbol has a constant value. */ + +int +symbol_constant_p (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 1; + return s->sy_value.X_op == O_constant; +} + +/* Return whether a symbol was cloned and thus removed from the global + symbol list. */ + +int +symbol_shadow_p (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + return 0; + return s->sy_next == s; +} + +/* Return the BFD symbol for a symbol. */ + +asymbol * +symbol_get_bfdsym (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + return s->bsym; +} + +/* Set the BFD symbol for a symbol. */ + +void +symbol_set_bfdsym (symbolS *s, asymbol *bsym) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + /* Usually, it is harmless to reset a symbol to a BFD section + symbol. For example, obj_elf_change_section sets the BFD symbol + of an old symbol with the newly created section symbol. But when + we have multiple sections with the same name, the newly created + section may have the same name as an old section. We check if the + old symbol has been already marked as a section symbol before + resetting it. */ + if ((s->bsym->flags & BSF_SECTION_SYM) == 0) + s->bsym = bsym; + /* else XXX - What do we do now ? */ +} + +#ifdef OBJ_SYMFIELD_TYPE + +/* Get a pointer to the object format information for a symbol. */ + +OBJ_SYMFIELD_TYPE * +symbol_get_obj (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + return &s->sy_obj; +} + +/* Set the object format information for a symbol. */ + +void +symbol_set_obj (symbolS *s, OBJ_SYMFIELD_TYPE *o) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + s->sy_obj = *o; +} + +#endif /* OBJ_SYMFIELD_TYPE */ + +#ifdef TC_SYMFIELD_TYPE + +/* Get a pointer to the processor information for a symbol. */ + +TC_SYMFIELD_TYPE * +symbol_get_tc (symbolS *s) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + return &s->sy_tc; +} + +/* Set the processor information for a symbol. */ + +void +symbol_set_tc (symbolS *s, TC_SYMFIELD_TYPE *o) +{ + if (LOCAL_SYMBOL_CHECK (s)) + s = local_symbol_convert ((struct local_symbol *) s); + s->sy_tc = *o; +} + +#endif /* TC_SYMFIELD_TYPE */ + +void +symbol_begin (void) +{ + symbol_lastP = NULL; + symbol_rootP = NULL; /* In case we have 0 symbols (!!) */ + sy_hash = hash_new (); + local_hash = hash_new (); + + memset ((char *) (&abs_symbol), '\0', sizeof (abs_symbol)); +#if defined (EMIT_SECTION_SYMBOLS) || !defined (RELOC_REQUIRES_SYMBOL) + abs_symbol.bsym = bfd_abs_section_ptr->symbol; +#endif + abs_symbol.sy_value.X_op = O_constant; + abs_symbol.sy_frag = &zero_address_frag; + + if (LOCAL_LABELS_FB) + fb_label_init (); +} + +void +dot_symbol_init (void) +{ + dot_symbol.bsym = bfd_make_empty_symbol (stdoutput); + if (dot_symbol.bsym == NULL) + as_fatal ("bfd_make_empty_symbol: %s", bfd_errmsg (bfd_get_error ())); + dot_symbol.bsym->name = "."; + dot_symbol.sy_flags.sy_forward_ref = 1; + dot_symbol.sy_value.X_op = O_constant; +} + +int indent_level; + +/* Maximum indent level. + Available for modification inside a gdb session. */ +static int max_indent_level = 8; + +void +print_symbol_value_1 (FILE *file, symbolS *sym) +{ + const char *name = S_GET_NAME (sym); + if (!name || !name[0]) + name = "(unnamed)"; + fprintf (file, "sym "); + fprintf_vma (file, (bfd_vma) ((bfd_hostptr_t) sym)); + fprintf (file, " %s", name); + + if (LOCAL_SYMBOL_CHECK (sym)) + { + struct local_symbol *locsym = (struct local_symbol *) sym; + + if (local_symbol_get_frag (locsym) != & zero_address_frag + && local_symbol_get_frag (locsym) != NULL) + { + fprintf (file, " frag "); + fprintf_vma (file, (bfd_vma) ((bfd_hostptr_t) local_symbol_get_frag (locsym))); + } + if (local_symbol_resolved_p (locsym)) + fprintf (file, " resolved"); + fprintf (file, " local"); + } + else + { + if (sym->sy_frag != &zero_address_frag) + { + fprintf (file, " frag "); + fprintf_vma (file, (bfd_vma) ((bfd_hostptr_t) sym->sy_frag)); + } + if (sym->sy_flags.sy_written) + fprintf (file, " written"); + if (sym->sy_flags.sy_resolved) + fprintf (file, " resolved"); + else if (sym->sy_flags.sy_resolving) + fprintf (file, " resolving"); + if (sym->sy_flags.sy_used_in_reloc) + fprintf (file, " used-in-reloc"); + if (sym->sy_flags.sy_used) + fprintf (file, " used"); + if (S_IS_LOCAL (sym)) + fprintf (file, " local"); + if (S_IS_EXTERNAL (sym)) + fprintf (file, " extern"); + if (S_IS_WEAK (sym)) + fprintf (file, " weak"); + if (S_IS_DEBUG (sym)) + fprintf (file, " debug"); + if (S_IS_DEFINED (sym)) + fprintf (file, " defined"); + } + if (S_IS_WEAKREFR (sym)) + fprintf (file, " weakrefr"); + if (S_IS_WEAKREFD (sym)) + fprintf (file, " weakrefd"); + fprintf (file, " %s", segment_name (S_GET_SEGMENT (sym))); + if (symbol_resolved_p (sym)) + { + segT s = S_GET_SEGMENT (sym); + + if (s != undefined_section + && s != expr_section) + fprintf (file, " %lx", (unsigned long) S_GET_VALUE (sym)); + } + else if (indent_level < max_indent_level + && S_GET_SEGMENT (sym) != undefined_section) + { + indent_level++; + fprintf (file, "\n%*s<", indent_level * 4, ""); + if (LOCAL_SYMBOL_CHECK (sym)) + fprintf (file, "constant %lx", + (unsigned long) ((struct local_symbol *) sym)->lsy_value); + else + print_expr_1 (file, &sym->sy_value); + fprintf (file, ">"); + indent_level--; + } + fflush (file); +} + +void +print_symbol_value (symbolS *sym) +{ + indent_level = 0; + print_symbol_value_1 (stderr, sym); + fprintf (stderr, "\n"); +} + +static void +print_binary (FILE *file, const char *name, expressionS *exp) +{ + indent_level++; + fprintf (file, "%s\n%*s<", name, indent_level * 4, ""); + print_symbol_value_1 (file, exp->X_add_symbol); + fprintf (file, ">\n%*s<", indent_level * 4, ""); + print_symbol_value_1 (file, exp->X_op_symbol); + fprintf (file, ">"); + indent_level--; +} + +void +print_expr_1 (FILE *file, expressionS *exp) +{ + fprintf (file, "expr "); + fprintf_vma (file, (bfd_vma) ((bfd_hostptr_t) exp)); + fprintf (file, " "); + switch (exp->X_op) + { + case O_illegal: + fprintf (file, "illegal"); + break; + case O_absent: + fprintf (file, "absent"); + break; + case O_constant: + fprintf (file, "constant %lx", (unsigned long) exp->X_add_number); + break; + case O_symbol: + indent_level++; + fprintf (file, "symbol\n%*s<", indent_level * 4, ""); + print_symbol_value_1 (file, exp->X_add_symbol); + fprintf (file, ">"); + maybe_print_addnum: + if (exp->X_add_number) + fprintf (file, "\n%*s%lx", indent_level * 4, "", + (unsigned long) exp->X_add_number); + indent_level--; + break; + case O_register: + fprintf (file, "register #%d", (int) exp->X_add_number); + break; + case O_big: + fprintf (file, "big"); + break; + case O_uminus: + fprintf (file, "uminus -<"); + indent_level++; + print_symbol_value_1 (file, exp->X_add_symbol); + fprintf (file, ">"); + goto maybe_print_addnum; + case O_bit_not: + fprintf (file, "bit_not"); + break; + case O_multiply: + print_binary (file, "multiply", exp); + break; + case O_divide: + print_binary (file, "divide", exp); + break; + case O_modulus: + print_binary (file, "modulus", exp); + break; + case O_left_shift: + print_binary (file, "lshift", exp); + break; + case O_right_shift: + print_binary (file, "rshift", exp); + break; + case O_bit_inclusive_or: + print_binary (file, "bit_ior", exp); + break; + case O_bit_exclusive_or: + print_binary (file, "bit_xor", exp); + break; + case O_bit_and: + print_binary (file, "bit_and", exp); + break; + case O_eq: + print_binary (file, "eq", exp); + break; + case O_ne: + print_binary (file, "ne", exp); + break; + case O_lt: + print_binary (file, "lt", exp); + break; + case O_le: + print_binary (file, "le", exp); + break; + case O_ge: + print_binary (file, "ge", exp); + break; + case O_gt: + print_binary (file, "gt", exp); + break; + case O_logical_and: + print_binary (file, "logical_and", exp); + break; + case O_logical_or: + print_binary (file, "logical_or", exp); + break; + case O_add: + indent_level++; + fprintf (file, "add\n%*s<", indent_level * 4, ""); + print_symbol_value_1 (file, exp->X_add_symbol); + fprintf (file, ">\n%*s<", indent_level * 4, ""); + print_symbol_value_1 (file, exp->X_op_symbol); + fprintf (file, ">"); + goto maybe_print_addnum; + case O_subtract: + indent_level++; + fprintf (file, "subtract\n%*s<", indent_level * 4, ""); + print_symbol_value_1 (file, exp->X_add_symbol); + fprintf (file, ">\n%*s<", indent_level * 4, ""); + print_symbol_value_1 (file, exp->X_op_symbol); + fprintf (file, ">"); + goto maybe_print_addnum; + default: + fprintf (file, "{unknown opcode %d}", (int) exp->X_op); + break; + } + fflush (stdout); +} + +void +print_expr (expressionS *exp) +{ + print_expr_1 (stderr, exp); + fprintf (stderr, "\n"); +} + +void +symbol_print_statistics (FILE *file) +{ + hash_print_statistics (file, "symbol table", sy_hash); + hash_print_statistics (file, "mini local symbol table", local_hash); + fprintf (file, "%lu mini local symbols created, %lu converted\n", + local_symbol_count, local_symbol_conversion_count); +} + +#ifdef OBJ_COMPLEX_RELC + +/* Convert given symbol to a new complex-relocation symbol name. This + may be a recursive function, since it might be called for non-leaf + nodes (plain symbols) in the expression tree. The caller owns the + returning string, so should free it eventually. Errors are + indicated via as_bad and a NULL return value. The given symbol + is marked with sy_used_in_reloc. */ + +char * +symbol_relc_make_sym (symbolS * sym) +{ + char * terminal = NULL; + const char * sname; + char typetag; + int sname_len; + + gas_assert (sym != NULL); + + /* Recurse to symbol_relc_make_expr if this symbol + is defined as an expression or a plain value. */ + if ( S_GET_SEGMENT (sym) == expr_section + || S_GET_SEGMENT (sym) == absolute_section) + return symbol_relc_make_expr (& sym->sy_value); + + /* This may be a "fake symbol" L0\001, referring to ".". + Write out a special null symbol to refer to this position. */ + if (! strcmp (S_GET_NAME (sym), FAKE_LABEL_NAME)) + return xstrdup ("."); + + /* We hope this is a plain leaf symbol. Construct the encoding + as {S,s}II...:CCCCCCC.... + where 'S'/'s' means section symbol / plain symbol + III is decimal for the symbol name length + CCC is the symbol name itself. */ + symbol_mark_used_in_reloc (sym); + + sname = S_GET_NAME (sym); + sname_len = strlen (sname); + typetag = symbol_section_p (sym) ? 'S' : 's'; + + terminal = xmalloc (1 /* S or s */ + + 8 /* sname_len in decimal */ + + 1 /* _ spacer */ + + sname_len /* name itself */ + + 1 /* \0 */ ); + + sprintf (terminal, "%c%d:%s", typetag, sname_len, sname); + return terminal; +} + +/* Convert given value to a new complex-relocation symbol name. This + is a non-recursive function, since it is be called for leaf nodes + (plain values) in the expression tree. The caller owns the + returning string, so should free() it eventually. No errors. */ + +char * +symbol_relc_make_value (offsetT val) +{ + char * terminal = xmalloc (28); /* Enough for long long. */ + + terminal[0] = '#'; + bfd_sprintf_vma (stdoutput, terminal + 1, val); + return terminal; +} + +/* Convert given expression to a new complex-relocation symbol name. + This is a recursive function, since it traverses the entire given + expression tree. The caller owns the returning string, so should + free() it eventually. Errors are indicated via as_bad() and a NULL + return value. */ + +char * +symbol_relc_make_expr (expressionS * exp) +{ + char * opstr = NULL; /* Operator prefix string. */ + int arity = 0; /* Arity of this operator. */ + char * operands[3]; /* Up to three operands. */ + char * concat_string = NULL; + + operands[0] = operands[1] = operands[2] = NULL; + + gas_assert (exp != NULL); + + /* Match known operators -> fill in opstr, arity, operands[] and fall + through to construct subexpression fragments; may instead return + string directly for leaf nodes. */ + + /* See expr.h for the meaning of all these enums. Many operators + have an unnatural arity (X_add_number implicitly added). The + conversion logic expands them to explicit "+" subexpressions. */ + + switch (exp->X_op) + { + default: + as_bad ("Unknown expression operator (enum %d)", exp->X_op); + break; + + /* Leaf nodes. */ + case O_constant: + return symbol_relc_make_value (exp->X_add_number); + + case O_symbol: + if (exp->X_add_number) + { + arity = 2; + opstr = "+"; + operands[0] = symbol_relc_make_sym (exp->X_add_symbol); + operands[1] = symbol_relc_make_value (exp->X_add_number); + break; + } + else + return symbol_relc_make_sym (exp->X_add_symbol); + + /* Helper macros for nesting nodes. */ + +#define HANDLE_XADD_OPT1(str_) \ + if (exp->X_add_number) \ + { \ + arity = 2; \ + opstr = "+:" str_; \ + operands[0] = symbol_relc_make_sym (exp->X_add_symbol); \ + operands[1] = symbol_relc_make_value (exp->X_add_number); \ + break; \ + } \ + else \ + { \ + arity = 1; \ + opstr = str_; \ + operands[0] = symbol_relc_make_sym (exp->X_add_symbol); \ + } \ + break + +#define HANDLE_XADD_OPT2(str_) \ + if (exp->X_add_number) \ + { \ + arity = 3; \ + opstr = "+:" str_; \ + operands[0] = symbol_relc_make_sym (exp->X_add_symbol); \ + operands[1] = symbol_relc_make_sym (exp->X_op_symbol); \ + operands[2] = symbol_relc_make_value (exp->X_add_number); \ + } \ + else \ + { \ + arity = 2; \ + opstr = str_; \ + operands[0] = symbol_relc_make_sym (exp->X_add_symbol); \ + operands[1] = symbol_relc_make_sym (exp->X_op_symbol); \ + } \ + break + + /* Nesting nodes. */ + + case O_uminus: HANDLE_XADD_OPT1 ("0-"); + case O_bit_not: HANDLE_XADD_OPT1 ("~"); + case O_logical_not: HANDLE_XADD_OPT1 ("!"); + case O_multiply: HANDLE_XADD_OPT2 ("*"); + case O_divide: HANDLE_XADD_OPT2 ("/"); + case O_modulus: HANDLE_XADD_OPT2 ("%"); + case O_left_shift: HANDLE_XADD_OPT2 ("<<"); + case O_right_shift: HANDLE_XADD_OPT2 (">>"); + case O_bit_inclusive_or: HANDLE_XADD_OPT2 ("|"); + case O_bit_exclusive_or: HANDLE_XADD_OPT2 ("^"); + case O_bit_and: HANDLE_XADD_OPT2 ("&"); + case O_add: HANDLE_XADD_OPT2 ("+"); + case O_subtract: HANDLE_XADD_OPT2 ("-"); + case O_eq: HANDLE_XADD_OPT2 ("=="); + case O_ne: HANDLE_XADD_OPT2 ("!="); + case O_lt: HANDLE_XADD_OPT2 ("<"); + case O_le: HANDLE_XADD_OPT2 ("<="); + case O_ge: HANDLE_XADD_OPT2 (">="); + case O_gt: HANDLE_XADD_OPT2 (">"); + case O_logical_and: HANDLE_XADD_OPT2 ("&&"); + case O_logical_or: HANDLE_XADD_OPT2 ("||"); + } + + /* Validate & reject early. */ + if (arity >= 1 && ((operands[0] == NULL) || (strlen (operands[0]) == 0))) + opstr = NULL; + if (arity >= 2 && ((operands[1] == NULL) || (strlen (operands[1]) == 0))) + opstr = NULL; + if (arity >= 3 && ((operands[2] == NULL) || (strlen (operands[2]) == 0))) + opstr = NULL; + + if (opstr == NULL) + concat_string = NULL; + else + { + /* Allocate new string; include inter-operand padding gaps etc. */ + concat_string = xmalloc (strlen (opstr) + + 1 + + (arity >= 1 ? (strlen (operands[0]) + 1 ) : 0) + + (arity >= 2 ? (strlen (operands[1]) + 1 ) : 0) + + (arity >= 3 ? (strlen (operands[2]) + 0 ) : 0) + + 1); + gas_assert (concat_string != NULL); + + /* Format the thing. */ + sprintf (concat_string, + (arity == 0 ? "%s" : + arity == 1 ? "%s:%s" : + arity == 2 ? "%s:%s:%s" : + /* arity == 3 */ "%s:%s:%s:%s"), + opstr, operands[0], operands[1], operands[2]); + } + + /* Free operand strings (not opstr). */ + if (arity >= 1) xfree (operands[0]); + if (arity >= 2) xfree (operands[1]); + if (arity >= 3) xfree (operands[2]); + + return concat_string; +} + +#endif diff --git a/contrib/toolchain/binutils/gas/symbols.h b/contrib/toolchain/binutils/gas/symbols.h new file mode 100644 index 0000000000..a3a31f765b --- /dev/null +++ b/contrib/toolchain/binutils/gas/symbols.h @@ -0,0 +1,216 @@ +/* symbols.h - + Copyright 1987, 1990, 1992, 1993, 1994, 1995, 1997, 1999, 2000, 2001, + 2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +extern struct obstack notes; /* eg FixS live here. */ + +extern struct obstack cond_obstack; /* this is where we track .ifdef/.endif + (if we do that at all). */ + +extern symbolS *symbol_rootP; /* all the symbol nodes */ +extern symbolS *symbol_lastP; /* last struct symbol we made, or NULL */ + +extern symbolS abs_symbol; +extern symbolS dot_symbol; + +extern int symbol_table_frozen; + +/* This is non-zero if symbols are case sensitive, which is the + default. */ +extern int symbols_case_sensitive; + +char * symbol_relc_make_expr (expressionS *); +char * symbol_relc_make_sym (symbolS *); +char * symbol_relc_make_value (offsetT); +char *decode_local_label_name (char *s); +symbolS *symbol_find (const char *name); +symbolS *symbol_find_noref (const char *name, int noref); +symbolS *symbol_find_exact (const char *name); +symbolS *symbol_find_exact_noref (const char *name, int noref); +symbolS *symbol_find_or_make (const char *name); +symbolS *symbol_make (const char *name); +symbolS *symbol_new (const char *name, segT segment, valueT value, + fragS * frag); +symbolS *symbol_create (const char *name, segT segment, valueT value, + fragS * frag); +struct local_symbol *local_symbol_make (const char *name, segT section, + valueT val, fragS *frag); +symbolS *symbol_clone (symbolS *, int); +#undef symbol_clone_if_forward_ref +symbolS *symbol_clone_if_forward_ref (symbolS *, int); +#define symbol_clone_if_forward_ref(s) symbol_clone_if_forward_ref (s, 0) +symbolS *symbol_temp_new (segT, valueT, fragS *); +symbolS *symbol_temp_new_now (void); +symbolS *symbol_temp_make (void); + +symbolS *colon (const char *sym_name); +void local_colon (int n); +void symbol_begin (void); +void dot_symbol_init (void); +void symbol_print_statistics (FILE *); +void symbol_table_insert (symbolS * symbolP); +valueT resolve_symbol_value (symbolS *); +void resolve_local_symbol_values (void); +int snapshot_symbol (symbolS **, valueT *, segT *, fragS **); + +void print_symbol_value (symbolS *); +void print_expr (expressionS *); +void print_expr_1 (FILE *, expressionS *); +void print_symbol_value_1 (FILE *, symbolS *); + +int dollar_label_defined (long l); +void dollar_label_clear (void); +void define_dollar_label (long l); +char *dollar_label_name (long l, int augend); + +void fb_label_instance_inc (long label); +char *fb_label_name (long n, long augend); + +extern void copy_symbol_attributes (symbolS *, symbolS *); + +/* Get and set the values of symbols. These used to be macros. */ +extern valueT S_GET_VALUE (symbolS *); +extern void S_SET_VALUE (symbolS *, valueT); + +extern int S_IS_FUNCTION (symbolS *); +extern int S_IS_EXTERNAL (symbolS *); +extern int S_IS_WEAK (symbolS *); +extern int S_IS_WEAKREFR (symbolS *); +extern int S_IS_WEAKREFD (symbolS *); +extern int S_IS_COMMON (symbolS *); +extern int S_IS_DEFINED (symbolS *); +extern int S_FORCE_RELOC (symbolS *, int); +extern int S_IS_DEBUG (symbolS *); +extern int S_IS_LOCAL (symbolS *); +extern int S_IS_STABD (symbolS *); +extern int S_CAN_BE_REDEFINED (const symbolS *); +extern int S_IS_VOLATILE (const symbolS *); +extern int S_IS_FORWARD_REF (const symbolS *); +extern const char *S_GET_NAME (symbolS *); +extern segT S_GET_SEGMENT (symbolS *); +extern void S_SET_SEGMENT (symbolS *, segT); +extern void S_SET_EXTERNAL (symbolS *); +extern void S_SET_NAME (symbolS *, const char *); +extern void S_CLEAR_EXTERNAL (symbolS *); +extern void S_SET_WEAK (symbolS *); +extern void S_SET_WEAKREFR (symbolS *); +extern void S_CLEAR_WEAKREFR (symbolS *); +extern void S_SET_WEAKREFD (symbolS *); +extern void S_CLEAR_WEAKREFD (symbolS *); +extern void S_SET_THREAD_LOCAL (symbolS *); +extern void S_SET_VOLATILE (symbolS *); +extern void S_CLEAR_VOLATILE (symbolS *); +extern void S_SET_FORWARD_REF (symbolS *); + +#ifndef WORKING_DOT_WORD +struct broken_word + { + /* Linked list -- one of these structures per ".word x-y+C" + expression. */ + struct broken_word *next_broken_word; + /* Segment and subsegment for broken word. */ + segT seg; + subsegT subseg; + /* Which frag is this broken word in? */ + fragS *frag; + /* Where in the frag is it? */ + char *word_goes_here; + /* Where to add the break. */ + fragS *dispfrag; /* where to add the break */ + /* Operands of expression. */ + symbolS *add; + symbolS *sub; + offsetT addnum; + + int added; /* nasty thing happened yet? */ + /* 1: added and has a long-jump */ + /* 2: added but uses someone elses long-jump */ + + /* Pointer to broken_word with a similar long-jump. */ + struct broken_word *use_jump; + }; +extern struct broken_word *broken_words; +#endif /* ndef WORKING_DOT_WORD */ + +/* + * Current means for getting from symbols to segments and vice verse. + * This will change for infinite-segments support (e.g. COFF). + */ +extern const segT N_TYPE_seg[]; /* subseg.c */ + +#define SEGMENT_TO_SYMBOL_TYPE(seg) ( seg_N_TYPE [(int) (seg)] ) +extern const short seg_N_TYPE[];/* subseg.c */ + +#define N_REGISTER 30 /* Fake N_TYPE value for SEG_REGISTER */ + +void symbol_clear_list_pointers (symbolS * symbolP); + +void symbol_insert (symbolS * addme, symbolS * target, + symbolS ** rootP, symbolS ** lastP); +void symbol_remove (symbolS * symbolP, symbolS ** rootP, + symbolS ** lastP); + +extern symbolS *symbol_previous (symbolS *); + +void verify_symbol_chain (symbolS * rootP, symbolS * lastP); + +void symbol_append (symbolS * addme, symbolS * target, + symbolS ** rootP, symbolS ** lastP); + +extern symbolS *symbol_next (symbolS *); + +extern expressionS *symbol_get_value_expression (symbolS *); +extern void symbol_set_value_expression (symbolS *, const expressionS *); +extern offsetT *symbol_X_add_number (symbolS *); +extern void symbol_set_value_now (symbolS *); +extern void symbol_set_frag (symbolS *, fragS *); +extern fragS *symbol_get_frag (symbolS *); +extern void symbol_mark_used (symbolS *); +extern void symbol_clear_used (symbolS *); +extern int symbol_used_p (symbolS *); +extern void symbol_mark_used_in_reloc (symbolS *); +extern void symbol_clear_used_in_reloc (symbolS *); +extern int symbol_used_in_reloc_p (symbolS *); +extern void symbol_mark_mri_common (symbolS *); +extern void symbol_clear_mri_common (symbolS *); +extern int symbol_mri_common_p (symbolS *); +extern void symbol_mark_written (symbolS *); +extern void symbol_clear_written (symbolS *); +extern int symbol_written_p (symbolS *); +extern void symbol_mark_resolved (symbolS *); +extern int symbol_resolved_p (symbolS *); +extern int symbol_section_p (symbolS *); +extern int symbol_equated_p (symbolS *); +extern int symbol_equated_reloc_p (symbolS *); +extern int symbol_constant_p (symbolS *); +extern int symbol_shadow_p (symbolS *); +extern asymbol *symbol_get_bfdsym (symbolS *); +extern void symbol_set_bfdsym (symbolS *, asymbol *); +extern int symbol_same_p (symbolS *, symbolS *); + +#ifdef OBJ_SYMFIELD_TYPE +OBJ_SYMFIELD_TYPE *symbol_get_obj (symbolS *); +void symbol_set_obj (symbolS *, OBJ_SYMFIELD_TYPE *); +#endif + +#ifdef TC_SYMFIELD_TYPE +TC_SYMFIELD_TYPE *symbol_get_tc (symbolS *); +void symbol_set_tc (symbolS *, TC_SYMFIELD_TYPE *); +#endif diff --git a/contrib/toolchain/binutils/gas/targ-cpu.h b/contrib/toolchain/binutils/gas/targ-cpu.h new file mode 100644 index 0000000000..f4be0e50bd --- /dev/null +++ b/contrib/toolchain/binutils/gas/targ-cpu.h @@ -0,0 +1 @@ +#include "tc-i386.h" diff --git a/contrib/toolchain/binutils/gas/targ-env.h b/contrib/toolchain/binutils/gas/targ-env.h new file mode 100644 index 0000000000..372c77016f --- /dev/null +++ b/contrib/toolchain/binutils/gas/targ-env.h @@ -0,0 +1 @@ +#include "te-pe.h" diff --git a/contrib/toolchain/binutils/gas/tc.h b/contrib/toolchain/binutils/gas/tc.h new file mode 100644 index 0000000000..ff1cf3618a --- /dev/null +++ b/contrib/toolchain/binutils/gas/tc.h @@ -0,0 +1,79 @@ +/* tc.h - target cpu dependent + + Copyright 1987, 1990, 1991, 1992, 1993, 1994, 1995, 2000, 2001, 2003, + 2004, 2005, 2006, 2007, 2008, 2009 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, + Boston, MA 02110-1301, USA. */ + +/* In theory (mine, at least!) the machine dependent part of the assembler + should only have to include one file. This one. -- JF */ + +extern const pseudo_typeS md_pseudo_table[]; + +char * md_atof (int, char *, int *); +int md_parse_option (int, char *); +void md_show_usage (FILE *); +void md_assemble (char *); +void md_begin (void); +#ifndef md_number_to_chars +void md_number_to_chars (char *, valueT, int); +#endif +void md_apply_fix (fixS *, valueT *, segT); + +#ifndef WORKING_DOT_WORD +extern int md_short_jump_size; +extern int md_long_jump_size; +#endif + +#ifdef TE_PE +/* The name of an external symbol which is + used to make weak PE symbol names unique. */ +extern const char * an_external_name; +#endif + +#ifndef md_create_long_jump +void md_create_long_jump (char *, addressT, addressT, fragS *, symbolS *); +#endif +#ifndef md_create_short_jump +void md_create_short_jump (char *, addressT, addressT, fragS *, symbolS *); +#endif +#ifndef md_pcrel_from +long md_pcrel_from (fixS *); +#endif +#ifndef md_operand +void md_operand (expressionS *); +#endif +#ifndef md_estimate_size_before_relax +int md_estimate_size_before_relax (fragS * fragP, segT); +#endif +#ifndef md_section_align +valueT md_section_align (segT, valueT); +#endif +#ifndef md_undefined_symbol +symbolS *md_undefined_symbol (char *); +#endif + +#ifndef md_convert_frag +void md_convert_frag (bfd *, segT, fragS *); +#endif +#ifndef RELOC_EXPANSION_POSSIBLE +extern arelent *tc_gen_reloc (asection *, fixS *); +#else +extern arelent **tc_gen_reloc (asection *, fixS *); +#endif diff --git a/contrib/toolchain/binutils/gas/write.c b/contrib/toolchain/binutils/gas/write.c new file mode 100644 index 0000000000..745abe66d8 --- /dev/null +++ b/contrib/toolchain/binutils/gas/write.c @@ -0,0 +1,2868 @@ +/* write.c - emit .o file + Copyright 1986, 1987, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, + 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, + 2010, 2011, 2012 Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +/* This thing should be set up to do byteordering correctly. But... */ + +#include "as.h" +#include "subsegs.h" +#include "obstack.h" +#include "output-file.h" +#include "dwarf2dbg.h" +#include "libbfd.h" +#include "compress-debug.h" + +#ifndef TC_FORCE_RELOCATION +#define TC_FORCE_RELOCATION(FIX) \ + (generic_force_reloc (FIX)) +#endif + +#ifndef TC_FORCE_RELOCATION_ABS +#define TC_FORCE_RELOCATION_ABS(FIX) \ + (TC_FORCE_RELOCATION (FIX)) +#endif + +#ifndef TC_FORCE_RELOCATION_LOCAL +#define TC_FORCE_RELOCATION_LOCAL(FIX) \ + (!(FIX)->fx_pcrel \ + || TC_FORCE_RELOCATION (FIX)) +#endif + +#ifndef TC_FORCE_RELOCATION_SUB_SAME +#define TC_FORCE_RELOCATION_SUB_SAME(FIX, SEG) \ + (! SEG_NORMAL (SEG)) +#endif + +#ifndef md_register_arithmetic +# define md_register_arithmetic 1 +#endif + +#ifndef TC_FORCE_RELOCATION_SUB_ABS +#define TC_FORCE_RELOCATION_SUB_ABS(FIX, SEG) \ + (!md_register_arithmetic && (SEG) == reg_section) +#endif + +#ifndef TC_FORCE_RELOCATION_SUB_LOCAL +#ifdef DIFF_EXPR_OK +#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) \ + (!md_register_arithmetic && (SEG) == reg_section) +#else +#define TC_FORCE_RELOCATION_SUB_LOCAL(FIX, SEG) 1 +#endif +#endif + +#ifndef TC_VALIDATE_FIX_SUB +#ifdef UNDEFINED_DIFFERENCE_OK +/* The PA needs this for PIC code generation. */ +#define TC_VALIDATE_FIX_SUB(FIX, SEG) \ + (md_register_arithmetic || (SEG) != reg_section) +#else +#define TC_VALIDATE_FIX_SUB(FIX, SEG) \ + ((md_register_arithmetic || (SEG) != reg_section) \ + && ((FIX)->fx_r_type == BFD_RELOC_GPREL32 \ + || (FIX)->fx_r_type == BFD_RELOC_GPREL16)) +#endif +#endif + +#ifndef TC_LINKRELAX_FIXUP +#define TC_LINKRELAX_FIXUP(SEG) 1 +#endif + +#ifndef MD_APPLY_SYM_VALUE +#define MD_APPLY_SYM_VALUE(FIX) 1 +#endif + +#ifndef TC_FINALIZE_SYMS_BEFORE_SIZE_SEG +#define TC_FINALIZE_SYMS_BEFORE_SIZE_SEG 1 +#endif + +#ifndef MD_PCREL_FROM_SECTION +#define MD_PCREL_FROM_SECTION(FIX, SEC) md_pcrel_from (FIX) +#endif + +#ifndef TC_FAKE_LABEL +#define TC_FAKE_LABEL(NAME) (strcmp ((NAME), FAKE_LABEL_NAME) == 0) +#endif + +/* Positive values of TC_FX_SIZE_SLACK allow a target to define + fixups that far past the end of a frag. Having such fixups + is of course most most likely a bug in setting fx_size correctly. + A negative value disables the fixup check entirely, which is + appropriate for something like the Renesas / SuperH SH_COUNT + reloc. */ +#ifndef TC_FX_SIZE_SLACK +#define TC_FX_SIZE_SLACK(FIX) 0 +#endif + +/* Used to control final evaluation of expressions. */ +int finalize_syms = 0; + +int symbol_table_frozen; + +symbolS *abs_section_sym; + +/* Remember the value of dot when parsing expressions. */ +addressT dot_value; + +/* The frag that dot_value is based from. */ +fragS *dot_frag; + +/* Relocs generated by ".reloc" pseudo. */ +struct reloc_list* reloc_list; + +void print_fixup (fixS *); + +/* We generally attach relocs to frag chains. However, after we have + chained these all together into a segment, any relocs we add after + that must be attached to a segment. This will include relocs added + in md_estimate_size_for_relax, for example. */ +static int frags_chained = 0; + +static int n_fixups; + +#define RELOC_ENUM enum bfd_reloc_code_real + +/* Create a fixS in obstack 'notes'. */ + +static fixS * +fix_new_internal (fragS *frag, /* Which frag? */ + int where, /* Where in that frag? */ + int size, /* 1, 2, or 4 usually. */ + symbolS *add_symbol, /* X_add_symbol. */ + symbolS *sub_symbol, /* X_op_symbol. */ + offsetT offset, /* X_add_number. */ + int pcrel, /* TRUE if PC-relative relocation. */ + RELOC_ENUM r_type /* Relocation type. */, + int at_beginning) /* Add to the start of the list? */ +{ + fixS *fixP; + + n_fixups++; + + fixP = (fixS *) obstack_alloc (¬es, sizeof (fixS)); + + fixP->fx_frag = frag; + fixP->fx_where = where; + fixP->fx_size = size; + /* We've made fx_size a narrow field; check that it's wide enough. */ + if (fixP->fx_size != size) + { + as_bad (_("field fx_size too small to hold %d"), size); + abort (); + } + fixP->fx_addsy = add_symbol; + fixP->fx_subsy = sub_symbol; + fixP->fx_offset = offset; + fixP->fx_dot_value = dot_value; + fixP->fx_dot_frag = dot_frag; + fixP->fx_pcrel = pcrel; + fixP->fx_r_type = r_type; + fixP->fx_im_disp = 0; + fixP->fx_pcrel_adjust = 0; + fixP->fx_bit_fixP = 0; + fixP->fx_addnumber = 0; + fixP->fx_tcbit = 0; + fixP->fx_tcbit2 = 0; + fixP->fx_done = 0; + fixP->fx_no_overflow = 0; + fixP->fx_signed = 0; + +#ifdef USING_CGEN + fixP->fx_cgen.insn = NULL; + fixP->fx_cgen.opinfo = 0; +#endif + +#ifdef TC_FIX_TYPE + TC_INIT_FIX_DATA (fixP); +#endif + + as_where (&fixP->fx_file, &fixP->fx_line); + + { + + fixS **seg_fix_rootP = (frags_chained + ? &seg_info (now_seg)->fix_root + : &frchain_now->fix_root); + fixS **seg_fix_tailP = (frags_chained + ? &seg_info (now_seg)->fix_tail + : &frchain_now->fix_tail); + + if (at_beginning) + { + fixP->fx_next = *seg_fix_rootP; + *seg_fix_rootP = fixP; + if (fixP->fx_next == NULL) + *seg_fix_tailP = fixP; + } + else + { + fixP->fx_next = NULL; + if (*seg_fix_tailP) + (*seg_fix_tailP)->fx_next = fixP; + else + *seg_fix_rootP = fixP; + *seg_fix_tailP = fixP; + } + } + + return fixP; +} + +/* Create a fixup relative to a symbol (plus a constant). */ + +fixS * +fix_new (fragS *frag, /* Which frag? */ + int where, /* Where in that frag? */ + int size, /* 1, 2, or 4 usually. */ + symbolS *add_symbol, /* X_add_symbol. */ + offsetT offset, /* X_add_number. */ + int pcrel, /* TRUE if PC-relative relocation. */ + RELOC_ENUM r_type /* Relocation type. */) +{ + return fix_new_internal (frag, where, size, add_symbol, + (symbolS *) NULL, offset, pcrel, r_type, FALSE); +} + +/* Create a fixup for an expression. Currently we only support fixups + for difference expressions. That is itself more than most object + file formats support anyhow. */ + +fixS * +fix_new_exp (fragS *frag, /* Which frag? */ + int where, /* Where in that frag? */ + int size, /* 1, 2, or 4 usually. */ + expressionS *exp, /* Expression. */ + int pcrel, /* TRUE if PC-relative relocation. */ + RELOC_ENUM r_type /* Relocation type. */) +{ + symbolS *add = NULL; + symbolS *sub = NULL; + offsetT off = 0; + + switch (exp->X_op) + { + case O_absent: + break; + + case O_register: + as_bad (_("register value used as expression")); + break; + + case O_add: + /* This comes up when _GLOBAL_OFFSET_TABLE_+(.-L0) is read, if + the difference expression cannot immediately be reduced. */ + { + symbolS *stmp = make_expr_symbol (exp); + + exp->X_op = O_symbol; + exp->X_op_symbol = 0; + exp->X_add_symbol = stmp; + exp->X_add_number = 0; + + return fix_new_exp (frag, where, size, exp, pcrel, r_type); + } + + case O_symbol_rva: + add = exp->X_add_symbol; + off = exp->X_add_number; + r_type = BFD_RELOC_RVA; + break; + + case O_uminus: + sub = exp->X_add_symbol; + off = exp->X_add_number; + break; + + case O_subtract: + sub = exp->X_op_symbol; + /* Fall through. */ + case O_symbol: + add = exp->X_add_symbol; + /* Fall through. */ + case O_constant: + off = exp->X_add_number; + break; + + default: + add = make_expr_symbol (exp); + break; + } + + return fix_new_internal (frag, where, size, add, sub, off, pcrel, + r_type, FALSE); +} + +/* Create a fixup at the beginning of FRAG. The arguments are the same + as for fix_new, except that WHERE is implicitly 0. */ + +fixS * +fix_at_start (fragS *frag, int size, symbolS *add_symbol, + offsetT offset, int pcrel, RELOC_ENUM r_type) +{ + return fix_new_internal (frag, 0, size, add_symbol, + (symbolS *) NULL, offset, pcrel, r_type, TRUE); +} + +/* Generic function to determine whether a fixup requires a relocation. */ +int +generic_force_reloc (fixS *fix) +{ + if (fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT + || fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY) + return 1; + + if (fix->fx_addsy == NULL) + return 0; + + return S_FORCE_RELOC (fix->fx_addsy, fix->fx_subsy == NULL); +} + +/* Append a string onto another string, bumping the pointer along. */ +void +append (char **charPP, char *fromP, unsigned long length) +{ + /* Don't trust memcpy() of 0 chars. */ + if (length == 0) + return; + + memcpy (*charPP, fromP, length); + *charPP += length; +} + +/* This routine records the largest alignment seen for each segment. + If the beginning of the segment is aligned on the worst-case + boundary, all of the other alignments within it will work. At + least one object format really uses this info. */ + +void +record_alignment (/* Segment to which alignment pertains. */ + segT seg, + /* Alignment, as a power of 2 (e.g., 1 => 2-byte + boundary, 2 => 4-byte boundary, etc.) */ + int align) +{ + if (seg == absolute_section) + return; + + if ((unsigned int) align > bfd_get_section_alignment (stdoutput, seg)) + bfd_set_section_alignment (stdoutput, seg, align); +} + +int +get_recorded_alignment (segT seg) +{ + if (seg == absolute_section) + return 0; + + return bfd_get_section_alignment (stdoutput, seg); +} + +/* Reset the section indices after removing the gas created sections. */ + +static void +renumber_sections (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *countparg) +{ + int *countp = (int *) countparg; + + sec->index = *countp; + ++*countp; +} + +static fragS * +chain_frchains_together_1 (segT section, struct frchain *frchp) +{ + fragS dummy, *prev_frag = &dummy; + fixS fix_dummy, *prev_fix = &fix_dummy; + + for (; frchp; frchp = frchp->frch_next) + { + prev_frag->fr_next = frchp->frch_root; + prev_frag = frchp->frch_last; + gas_assert (prev_frag->fr_type != 0); + if (frchp->fix_root != (fixS *) NULL) + { + if (seg_info (section)->fix_root == (fixS *) NULL) + seg_info (section)->fix_root = frchp->fix_root; + prev_fix->fx_next = frchp->fix_root; + seg_info (section)->fix_tail = frchp->fix_tail; + prev_fix = frchp->fix_tail; + } + } + gas_assert (prev_frag != &dummy + && prev_frag->fr_type != 0); + prev_frag->fr_next = 0; + return prev_frag; +} + +static void +chain_frchains_together (bfd *abfd ATTRIBUTE_UNUSED, + segT section, + void *xxx ATTRIBUTE_UNUSED) +{ + segment_info_type *info; + + /* BFD may have introduced its own sections without using + subseg_new, so it is possible that seg_info is NULL. */ + info = seg_info (section); + if (info != (segment_info_type *) NULL) + info->frchainP->frch_last + = chain_frchains_together_1 (section, info->frchainP); + + /* Now that we've chained the frags together, we must add new fixups + to the segment, not to the frag chain. */ + frags_chained = 1; +} + +static void +cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP) +{ + switch (fragP->fr_type) + { + case rs_align: + case rs_align_code: + case rs_align_test: + case rs_org: + case rs_space: +#ifdef HANDLE_ALIGN + HANDLE_ALIGN (fragP); +#endif + know (fragP->fr_next != NULL); + fragP->fr_offset = (fragP->fr_next->fr_address + - fragP->fr_address + - fragP->fr_fix) / fragP->fr_var; + if (fragP->fr_offset < 0) + { + as_bad_where (fragP->fr_file, fragP->fr_line, + _("attempt to .org/.space backwards? (%ld)"), + (long) fragP->fr_offset); + fragP->fr_offset = 0; + } + fragP->fr_type = rs_fill; + break; + + case rs_fill: + break; + + case rs_leb128: + { + valueT value = S_GET_VALUE (fragP->fr_symbol); + int size; + + size = output_leb128 (fragP->fr_literal + fragP->fr_fix, value, + fragP->fr_subtype); + + fragP->fr_fix += size; + fragP->fr_type = rs_fill; + fragP->fr_var = 0; + fragP->fr_offset = 0; + fragP->fr_symbol = NULL; + } + break; + + case rs_cfa: + eh_frame_convert_frag (fragP); + break; + + case rs_dwarf2dbg: + dwarf2dbg_convert_frag (fragP); + break; + + case rs_machine_dependent: + md_convert_frag (stdoutput, sec, fragP); + + gas_assert (fragP->fr_next == NULL + || ((offsetT) (fragP->fr_next->fr_address - fragP->fr_address) + == fragP->fr_fix)); + + /* After md_convert_frag, we make the frag into a ".space 0". + md_convert_frag() should set up any fixSs and constants + required. */ + frag_wane (fragP); + break; + +#ifndef WORKING_DOT_WORD + case rs_broken_word: + { + struct broken_word *lie; + + if (fragP->fr_subtype) + { + fragP->fr_fix += md_short_jump_size; + for (lie = (struct broken_word *) (fragP->fr_symbol); + lie && lie->dispfrag == fragP; + lie = lie->next_broken_word) + if (lie->added == 1) + fragP->fr_fix += md_long_jump_size; + } + frag_wane (fragP); + } + break; +#endif + + default: + BAD_CASE (fragP->fr_type); + break; + } +#ifdef md_frag_check + md_frag_check (fragP); +#endif +} + +struct relax_seg_info +{ + int pass; + int changed; +}; + +static void +relax_seg (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *xxx) +{ + segment_info_type *seginfo = seg_info (sec); + struct relax_seg_info *info = (struct relax_seg_info *) xxx; + + if (seginfo && seginfo->frchainP + && relax_segment (seginfo->frchainP->frch_root, sec, info->pass)) + info->changed = 1; +} + +static void +size_seg (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED) +{ + flagword flags; + fragS *fragp; + segment_info_type *seginfo; + int x; + valueT size, newsize; + + subseg_change (sec, 0); + + seginfo = seg_info (sec); + if (seginfo && seginfo->frchainP) + { + for (fragp = seginfo->frchainP->frch_root; fragp; fragp = fragp->fr_next) + cvt_frag_to_fill (sec, fragp); + for (fragp = seginfo->frchainP->frch_root; + fragp->fr_next; + fragp = fragp->fr_next) + /* Walk to last elt. */ + ; + size = fragp->fr_address + fragp->fr_fix; + } + else + size = 0; + + flags = bfd_get_section_flags (abfd, sec); + if (size == 0 && bfd_get_section_size (sec) != 0 && + (flags & SEC_HAS_CONTENTS) != 0) + return; + + if (size > 0 && ! seginfo->bss) + flags |= SEC_HAS_CONTENTS; + + flags &= ~SEC_RELOC; + x = bfd_set_section_flags (abfd, sec, flags); + gas_assert (x); + + newsize = md_section_align (sec, size); + x = bfd_set_section_size (abfd, sec, newsize); + gas_assert (x); + + /* If the size had to be rounded up, add some padding in the last + non-empty frag. */ + gas_assert (newsize >= size); + if (size != newsize) + { + fragS *last = seginfo->frchainP->frch_last; + fragp = seginfo->frchainP->frch_root; + while (fragp->fr_next != last) + fragp = fragp->fr_next; + last->fr_address = size; + if ((newsize - size) % fragp->fr_var == 0) + fragp->fr_offset += (newsize - size) / fragp->fr_var; + else + /* If we hit this abort, it's likely due to subsegs_finish not + providing sufficient alignment on the last frag, and the + machine dependent code using alignment frags with fr_var + greater than 1. */ + abort (); + } + +#ifdef tc_frob_section + tc_frob_section (sec); +#endif +#ifdef obj_frob_section + obj_frob_section (sec); +#endif +} + +#ifdef DEBUG2 +static void +dump_section_relocs (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, FILE *stream) +{ + segment_info_type *seginfo = seg_info (sec); + fixS *fixp = seginfo->fix_root; + + if (!fixp) + return; + + fprintf (stream, "sec %s relocs:\n", sec->name); + while (fixp) + { + symbolS *s = fixp->fx_addsy; + + fprintf (stream, " %08lx: type %d ", (unsigned long) fixp, + (int) fixp->fx_r_type); + if (s == NULL) + fprintf (stream, "no sym\n"); + else + { + print_symbol_value_1 (stream, s); + fprintf (stream, "\n"); + } + fixp = fixp->fx_next; + } +} +#else +#define dump_section_relocs(ABFD,SEC,STREAM) ((void) 0) +#endif + +#ifndef EMIT_SECTION_SYMBOLS +#define EMIT_SECTION_SYMBOLS 1 +#endif + +/* Resolve U.A.OFFSET_SYM and U.A.SYM fields of RELOC_LIST entries, + and check for validity. Convert RELOC_LIST from using U.A fields + to U.B fields. */ +static void +resolve_reloc_expr_symbols (void) +{ + bfd_vma addr_mask = 1; + struct reloc_list *r; + + /* Avoid a shift by the width of type. */ + addr_mask <<= bfd_arch_bits_per_address (stdoutput) - 1; + addr_mask <<= 1; + addr_mask -= 1; + + for (r = reloc_list; r; r = r->next) + { + reloc_howto_type *howto = r->u.a.howto; + expressionS *symval; + symbolS *sym; + bfd_vma offset, addend; + asection *sec; + + resolve_symbol_value (r->u.a.offset_sym); + symval = symbol_get_value_expression (r->u.a.offset_sym); + + offset = 0; + sym = NULL; + if (symval->X_op == O_constant) + sym = r->u.a.offset_sym; + else if (symval->X_op == O_symbol) + { + sym = symval->X_add_symbol; + offset = symval->X_add_number; + symval = symbol_get_value_expression (symval->X_add_symbol); + } + if (sym == NULL + || symval->X_op != O_constant + || (sec = S_GET_SEGMENT (sym)) == NULL + || !SEG_NORMAL (sec)) + { + as_bad_where (r->file, r->line, _("invalid offset expression")); + sec = NULL; + } + else + offset += S_GET_VALUE (sym); + + sym = NULL; + addend = r->u.a.addend; + if (r->u.a.sym != NULL) + { + resolve_symbol_value (r->u.a.sym); + symval = symbol_get_value_expression (r->u.a.sym); + if (symval->X_op == O_constant) + sym = r->u.a.sym; + else if (symval->X_op == O_symbol) + { + sym = symval->X_add_symbol; + addend += symval->X_add_number; + symval = symbol_get_value_expression (symval->X_add_symbol); + } + if (symval->X_op != O_constant) + { + as_bad_where (r->file, r->line, _("invalid reloc expression")); + sec = NULL; + } + else if (sym != NULL) + { + /* Convert relocs against local symbols to refer to the + corresponding section symbol plus offset instead. Keep + PC-relative relocs of the REL variety intact though to + prevent the offset from overflowing the relocated field, + unless it has enough bits to cover the whole address + space. */ + if (S_IS_LOCAL (sym) && !symbol_section_p (sym) + && (sec->use_rela_p + || (howto->partial_inplace + && (!howto->pc_relative + || howto->src_mask == addr_mask)))) + { + asection *symsec = S_GET_SEGMENT (sym); + if (!(((symsec->flags & SEC_MERGE) != 0 + && addend != 0) + || (symsec->flags & SEC_THREAD_LOCAL) != 0)) + { + addend += S_GET_VALUE (sym); + sym = section_symbol (symsec); + } + } + symbol_mark_used_in_reloc (sym); + } + } + if (sym == NULL) + { + if (abs_section_sym == NULL) + abs_section_sym = section_symbol (absolute_section); + sym = abs_section_sym; + } + + r->u.b.sec = sec; + r->u.b.s = symbol_get_bfdsym (sym); + r->u.b.r.sym_ptr_ptr = &r->u.b.s; + r->u.b.r.address = offset; + r->u.b.r.addend = addend; + r->u.b.r.howto = howto; + } +} + +/* This pass over fixups decides whether symbols can be replaced with + section symbols. */ + +static void +adjust_reloc_syms (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, + void *xxx ATTRIBUTE_UNUSED) +{ + segment_info_type *seginfo = seg_info (sec); + fixS *fixp; + + if (seginfo == NULL) + return; + + dump_section_relocs (abfd, sec, stderr); + + for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next) + if (fixp->fx_done) + /* Ignore it. */ + ; + else if (fixp->fx_addsy) + { + symbolS *sym; + asection *symsec; + +#ifdef DEBUG5 + fprintf (stderr, "\n\nadjusting fixup:\n"); + print_fixup (fixp); +#endif + + sym = fixp->fx_addsy; + + /* All symbols should have already been resolved at this + point. It is possible to see unresolved expression + symbols, though, since they are not in the regular symbol + table. */ + resolve_symbol_value (sym); + + if (fixp->fx_subsy != NULL) + resolve_symbol_value (fixp->fx_subsy); + + /* If this symbol is equated to an undefined or common symbol, + convert the fixup to being against that symbol. */ + while (symbol_equated_reloc_p (sym) + || S_IS_WEAKREFR (sym)) + { + symbolS *newsym = symbol_get_value_expression (sym)->X_add_symbol; + if (sym == newsym) + break; + fixp->fx_offset += symbol_get_value_expression (sym)->X_add_number; + fixp->fx_addsy = newsym; + sym = newsym; + } + + if (symbol_mri_common_p (sym)) + { + fixp->fx_offset += S_GET_VALUE (sym); + fixp->fx_addsy = symbol_get_value_expression (sym)->X_add_symbol; + continue; + } + + /* If the symbol is undefined, common, weak, or global (ELF + shared libs), we can't replace it with the section symbol. */ + if (S_FORCE_RELOC (fixp->fx_addsy, 1)) + continue; + + /* Is there some other (target cpu dependent) reason we can't adjust + this one? (E.g. relocations involving function addresses on + the PA. */ +#ifdef tc_fix_adjustable + if (! tc_fix_adjustable (fixp)) + continue; +#endif + + /* Since we're reducing to section symbols, don't attempt to reduce + anything that's already using one. */ + if (symbol_section_p (sym)) + continue; + + symsec = S_GET_SEGMENT (sym); + if (symsec == NULL) + abort (); + + if (bfd_is_abs_section (symsec)) + { + /* The fixup_segment routine normally will not use this + symbol in a relocation. */ + continue; + } + + /* Don't try to reduce relocs which refer to non-local symbols + in .linkonce sections. It can lead to confusion when a + debugging section refers to a .linkonce section. I hope + this will always be correct. */ + if (symsec != sec && ! S_IS_LOCAL (sym)) + { + if ((symsec->flags & SEC_LINK_ONCE) != 0 + || (IS_ELF + /* The GNU toolchain uses an extension for ELF: a + section beginning with the magic string + .gnu.linkonce is a linkonce section. */ + && strncmp (segment_name (symsec), ".gnu.linkonce", + sizeof ".gnu.linkonce" - 1) == 0)) + continue; + } + + /* Never adjust a reloc against local symbol in a merge section + with non-zero addend. */ + if ((symsec->flags & SEC_MERGE) != 0 + && (fixp->fx_offset != 0 || fixp->fx_subsy != NULL)) + continue; + + /* Never adjust a reloc against TLS local symbol. */ + if ((symsec->flags & SEC_THREAD_LOCAL) != 0) + continue; + + /* We refetch the segment when calling section_symbol, rather + than using symsec, because S_GET_VALUE may wind up changing + the section when it calls resolve_symbol_value. */ + fixp->fx_offset += S_GET_VALUE (sym); + fixp->fx_addsy = section_symbol (S_GET_SEGMENT (sym)); +#ifdef DEBUG5 + fprintf (stderr, "\nadjusted fixup:\n"); + print_fixup (fixp); +#endif + } + + dump_section_relocs (abfd, sec, stderr); +} + +/* fixup_segment() + + Go through all the fixS's in a segment and see which ones can be + handled now. (These consist of fixS where we have since discovered + the value of a symbol, or the address of the frag involved.) + For each one, call md_apply_fix to put the fix into the frag data. + Ones that we couldn't completely handle here will be output later + by emit_relocations. */ + +static void +fixup_segment (fixS *fixP, segT this_segment) +{ + valueT add_number; + fragS *fragP; + segT add_symbol_segment = absolute_section; + + if (fixP != NULL && abs_section_sym == NULL) + abs_section_sym = section_symbol (absolute_section); + + /* If the linker is doing the relaxing, we must not do any fixups. + + Well, strictly speaking that's not true -- we could do any that + are PC-relative and don't cross regions that could change size. + And for the i960 we might be able to turn callx/callj into bal + anyways in cases where we know the maximum displacement. */ + if (linkrelax && TC_LINKRELAX_FIXUP (this_segment)) + { + for (; fixP; fixP = fixP->fx_next) + if (!fixP->fx_done) + { + if (fixP->fx_addsy == NULL) + { + /* There was no symbol required by this relocation. + However, BFD doesn't really handle relocations + without symbols well. So fake up a local symbol in + the absolute section. */ + fixP->fx_addsy = abs_section_sym; + } + symbol_mark_used_in_reloc (fixP->fx_addsy); + if (fixP->fx_subsy != NULL) + symbol_mark_used_in_reloc (fixP->fx_subsy); + } + return; + } + + for (; fixP; fixP = fixP->fx_next) + { +#ifdef DEBUG5 + fprintf (stderr, "\nprocessing fixup:\n"); + print_fixup (fixP); +#endif + + fragP = fixP->fx_frag; + know (fragP); +#ifdef TC_VALIDATE_FIX + TC_VALIDATE_FIX (fixP, this_segment, skip); +#endif + add_number = fixP->fx_offset; + + if (fixP->fx_addsy != NULL) + add_symbol_segment = S_GET_SEGMENT (fixP->fx_addsy); + + if (fixP->fx_subsy != NULL) + { + segT sub_symbol_segment; + resolve_symbol_value (fixP->fx_subsy); + sub_symbol_segment = S_GET_SEGMENT (fixP->fx_subsy); + if (fixP->fx_addsy != NULL + && sub_symbol_segment == add_symbol_segment + && !S_FORCE_RELOC (fixP->fx_addsy, 0) + && !S_FORCE_RELOC (fixP->fx_subsy, 0) + && !TC_FORCE_RELOCATION_SUB_SAME (fixP, add_symbol_segment)) + { + add_number += S_GET_VALUE (fixP->fx_addsy); + add_number -= S_GET_VALUE (fixP->fx_subsy); + fixP->fx_offset = add_number; + fixP->fx_addsy = NULL; + fixP->fx_subsy = NULL; +#ifdef TC_M68K + /* See the comment below about 68k weirdness. */ + fixP->fx_pcrel = 0; +#endif + } + else if (sub_symbol_segment == absolute_section + && !S_FORCE_RELOC (fixP->fx_subsy, 0) + && !TC_FORCE_RELOCATION_SUB_ABS (fixP, add_symbol_segment)) + { + add_number -= S_GET_VALUE (fixP->fx_subsy); + fixP->fx_offset = add_number; + fixP->fx_subsy = NULL; + } + else if (sub_symbol_segment == this_segment + && !S_FORCE_RELOC (fixP->fx_subsy, 0) + && !TC_FORCE_RELOCATION_SUB_LOCAL (fixP, add_symbol_segment)) + { + add_number -= S_GET_VALUE (fixP->fx_subsy); + fixP->fx_offset = (add_number + fixP->fx_dot_value + + fixP->fx_dot_frag->fr_address); + + /* Make it pc-relative. If the back-end code has not + selected a pc-relative reloc, cancel the adjustment + we do later on all pc-relative relocs. */ + if (0 +#ifdef TC_M68K + /* Do this for m68k even if it's already described + as pc-relative. On the m68k, an operand of + "pc@(foo-.-2)" should address "foo" in a + pc-relative mode. */ + || 1 +#endif + || !fixP->fx_pcrel) + add_number += MD_PCREL_FROM_SECTION (fixP, this_segment); + fixP->fx_subsy = NULL; + fixP->fx_pcrel = 1; + } + else if (!TC_VALIDATE_FIX_SUB (fixP, add_symbol_segment)) + { + if (!md_register_arithmetic + && (add_symbol_segment == reg_section + || sub_symbol_segment == reg_section)) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("register value used as expression")); + else + as_bad_where (fixP->fx_file, fixP->fx_line, + _("can't resolve `%s' {%s section} - `%s' {%s section}"), + fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "0", + segment_name (add_symbol_segment), + S_GET_NAME (fixP->fx_subsy), + segment_name (sub_symbol_segment)); + } + else if (sub_symbol_segment != undefined_section + && ! bfd_is_com_section (sub_symbol_segment) + && MD_APPLY_SYM_VALUE (fixP)) + add_number -= S_GET_VALUE (fixP->fx_subsy); + } + + if (fixP->fx_addsy) + { + if (add_symbol_segment == this_segment + && !S_FORCE_RELOC (fixP->fx_addsy, 0) + && !TC_FORCE_RELOCATION_LOCAL (fixP)) + { + /* This fixup was made when the symbol's segment was + SEG_UNKNOWN, but it is now in the local segment. + So we know how to do the address without relocation. */ + add_number += S_GET_VALUE (fixP->fx_addsy); + fixP->fx_offset = add_number; + if (fixP->fx_pcrel) + add_number -= MD_PCREL_FROM_SECTION (fixP, this_segment); + fixP->fx_addsy = NULL; + fixP->fx_pcrel = 0; + } + else if (add_symbol_segment == absolute_section + && !S_FORCE_RELOC (fixP->fx_addsy, 0) + && !TC_FORCE_RELOCATION_ABS (fixP)) + { + add_number += S_GET_VALUE (fixP->fx_addsy); + fixP->fx_offset = add_number; + fixP->fx_addsy = NULL; + } + else if (add_symbol_segment != undefined_section + && ! bfd_is_com_section (add_symbol_segment) + && MD_APPLY_SYM_VALUE (fixP)) + add_number += S_GET_VALUE (fixP->fx_addsy); + } + + if (fixP->fx_pcrel) + { + add_number -= MD_PCREL_FROM_SECTION (fixP, this_segment); + if (!fixP->fx_done && fixP->fx_addsy == NULL) + { + /* There was no symbol required by this relocation. + However, BFD doesn't really handle relocations + without symbols well. So fake up a local symbol in + the absolute section. */ + fixP->fx_addsy = abs_section_sym; + } + } + + if (!fixP->fx_done) + md_apply_fix (fixP, &add_number, this_segment); + + if (!fixP->fx_done) + { + if (fixP->fx_addsy == NULL) + fixP->fx_addsy = abs_section_sym; + symbol_mark_used_in_reloc (fixP->fx_addsy); + if (fixP->fx_subsy != NULL) + symbol_mark_used_in_reloc (fixP->fx_subsy); + } + + if (!fixP->fx_bit_fixP && !fixP->fx_no_overflow && fixP->fx_size != 0) + { + if (fixP->fx_size < sizeof (valueT)) + { + valueT mask; + + mask = 0; + mask--; /* Set all bits to one. */ + mask <<= fixP->fx_size * 8 - (fixP->fx_signed ? 1 : 0); + if ((add_number & mask) != 0 && (add_number & mask) != mask) + { + char buf[50], buf2[50]; + sprint_value (buf, fragP->fr_address + fixP->fx_where); + if (add_number > 1000) + sprint_value (buf2, add_number); + else + sprintf (buf2, "%ld", (long) add_number); + as_bad_where (fixP->fx_file, fixP->fx_line, + _("value of %s too large for field of %d bytes at %s"), + buf2, fixP->fx_size, buf); + } /* Generic error checking. */ + } +#ifdef WARN_SIGNED_OVERFLOW_WORD + /* Warn if a .word value is too large when treated as a signed + number. We already know it is not too negative. This is to + catch over-large switches generated by gcc on the 68k. */ + if (!flag_signed_overflow_ok + && fixP->fx_size == 2 + && add_number > 0x7fff) + as_bad_where (fixP->fx_file, fixP->fx_line, + _("signed .word overflow; switch may be too large; %ld at 0x%lx"), + (long) add_number, + (long) (fragP->fr_address + fixP->fx_where)); +#endif + } /* Not a bit fix. */ + +#ifdef TC_VALIDATE_FIX + skip: ATTRIBUTE_UNUSED_LABEL + ; +#endif +#ifdef DEBUG5 + fprintf (stderr, "result:\n"); + print_fixup (fixP); +#endif + } /* For each fixS in this segment. */ +} + +static void +fix_segment (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, + void *xxx ATTRIBUTE_UNUSED) +{ + segment_info_type *seginfo = seg_info (sec); + + fixup_segment (seginfo->fix_root, sec); +} + +static void +install_reloc (asection *sec, arelent *reloc, fragS *fragp, + char *file, unsigned int line) +{ + char *err; + bfd_reloc_status_type s; + asymbol *sym; + + if (reloc->sym_ptr_ptr != NULL + && (sym = *reloc->sym_ptr_ptr) != NULL + && (sym->flags & BSF_KEEP) == 0 + && ((sym->flags & BSF_SECTION_SYM) == 0 + || (EMIT_SECTION_SYMBOLS + && !bfd_is_abs_section (sym->section)))) + as_bad_where (file, line, _("redefined symbol cannot be used on reloc")); + + s = bfd_install_relocation (stdoutput, reloc, + fragp->fr_literal, fragp->fr_address, + sec, &err); + switch (s) + { + case bfd_reloc_ok: + break; + case bfd_reloc_overflow: + as_bad_where (file, line, _("relocation overflow")); + break; + case bfd_reloc_outofrange: + as_bad_where (file, line, _("relocation out of range")); + break; + default: + as_fatal (_("%s:%u: bad return from bfd_install_relocation: %x"), + file, line, s); + } +} + +static fragS * +get_frag_for_reloc (fragS *last_frag, + const segment_info_type *seginfo, + const struct reloc_list *r) +{ + fragS *f; + + for (f = last_frag; f != NULL; f = f->fr_next) + if (f->fr_address <= r->u.b.r.address + && r->u.b.r.address < f->fr_address + f->fr_fix) + return f; + + for (f = seginfo->frchainP->frch_root; f != NULL; f = f->fr_next) + if (f->fr_address <= r->u.b.r.address + && r->u.b.r.address < f->fr_address + f->fr_fix) + return f; + + as_bad_where (r->file, r->line, + _("reloc not within (fixed part of) section")); + return NULL; +} + +static void +write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED) +{ + segment_info_type *seginfo = seg_info (sec); + unsigned int n; + struct reloc_list *my_reloc_list, **rp, *r; + arelent **relocs; + fixS *fixp; + fragS *last_frag; + + /* If seginfo is NULL, we did not create this section; don't do + anything with it. */ + if (seginfo == NULL) + return; + + n = 0; + for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next) + if (!fixp->fx_done) + n++; + +#ifdef RELOC_EXPANSION_POSSIBLE + n *= MAX_RELOC_EXPANSION; +#endif + + /* Extract relocs for this section from reloc_list. */ + rp = &reloc_list; + my_reloc_list = NULL; + while ((r = *rp) != NULL) + { + if (r->u.b.sec == sec) + { + *rp = r->next; + r->next = my_reloc_list; + my_reloc_list = r; + n++; + } + else + rp = &r->next; + } + + relocs = (arelent **) xcalloc (n, sizeof (arelent *)); + + n = 0; + r = my_reloc_list; + last_frag = NULL; + for (fixp = seginfo->fix_root; fixp != (fixS *) NULL; fixp = fixp->fx_next) + { + int fx_size, slack; + offsetT loc; + arelent **reloc; +#ifndef RELOC_EXPANSION_POSSIBLE + arelent *rel; + + reloc = &rel; +#endif + + if (fixp->fx_done) + continue; + + fx_size = fixp->fx_size; + slack = TC_FX_SIZE_SLACK (fixp); + if (slack > 0) + fx_size = fx_size > slack ? fx_size - slack : 0; + loc = fixp->fx_where + fx_size; + if (slack >= 0 && loc > fixp->fx_frag->fr_fix) + as_bad_where (fixp->fx_file, fixp->fx_line, + _("internal error: fixup not contained within frag")); + +#ifndef RELOC_EXPANSION_POSSIBLE + *reloc = tc_gen_reloc (sec, fixp); +#else + reloc = tc_gen_reloc (sec, fixp); +#endif + + while (*reloc) + { + while (r != NULL && r->u.b.r.address < (*reloc)->address) + { + fragS *f = get_frag_for_reloc (last_frag, seginfo, r); + if (f != NULL) + { + last_frag = f; + relocs[n++] = &r->u.b.r; + install_reloc (sec, &r->u.b.r, f, r->file, r->line); + } + r = r->next; + } + relocs[n++] = *reloc; + install_reloc (sec, *reloc, fixp->fx_frag, + fixp->fx_file, fixp->fx_line); +#ifndef RELOC_EXPANSION_POSSIBLE + break; +#else + reloc++; +#endif + } + } + + while (r != NULL) + { + fragS *f = get_frag_for_reloc (last_frag, seginfo, r); + if (f != NULL) + { + last_frag = f; + relocs[n++] = &r->u.b.r; + install_reloc (sec, &r->u.b.r, f, r->file, r->line); + } + r = r->next; + } + +#ifdef DEBUG4 + { + unsigned int k, j, nsyms; + asymbol **sympp; + sympp = bfd_get_outsymbols (stdoutput); + nsyms = bfd_get_symcount (stdoutput); + for (k = 0; k < n; k++) + if (((*relocs[k]->sym_ptr_ptr)->flags & BSF_SECTION_SYM) == 0) + { + for (j = 0; j < nsyms; j++) + if (sympp[j] == *relocs[k]->sym_ptr_ptr) + break; + if (j == nsyms) + abort (); + } + } +#endif + + if (n) + { + flagword flags = bfd_get_section_flags (abfd, sec); + flags |= SEC_RELOC; + bfd_set_section_flags (abfd, sec, flags); + bfd_set_reloc (stdoutput, sec, relocs, n); + } + +#ifdef SET_SECTION_RELOCS + SET_SECTION_RELOCS (sec, relocs, n); +#endif + +#ifdef DEBUG3 + { + unsigned int k; + + fprintf (stderr, "relocs for sec %s\n", sec->name); + for (k = 0; k < n; k++) + { + arelent *rel = relocs[k]; + asymbol *s = *rel->sym_ptr_ptr; + fprintf (stderr, " reloc %2d @%p off %4lx : sym %-10s addend %lx\n", + k, rel, (unsigned long)rel->address, s->name, + (unsigned long)rel->addend); + } + } +#endif +} + +static int +compress_frag (struct z_stream_s *strm, const char *contents, int in_size, + fragS **last_newf, struct obstack *ob) +{ + int out_size; + int total_out_size = 0; + fragS *f = *last_newf; + char *next_out; + int avail_out; + + /* Call the compression routine repeatedly until it has finished + processing the frag. */ + while (in_size > 0) + { + /* Reserve all the space available in the current chunk. + If none is available, start a new frag. */ + avail_out = obstack_room (ob); + if (avail_out <= 0) + { + obstack_finish (ob); + f = frag_alloc (ob); + f->fr_type = rs_fill; + (*last_newf)->fr_next = f; + *last_newf = f; + avail_out = obstack_room (ob); + } + if (avail_out <= 0) + as_fatal (_("can't extend frag")); + next_out = obstack_next_free (ob); + obstack_blank_fast (ob, avail_out); + out_size = compress_data (strm, &contents, &in_size, + &next_out, &avail_out); + if (out_size < 0) + return -1; + + f->fr_fix += out_size; + total_out_size += out_size; + + /* Return unused space. */ + if (avail_out > 0) + obstack_blank_fast (ob, -avail_out); + } + + return total_out_size; +} + +static void +compress_debug (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED) +{ + segment_info_type *seginfo = seg_info (sec); + fragS *f; + fragS *first_newf; + fragS *last_newf; + struct obstack *ob = &seginfo->frchainP->frch_obstack; + bfd_size_type uncompressed_size = (bfd_size_type) sec->size; + bfd_size_type compressed_size; + const char *section_name; + char *compressed_name; + char *header; + struct z_stream_s *strm; + int x; + flagword flags = bfd_get_section_flags (abfd, sec); + + if (seginfo == NULL + || sec->size < 32 + || (flags & (SEC_ALLOC | SEC_HAS_CONTENTS)) == SEC_ALLOC) + return; + + section_name = bfd_get_section_name (stdoutput, sec); + if (strncmp (section_name, ".debug_", 7) != 0) + return; + + strm = compress_init (); + if (strm == NULL) + return; + + /* Create a new frag to contain the "ZLIB" header. */ + first_newf = frag_alloc (ob); + if (obstack_room (ob) < 12) + first_newf = frag_alloc (ob); + if (obstack_room (ob) < 12) + as_fatal (_("can't extend frag %u chars"), 12); + last_newf = first_newf; + obstack_blank_fast (ob, 12); + last_newf->fr_type = rs_fill; + last_newf->fr_fix = 12; + header = last_newf->fr_literal; + memcpy (header, "ZLIB", 4); + header[11] = uncompressed_size; uncompressed_size >>= 8; + header[10] = uncompressed_size; uncompressed_size >>= 8; + header[9] = uncompressed_size; uncompressed_size >>= 8; + header[8] = uncompressed_size; uncompressed_size >>= 8; + header[7] = uncompressed_size; uncompressed_size >>= 8; + header[6] = uncompressed_size; uncompressed_size >>= 8; + header[5] = uncompressed_size; uncompressed_size >>= 8; + header[4] = uncompressed_size; + compressed_size = 12; + + /* Stream the frags through the compression engine, adding new frags + as necessary to accomodate the compressed output. */ + for (f = seginfo->frchainP->frch_root; + f; + f = f->fr_next) + { + offsetT fill_size; + char *fill_literal; + offsetT count; + int out_size; + + gas_assert (f->fr_type == rs_fill); + if (f->fr_fix) + { + out_size = compress_frag (strm, f->fr_literal, f->fr_fix, + &last_newf, ob); + if (out_size < 0) + return; + compressed_size += out_size; + } + fill_literal = f->fr_literal + f->fr_fix; + fill_size = f->fr_var; + count = f->fr_offset; + gas_assert (count >= 0); + if (fill_size && count) + { + while (count--) + { + out_size = compress_frag (strm, fill_literal, (int) fill_size, + &last_newf, ob); + if (out_size < 0) + return; + compressed_size += out_size; + } + } + } + + /* Flush the compression state. */ + for (;;) + { + int avail_out; + char *next_out; + int out_size; + + /* Reserve all the space available in the current chunk. + If none is available, start a new frag. */ + avail_out = obstack_room (ob); + if (avail_out <= 0) + { + fragS *newf; + + obstack_finish (ob); + newf = frag_alloc (ob); + newf->fr_type = rs_fill; + last_newf->fr_next = newf; + last_newf = newf; + avail_out = obstack_room (ob); + } + if (avail_out <= 0) + as_fatal (_("can't extend frag")); + next_out = obstack_next_free (ob); + obstack_blank_fast (ob, avail_out); + x = compress_finish (strm, &next_out, &avail_out, &out_size); + if (x < 0) + return; + + last_newf->fr_fix += out_size; + compressed_size += out_size; + + /* Return unused space. */ + if (avail_out > 0) + obstack_blank_fast (ob, -avail_out); + + if (x == 0) + break; + } + + /* Replace the uncompressed frag list with the compressed frag list. */ + seginfo->frchainP->frch_root = first_newf; + seginfo->frchainP->frch_last = last_newf; + + /* Update the section size and its name. */ + x = bfd_set_section_size (abfd, sec, compressed_size); + gas_assert (x); + compressed_name = (char *) xmalloc (strlen (section_name) + 2); + compressed_name[0] = '.'; + compressed_name[1] = 'z'; + strcpy (compressed_name + 2, section_name + 1); + bfd_section_name (stdoutput, sec) = compressed_name; +} + +static void +write_contents (bfd *abfd ATTRIBUTE_UNUSED, + asection *sec, + void *xxx ATTRIBUTE_UNUSED) +{ + segment_info_type *seginfo = seg_info (sec); + addressT offset = 0; + fragS *f; + + /* Write out the frags. */ + if (seginfo == NULL + || !(bfd_get_section_flags (abfd, sec) & SEC_HAS_CONTENTS)) + return; + + for (f = seginfo->frchainP->frch_root; + f; + f = f->fr_next) + { + int x; + addressT fill_size; + char *fill_literal; + offsetT count; + + gas_assert (f->fr_type == rs_fill); + if (f->fr_fix) + { + x = bfd_set_section_contents (stdoutput, sec, + f->fr_literal, (file_ptr) offset, + (bfd_size_type) f->fr_fix); + if (!x) + as_fatal (_("can't write %s: %s"), stdoutput->filename, + bfd_errmsg (bfd_get_error ())); + offset += f->fr_fix; + } + fill_literal = f->fr_literal + f->fr_fix; + fill_size = f->fr_var; + count = f->fr_offset; + gas_assert (count >= 0); + if (fill_size && count) + { + char buf[256]; + if (fill_size > sizeof (buf)) + { + /* Do it the old way. Can this ever happen? */ + while (count--) + { + x = bfd_set_section_contents (stdoutput, sec, + fill_literal, + (file_ptr) offset, + (bfd_size_type) fill_size); + if (!x) + as_fatal (_("can't write %s: %s"), stdoutput->filename, + bfd_errmsg (bfd_get_error ())); + offset += fill_size; + } + } + else + { + /* Build a buffer full of fill objects and output it as + often as necessary. This saves on the overhead of + potentially lots of bfd_set_section_contents calls. */ + int n_per_buf, i; + if (fill_size == 1) + { + n_per_buf = sizeof (buf); + memset (buf, *fill_literal, n_per_buf); + } + else + { + char *bufp; + n_per_buf = sizeof (buf) / fill_size; + for (i = n_per_buf, bufp = buf; i; i--, bufp += fill_size) + memcpy (bufp, fill_literal, fill_size); + } + for (; count > 0; count -= n_per_buf) + { + n_per_buf = n_per_buf > count ? count : n_per_buf; + x = bfd_set_section_contents + (stdoutput, sec, buf, (file_ptr) offset, + (bfd_size_type) n_per_buf * fill_size); + if (!x) + as_fatal (_("cannot write to output file '%s': %s"), + stdoutput->filename, + bfd_errmsg (bfd_get_error ())); + offset += n_per_buf * fill_size; + } + } + } + } +} + +static void +merge_data_into_text (void) +{ + seg_info (text_section)->frchainP->frch_last->fr_next = + seg_info (data_section)->frchainP->frch_root; + seg_info (text_section)->frchainP->frch_last = + seg_info (data_section)->frchainP->frch_last; + seg_info (data_section)->frchainP = 0; +} + +static void +set_symtab (void) +{ + int nsyms; + asymbol **asympp; + symbolS *symp; + bfd_boolean result; + + /* Count symbols. We can't rely on a count made by the loop in + write_object_file, because *_frob_file may add a new symbol or + two. */ + nsyms = 0; + for (symp = symbol_rootP; symp; symp = symbol_next (symp)) + nsyms++; + + if (nsyms) + { + int i; + bfd_size_type amt = (bfd_size_type) nsyms * sizeof (asymbol *); + + asympp = (asymbol **) bfd_alloc (stdoutput, amt); + symp = symbol_rootP; + for (i = 0; i < nsyms; i++, symp = symbol_next (symp)) + { + asympp[i] = symbol_get_bfdsym (symp); + if (asympp[i]->flags != BSF_SECTION_SYM + || !(bfd_is_const_section (asympp[i]->section) + && asympp[i]->section->symbol == asympp[i])) + asympp[i]->flags |= BSF_KEEP; + symbol_mark_written (symp); + } + } + else + asympp = 0; + result = bfd_set_symtab (stdoutput, asympp, nsyms); + gas_assert (result); + symbol_table_frozen = 1; +} + +/* Finish the subsegments. After every sub-segment, we fake an + ".align ...". This conforms to BSD4.2 brane-damage. We then fake + ".fill 0" because that is the kind of frag that requires least + thought. ".align" frags like to have a following frag since that + makes calculating their intended length trivial. */ + +#ifndef SUB_SEGMENT_ALIGN +#ifdef HANDLE_ALIGN +/* The last subsegment gets an alignment corresponding to the alignment + of the section. This allows proper nop-filling at the end of + code-bearing sections. */ +#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) \ + (!(FRCHAIN)->frch_next ? get_recorded_alignment (SEG) : 0) +#else +#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 0 +#endif +#endif + +void +subsegs_finish (void) +{ + struct frchain *frchainP; + asection *s; + + for (s = stdoutput->sections; s; s = s->next) + { + segment_info_type *seginfo = seg_info (s); + if (!seginfo) + continue; + + for (frchainP = seginfo->frchainP; + frchainP != NULL; + frchainP = frchainP->frch_next) + { + int alignment = 0; + + subseg_set (s, frchainP->frch_subseg); + + /* This now gets called even if we had errors. In that case, + any alignment is meaningless, and, moreover, will look weird + if we are generating a listing. */ + if (!had_errors ()) + { + alignment = SUB_SEGMENT_ALIGN (now_seg, frchainP); + if ((bfd_get_section_flags (now_seg->owner, now_seg) & SEC_MERGE) + && now_seg->entsize) + { + unsigned int entsize = now_seg->entsize; + int entalign = 0; + + while ((entsize & 1) == 0) + { + ++entalign; + entsize >>= 1; + } + if (entalign > alignment) + alignment = entalign; + } + } + + if (subseg_text_p (now_seg)) + frag_align_code (alignment, 0); + else + frag_align (alignment, 0, 0); + + /* frag_align will have left a new frag. + Use this last frag for an empty ".fill". + + For this segment ... + Create a last frag. Do not leave a "being filled in frag". */ + frag_wane (frag_now); + frag_now->fr_fix = 0; + know (frag_now->fr_next == NULL); + } + } +} + +/* Write the object file. */ + +void +write_object_file (void) +{ + struct relax_seg_info rsi; +#ifndef WORKING_DOT_WORD + fragS *fragP; /* Track along all frags. */ +#endif + +#ifdef md_pre_output_hook + md_pre_output_hook; +#endif + + /* Do we really want to write it? */ + { + int n_warns, n_errs; + n_warns = had_warnings (); + n_errs = had_errors (); + /* The -Z flag indicates that an object file should be generated, + regardless of warnings and errors. */ + if (flag_always_generate_output) + { + if (n_warns || n_errs) + as_warn (_("%d error%s, %d warning%s, generating bad object file"), + n_errs, n_errs == 1 ? "" : "s", + n_warns, n_warns == 1 ? "" : "s"); + } + else + { + if (n_errs) + as_fatal (_("%d error%s, %d warning%s, no object file generated"), + n_errs, n_errs == 1 ? "" : "s", + n_warns, n_warns == 1 ? "" : "s"); + } + } + +#ifdef md_pre_relax_hook + md_pre_relax_hook; +#endif + + /* From now on, we don't care about sub-segments. Build one frag chain + for each segment. Linked thru fr_next. */ + + /* Remove the sections created by gas for its own purposes. */ + { + int i; + + bfd_section_list_remove (stdoutput, reg_section); + bfd_section_list_remove (stdoutput, expr_section); + stdoutput->section_count -= 2; + i = 0; + bfd_map_over_sections (stdoutput, renumber_sections, &i); + } + + bfd_map_over_sections (stdoutput, chain_frchains_together, (char *) 0); + + /* We have two segments. If user gave -R flag, then we must put the + data frags into the text segment. Do this before relaxing so + we know to take advantage of -R and make shorter addresses. */ + if (flag_readonly_data_in_text) + { + merge_data_into_text (); + } + + rsi.pass = 0; + while (1) + { +#ifndef WORKING_DOT_WORD + /* We need to reset the markers in the broken word list and + associated frags between calls to relax_segment (via + relax_seg). Since the broken word list is global, we do it + once per round, rather than locally in relax_segment for each + segment. */ + struct broken_word *brokp; + + for (brokp = broken_words; + brokp != (struct broken_word *) NULL; + brokp = brokp->next_broken_word) + { + brokp->added = 0; + + if (brokp->dispfrag != (fragS *) NULL + && brokp->dispfrag->fr_type == rs_broken_word) + brokp->dispfrag->fr_subtype = 0; + } +#endif + + rsi.changed = 0; + bfd_map_over_sections (stdoutput, relax_seg, &rsi); + rsi.pass++; + if (!rsi.changed) + break; + } + + /* Note - Most ports will use the default value of + TC_FINALIZE_SYMS_BEFORE_SIZE_SEG, which 1. This will force + local symbols to be resolved, removing their frag information. + Some ports however, will not have finished relaxing all of + their frags and will still need the local symbol frag + information. These ports can set + TC_FINALIZE_SYMS_BEFORE_SIZE_SEG to 0. */ + finalize_syms = TC_FINALIZE_SYMS_BEFORE_SIZE_SEG; + + bfd_map_over_sections (stdoutput, size_seg, (char *) 0); + + /* Relaxation has completed. Freeze all syms. */ + finalize_syms = 1; + +#ifdef md_post_relax_hook + md_post_relax_hook; +#endif + +#ifndef WORKING_DOT_WORD + { + struct broken_word *lie; + struct broken_word **prevP; + + prevP = &broken_words; + for (lie = broken_words; lie; lie = lie->next_broken_word) + if (!lie->added) + { + expressionS exp; + + subseg_change (lie->seg, lie->subseg); + exp.X_op = O_subtract; + exp.X_add_symbol = lie->add; + exp.X_op_symbol = lie->sub; + exp.X_add_number = lie->addnum; +#ifdef TC_CONS_FIX_NEW + TC_CONS_FIX_NEW (lie->frag, + lie->word_goes_here - lie->frag->fr_literal, + 2, &exp); +#else + fix_new_exp (lie->frag, + lie->word_goes_here - lie->frag->fr_literal, + 2, &exp, 0, BFD_RELOC_16); +#endif + *prevP = lie->next_broken_word; + } + else + prevP = &(lie->next_broken_word); + + for (lie = broken_words; lie;) + { + struct broken_word *untruth; + char *table_ptr; + addressT table_addr; + addressT from_addr, to_addr; + int n, m; + + subseg_change (lie->seg, lie->subseg); + fragP = lie->dispfrag; + + /* Find out how many broken_words go here. */ + n = 0; + for (untruth = lie; + untruth && untruth->dispfrag == fragP; + untruth = untruth->next_broken_word) + if (untruth->added == 1) + n++; + + table_ptr = lie->dispfrag->fr_opcode; + table_addr = (lie->dispfrag->fr_address + + (table_ptr - lie->dispfrag->fr_literal)); + /* Create the jump around the long jumps. This is a short + jump from table_ptr+0 to table_ptr+n*long_jump_size. */ + from_addr = table_addr; + to_addr = table_addr + md_short_jump_size + n * md_long_jump_size; + md_create_short_jump (table_ptr, from_addr, to_addr, lie->dispfrag, + lie->add); + table_ptr += md_short_jump_size; + table_addr += md_short_jump_size; + + for (m = 0; + lie && lie->dispfrag == fragP; + m++, lie = lie->next_broken_word) + { + if (lie->added == 2) + continue; + /* Patch the jump table. */ + for (untruth = (struct broken_word *) (fragP->fr_symbol); + untruth && untruth->dispfrag == fragP; + untruth = untruth->next_broken_word) + { + if (untruth->use_jump == lie) + { + /* This is the offset from ??? to table_ptr+0. + The target is the same for all users of this + md_long_jump, but the "sub" bases (and hence the + offsets) may be different. */ + addressT to_word = table_addr - S_GET_VALUE (untruth->sub); +#ifdef TC_CHECK_ADJUSTED_BROKEN_DOT_WORD + TC_CHECK_ADJUSTED_BROKEN_DOT_WORD (to_word, untruth); +#endif + md_number_to_chars (untruth->word_goes_here, to_word, 2); + } + } + + /* Install the long jump. */ + /* This is a long jump from table_ptr+0 to the final target. */ + from_addr = table_addr; + to_addr = S_GET_VALUE (lie->add) + lie->addnum; + md_create_long_jump (table_ptr, from_addr, to_addr, lie->dispfrag, + lie->add); + table_ptr += md_long_jump_size; + table_addr += md_long_jump_size; + } + } + } +#endif /* not WORKING_DOT_WORD */ + + /* Resolve symbol values. This needs to be done before processing + the relocations. */ + if (symbol_rootP) + { + symbolS *symp; + + for (symp = symbol_rootP; symp; symp = symbol_next (symp)) + resolve_symbol_value (symp); + } + resolve_local_symbol_values (); + resolve_reloc_expr_symbols (); + + PROGRESS (1); + +#ifdef tc_frob_file_before_adjust + tc_frob_file_before_adjust (); +#endif +#ifdef obj_frob_file_before_adjust + obj_frob_file_before_adjust (); +#endif + + bfd_map_over_sections (stdoutput, adjust_reloc_syms, (char *) 0); + +#ifdef tc_frob_file_before_fix + tc_frob_file_before_fix (); +#endif +#ifdef obj_frob_file_before_fix + obj_frob_file_before_fix (); +#endif + + bfd_map_over_sections (stdoutput, fix_segment, (char *) 0); + + /* Set up symbol table, and write it out. */ + if (symbol_rootP) + { + symbolS *symp; + bfd_boolean skip_next_symbol = FALSE; + + for (symp = symbol_rootP; symp; symp = symbol_next (symp)) + { + int punt = 0; + const char *name; + + if (skip_next_symbol) + { + /* Don't do anything besides moving the value of the + symbol from the GAS value-field to the BFD value-field. */ + symbol_get_bfdsym (symp)->value = S_GET_VALUE (symp); + skip_next_symbol = FALSE; + continue; + } + + if (symbol_mri_common_p (symp)) + { + if (S_IS_EXTERNAL (symp)) + as_bad (_("%s: global symbols not supported in common sections"), + S_GET_NAME (symp)); + symbol_remove (symp, &symbol_rootP, &symbol_lastP); + continue; + } + + name = S_GET_NAME (symp); + if (name) + { + const char *name2 = + decode_local_label_name ((char *) S_GET_NAME (symp)); + /* They only differ if `name' is a fb or dollar local + label name. */ + if (name2 != name && ! S_IS_DEFINED (symp)) + as_bad (_("local label `%s' is not defined"), name2); + } + + /* Do it again, because adjust_reloc_syms might introduce + more symbols. They'll probably only be section symbols, + but they'll still need to have the values computed. */ + resolve_symbol_value (symp); + + /* Skip symbols which were equated to undefined or common + symbols. */ + if (symbol_equated_reloc_p (symp) + || S_IS_WEAKREFR (symp)) + { + const char *sname = S_GET_NAME (symp); + + if (S_IS_COMMON (symp) + && !TC_FAKE_LABEL (sname) + && !S_IS_WEAKREFR (symp) + && (!S_IS_EXTERNAL (symp) || S_IS_LOCAL (symp))) + { + expressionS *e = symbol_get_value_expression (symp); + + as_bad (_("Local symbol `%s' can't be equated to common symbol `%s'"), + sname, S_GET_NAME (e->X_add_symbol)); + } + if (S_GET_SEGMENT (symp) == reg_section) + { + /* Report error only if we know the symbol name. */ + if (S_GET_NAME (symp) != reg_section->name) + as_bad (_("can't make global register symbol `%s'"), + sname); + } + symbol_remove (symp, &symbol_rootP, &symbol_lastP); + continue; + } + +#ifdef obj_frob_symbol + obj_frob_symbol (symp, punt); +#endif +#ifdef tc_frob_symbol + if (! punt || symbol_used_in_reloc_p (symp)) + tc_frob_symbol (symp, punt); +#endif + + /* If we don't want to keep this symbol, splice it out of + the chain now. If EMIT_SECTION_SYMBOLS is 0, we never + want section symbols. Otherwise, we skip local symbols + and symbols that the frob_symbol macros told us to punt, + but we keep such symbols if they are used in relocs. */ + if (symp == abs_section_sym + || (! EMIT_SECTION_SYMBOLS + && symbol_section_p (symp)) + /* Note that S_IS_EXTERNAL and S_IS_LOCAL are not always + opposites. Sometimes the former checks flags and the + latter examines the name... */ + || (!S_IS_EXTERNAL (symp) + && (punt || S_IS_LOCAL (symp) || + (S_IS_WEAKREFD (symp) && ! symbol_used_p (symp))) + && ! symbol_used_in_reloc_p (symp))) + { + symbol_remove (symp, &symbol_rootP, &symbol_lastP); + + /* After symbol_remove, symbol_next(symp) still returns + the one that came after it in the chain. So we don't + need to do any extra cleanup work here. */ + continue; + } + + /* Make sure we really got a value for the symbol. */ + if (! symbol_resolved_p (symp)) + { + as_bad (_("can't resolve value for symbol `%s'"), + S_GET_NAME (symp)); + symbol_mark_resolved (symp); + } + + /* Set the value into the BFD symbol. Up til now the value + has only been kept in the gas symbolS struct. */ + symbol_get_bfdsym (symp)->value = S_GET_VALUE (symp); + + /* A warning construct is a warning symbol followed by the + symbol warned about. Don't let anything object-format or + target-specific muck with it; it's ready for output. */ + if (symbol_get_bfdsym (symp)->flags & BSF_WARNING) + skip_next_symbol = TRUE; + } + } + + PROGRESS (1); + + /* Now do any format-specific adjustments to the symbol table, such + as adding file symbols. */ +#ifdef tc_adjust_symtab + tc_adjust_symtab (); +#endif +#ifdef obj_adjust_symtab + obj_adjust_symtab (); +#endif + + /* Stop if there is an error. */ + if (had_errors ()) + return; + + /* Now that all the sizes are known, and contents correct, we can + start writing to the file. */ + set_symtab (); + + /* If *_frob_file changes the symbol value at this point, it is + responsible for moving the changed value into symp->bsym->value + as well. Hopefully all symbol value changing can be done in + *_frob_symbol. */ +#ifdef tc_frob_file + tc_frob_file (); +#endif +#ifdef obj_frob_file + obj_frob_file (); +#endif +#ifdef obj_coff_generate_pdata + obj_coff_generate_pdata (); +#endif + bfd_map_over_sections (stdoutput, write_relocs, (char *) 0); + +#ifdef tc_frob_file_after_relocs + tc_frob_file_after_relocs (); +#endif +#ifdef obj_frob_file_after_relocs + obj_frob_file_after_relocs (); +#endif + + /* Once all relocations have been written, we can compress the + contents of the debug sections. This needs to be done before + we start writing any sections, because it will affect the file + layout, which is fixed once we start writing contents. */ + if (flag_compress_debug) + bfd_map_over_sections (stdoutput, compress_debug, (char *) 0); + + bfd_map_over_sections (stdoutput, write_contents, (char *) 0); +} + +#ifdef TC_GENERIC_RELAX_TABLE +/* Relax a fragment by scanning TC_GENERIC_RELAX_TABLE. */ + +long +relax_frag (segT segment, fragS *fragP, long stretch) +{ + const relax_typeS *this_type; + const relax_typeS *start_type; + relax_substateT next_state; + relax_substateT this_state; + offsetT growth; + offsetT aim; + addressT target; + addressT address; + symbolS *symbolP; + const relax_typeS *table; + + target = fragP->fr_offset; + address = fragP->fr_address; + table = TC_GENERIC_RELAX_TABLE; + this_state = fragP->fr_subtype; + start_type = this_type = table + this_state; + symbolP = fragP->fr_symbol; + + if (symbolP) + { + fragS *sym_frag; + + sym_frag = symbol_get_frag (symbolP); + +#ifndef DIFF_EXPR_OK + know (sym_frag != NULL); +#endif + know (S_GET_SEGMENT (symbolP) != absolute_section + || sym_frag == &zero_address_frag); + target += S_GET_VALUE (symbolP); + + /* If SYM_FRAG has yet to be reached on this pass, assume it + will move by STRETCH just as we did, unless there is an + alignment frag between here and SYM_FRAG. An alignment may + well absorb any STRETCH, and we don't want to choose a larger + branch insn by overestimating the needed reach of this + branch. It isn't critical to calculate TARGET exactly; We + know we'll be doing another pass if STRETCH is non-zero. */ + + if (stretch != 0 + && sym_frag->relax_marker != fragP->relax_marker + && S_GET_SEGMENT (symbolP) == segment) + { + if (stretch < 0 + || sym_frag->region == fragP->region) + target += stretch; + /* If we get here we know we have a forward branch. This + relax pass may have stretched previous instructions so + far that omitting STRETCH would make the branch + negative. Don't allow this in case the negative reach is + large enough to require a larger branch instruction. */ + else if (target < address) + target = fragP->fr_next->fr_address + stretch; + } + } + + aim = target - address - fragP->fr_fix; +#ifdef TC_PCREL_ADJUST + /* Currently only the ns32k family needs this. */ + aim += TC_PCREL_ADJUST (fragP); +#endif + +#ifdef md_prepare_relax_scan + /* Formerly called M68K_AIM_KLUDGE. */ + md_prepare_relax_scan (fragP, address, aim, this_state, this_type); +#endif + + if (aim < 0) + { + /* Look backwards. */ + for (next_state = this_type->rlx_more; next_state;) + if (aim >= this_type->rlx_backward) + next_state = 0; + else + { + /* Grow to next state. */ + this_state = next_state; + this_type = table + this_state; + next_state = this_type->rlx_more; + } + } + else + { + /* Look forwards. */ + for (next_state = this_type->rlx_more; next_state;) + if (aim <= this_type->rlx_forward) + next_state = 0; + else + { + /* Grow to next state. */ + this_state = next_state; + this_type = table + this_state; + next_state = this_type->rlx_more; + } + } + + growth = this_type->rlx_length - start_type->rlx_length; + if (growth != 0) + fragP->fr_subtype = this_state; + return growth; +} + +#endif /* defined (TC_GENERIC_RELAX_TABLE) */ + +/* Relax_align. Advance location counter to next address that has 'alignment' + lowest order bits all 0s, return size of adjustment made. */ +static relax_addressT +relax_align (register relax_addressT address, /* Address now. */ + register int alignment /* Alignment (binary). */) +{ + relax_addressT mask; + relax_addressT new_address; + + mask = ~((~0) << alignment); + new_address = (address + mask) & (~mask); +#ifdef LINKER_RELAXING_SHRINKS_ONLY + if (linkrelax) + /* We must provide lots of padding, so the linker can discard it + when needed. The linker will not add extra space, ever. */ + new_address += (1 << alignment); +#endif + return (new_address - address); +} + +/* Now we have a segment, not a crowd of sub-segments, we can make + fr_address values. + + Relax the frags. + + After this, all frags in this segment have addresses that are correct + within the segment. Since segments live in different file addresses, + these frag addresses may not be the same as final object-file + addresses. */ + +int +relax_segment (struct frag *segment_frag_root, segT segment, int pass) +{ + unsigned long frag_count; + struct frag *fragP; + relax_addressT address; + int region; + int ret; + + /* In case md_estimate_size_before_relax() wants to make fixSs. */ + subseg_change (segment, 0); + + /* For each frag in segment: count and store (a 1st guess of) + fr_address. */ + address = 0; + region = 0; + for (frag_count = 0, fragP = segment_frag_root; + fragP; + fragP = fragP->fr_next, frag_count ++) + { + fragP->region = region; + fragP->relax_marker = 0; + fragP->fr_address = address; + address += fragP->fr_fix; + + switch (fragP->fr_type) + { + case rs_fill: + address += fragP->fr_offset * fragP->fr_var; + break; + + case rs_align: + case rs_align_code: + case rs_align_test: + { + addressT offset = relax_align (address, (int) fragP->fr_offset); + + if (fragP->fr_subtype != 0 && offset > fragP->fr_subtype) + offset = 0; + + if (offset % fragP->fr_var != 0) + { + as_bad_where (fragP->fr_file, fragP->fr_line, + _("alignment padding (%lu bytes) not a multiple of %ld"), + (unsigned long) offset, (long) fragP->fr_var); + offset -= (offset % fragP->fr_var); + } + + address += offset; + region += 1; + } + break; + + case rs_org: + /* Assume .org is nugatory. It will grow with 1st relax. */ + region += 1; + break; + + case rs_space: + break; + + case rs_machine_dependent: + /* If fr_symbol is an expression, this call to + resolve_symbol_value sets up the correct segment, which will + likely be needed in md_estimate_size_before_relax. */ + if (fragP->fr_symbol) + resolve_symbol_value (fragP->fr_symbol); + + address += md_estimate_size_before_relax (fragP, segment); + break; + +#ifndef WORKING_DOT_WORD + /* Broken words don't concern us yet. */ + case rs_broken_word: + break; +#endif + + case rs_leb128: + /* Initial guess is always 1; doing otherwise can result in + stable solutions that are larger than the minimum. */ + address += fragP->fr_offset = 1; + break; + + case rs_cfa: + address += eh_frame_estimate_size_before_relax (fragP); + break; + + case rs_dwarf2dbg: + address += dwarf2dbg_estimate_size_before_relax (fragP); + break; + + default: + BAD_CASE (fragP->fr_type); + break; + } + } + + /* Do relax(). */ + { + unsigned long max_iterations; + + /* Cumulative address adjustment. */ + offsetT stretch; + + /* Have we made any adjustment this pass? We can't just test + stretch because one piece of code may have grown and another + shrank. */ + int stretched; + + /* Most horrible, but gcc may give us some exception data that + is impossible to assemble, of the form + + .align 4 + .byte 0, 0 + .uleb128 end - start + start: + .space 128*128 - 1 + .align 4 + end: + + If the leb128 is two bytes in size, then end-start is 128*128, + which requires a three byte leb128. If the leb128 is three + bytes in size, then end-start is 128*128-1, which requires a + two byte leb128. We work around this dilemma by inserting + an extra 4 bytes of alignment just after the .align. This + works because the data after the align is accessed relative to + the end label. + + This counter is used in a tiny state machine to detect + whether a leb128 followed by an align is impossible to + relax. */ + int rs_leb128_fudge = 0; + + /* We want to prevent going into an infinite loop where one frag grows + depending upon the location of a symbol which is in turn moved by + the growing frag. eg: + + foo = . + .org foo+16 + foo = . + + So we dictate that this algorithm can be at most O2. */ + max_iterations = frag_count * frag_count; + /* Check for overflow. */ + if (max_iterations < frag_count) + max_iterations = frag_count; + + ret = 0; + do + { + stretch = 0; + stretched = 0; + + for (fragP = segment_frag_root; fragP; fragP = fragP->fr_next) + { + offsetT growth = 0; + addressT was_address; + offsetT offset; + symbolS *symbolP; + + fragP->relax_marker ^= 1; + was_address = fragP->fr_address; + address = fragP->fr_address += stretch; + symbolP = fragP->fr_symbol; + offset = fragP->fr_offset; + + switch (fragP->fr_type) + { + case rs_fill: /* .fill never relaxes. */ + growth = 0; + break; + +#ifndef WORKING_DOT_WORD + /* JF: This is RMS's idea. I do *NOT* want to be blamed + for it I do not want to write it. I do not want to have + anything to do with it. This is not the proper way to + implement this misfeature. */ + case rs_broken_word: + { + struct broken_word *lie; + struct broken_word *untruth; + + /* Yes this is ugly (storing the broken_word pointer + in the symbol slot). Still, this whole chunk of + code is ugly, and I don't feel like doing anything + about it. Think of it as stubbornness in action. */ + growth = 0; + for (lie = (struct broken_word *) (fragP->fr_symbol); + lie && lie->dispfrag == fragP; + lie = lie->next_broken_word) + { + + if (lie->added) + continue; + + offset = (S_GET_VALUE (lie->add) + + lie->addnum + - S_GET_VALUE (lie->sub)); + if (offset <= -32768 || offset >= 32767) + { + if (flag_warn_displacement) + { + char buf[50]; + sprint_value (buf, (addressT) lie->addnum); + as_warn_where (fragP->fr_file, fragP->fr_line, + _(".word %s-%s+%s didn't fit"), + S_GET_NAME (lie->add), + S_GET_NAME (lie->sub), + buf); + } + if (fragP->fr_subtype == 0) + { + fragP->fr_subtype++; + growth += md_short_jump_size; + } + + /* Redirect *all* words of this table with the same + target, lest we have to handle the case where the + same target but with a offset that fits on this + round overflows at the next relaxation round. */ + for (untruth = (struct broken_word *) (fragP->fr_symbol); + untruth && untruth->dispfrag == lie->dispfrag; + untruth = untruth->next_broken_word) + if ((symbol_get_frag (untruth->add) + == symbol_get_frag (lie->add)) + && (S_GET_VALUE (untruth->add) + == S_GET_VALUE (lie->add))) + { + untruth->added = 2; + untruth->use_jump = lie; + } + + lie->added = 1; + growth += md_long_jump_size; + } + } + + break; + } /* case rs_broken_word */ +#endif + case rs_align: + case rs_align_code: + case rs_align_test: + { + addressT oldoff, newoff; + + oldoff = relax_align (was_address + fragP->fr_fix, + (int) offset); + newoff = relax_align (address + fragP->fr_fix, + (int) offset); + + if (fragP->fr_subtype != 0) + { + if (oldoff > fragP->fr_subtype) + oldoff = 0; + if (newoff > fragP->fr_subtype) + newoff = 0; + } + + growth = newoff - oldoff; + + /* If this align happens to follow a leb128 and + we have determined that the leb128 is bouncing + in size, then break the cycle by inserting an + extra alignment. */ + if (growth < 0 + && (rs_leb128_fudge & 16) != 0 + && (rs_leb128_fudge & 15) >= 2) + { + segment_info_type *seginfo = seg_info (segment); + struct obstack *ob = &seginfo->frchainP->frch_obstack; + struct frag *newf; + + newf = frag_alloc (ob); + obstack_blank_fast (ob, fragP->fr_var); + obstack_finish (ob); + memcpy (newf, fragP, SIZEOF_STRUCT_FRAG); + memcpy (newf->fr_literal, + fragP->fr_literal + fragP->fr_fix, + fragP->fr_var); + newf->fr_type = rs_fill; + newf->fr_address = address + fragP->fr_fix + newoff; + newf->fr_fix = 0; + newf->fr_offset = (((offsetT) 1 << fragP->fr_offset) + / fragP->fr_var); + if (newf->fr_offset * newf->fr_var + != (offsetT) 1 << fragP->fr_offset) + { + newf->fr_offset = (offsetT) 1 << fragP->fr_offset; + newf->fr_var = 1; + } + /* Include size of new frag in GROWTH. */ + growth += newf->fr_offset * newf->fr_var; + /* Adjust the new frag address for the amount + we'll add when we process the new frag. */ + newf->fr_address -= stretch + growth; + newf->relax_marker ^= 1; + fragP->fr_next = newf; +#ifdef DEBUG + as_warn (_("padding added")); +#endif + } + } + break; + + case rs_org: + { + addressT target = offset; + addressT after; + + if (symbolP) + { + /* Convert from an actual address to an octet offset + into the section. Here it is assumed that the + section's VMA is zero, and can omit subtracting it + from the symbol's value to get the address offset. */ + know (S_GET_SEGMENT (symbolP)->vma == 0); + target += S_GET_VALUE (symbolP) * OCTETS_PER_BYTE; + } + + know (fragP->fr_next); + after = fragP->fr_next->fr_address + stretch; + growth = target - after; + if (growth < 0) + { + growth = 0; + + /* Don't error on first few frag relax passes. + The symbol might be an expression involving + symbol values from other sections. If those + sections have not yet been processed their + frags will all have zero addresses, so we + will calculate incorrect values for them. The + number of passes we allow before giving an + error is somewhat arbitrary. It should be at + least one, with larger values requiring + increasingly contrived dependencies between + frags to trigger a false error. */ + if (pass < 2) + { + /* Force another pass. */ + ret = 1; + break; + } + + /* Growth may be negative, but variable part of frag + cannot have fewer than 0 chars. That is, we can't + .org backwards. */ + as_bad_where (fragP->fr_file, fragP->fr_line, + _("attempt to move .org backwards")); + + /* We've issued an error message. Change the + frag to avoid cascading errors. */ + fragP->fr_type = rs_align; + fragP->fr_subtype = 0; + fragP->fr_offset = 0; + fragP->fr_fix = after - address; + } + } + break; + + case rs_space: + growth = 0; + if (symbolP) + { + offsetT amount; + + amount = S_GET_VALUE (symbolP); + if (S_GET_SEGMENT (symbolP) != absolute_section + || S_IS_COMMON (symbolP) + || ! S_IS_DEFINED (symbolP)) + { + as_bad_where (fragP->fr_file, fragP->fr_line, + _(".space specifies non-absolute value")); + /* Prevent repeat of this error message. */ + fragP->fr_symbol = 0; + } + else if (amount < 0) + { + /* Don't error on first few frag relax passes. + See rs_org comment for a longer explanation. */ + if (pass < 2) + { + ret = 1; + break; + } + + as_warn_where (fragP->fr_file, fragP->fr_line, + _(".space or .fill with negative value, ignored")); + fragP->fr_symbol = 0; + } + else + growth = (was_address + fragP->fr_fix + amount + - fragP->fr_next->fr_address); + } + break; + + case rs_machine_dependent: +#ifdef md_relax_frag + growth = md_relax_frag (segment, fragP, stretch); +#else +#ifdef TC_GENERIC_RELAX_TABLE + /* The default way to relax a frag is to look through + TC_GENERIC_RELAX_TABLE. */ + growth = relax_frag (segment, fragP, stretch); +#endif /* TC_GENERIC_RELAX_TABLE */ +#endif + break; + + case rs_leb128: + { + valueT value; + offsetT size; + + value = resolve_symbol_value (fragP->fr_symbol); + size = sizeof_leb128 (value, fragP->fr_subtype); + growth = size - fragP->fr_offset; + fragP->fr_offset = size; + } + break; + + case rs_cfa: + growth = eh_frame_relax_frag (fragP); + break; + + case rs_dwarf2dbg: + growth = dwarf2dbg_relax_frag (fragP); + break; + + default: + BAD_CASE (fragP->fr_type); + break; + } + if (growth) + { + stretch += growth; + stretched = 1; + if (fragP->fr_type == rs_leb128) + rs_leb128_fudge += 16; + else if (fragP->fr_type == rs_align + && (rs_leb128_fudge & 16) != 0 + && stretch == 0) + rs_leb128_fudge += 16; + else + rs_leb128_fudge = 0; + } + } + + if (stretch == 0 + && (rs_leb128_fudge & 16) == 0 + && (rs_leb128_fudge & -16) != 0) + rs_leb128_fudge += 1; + else + rs_leb128_fudge = 0; + } + /* Until nothing further to relax. */ + while (stretched && -- max_iterations); + + if (stretched) + as_fatal (_("Infinite loop encountered whilst attempting to compute the addresses of symbols in section %s"), + segment_name (segment)); + } + + for (fragP = segment_frag_root; fragP; fragP = fragP->fr_next) + if (fragP->last_fr_address != fragP->fr_address) + { + fragP->last_fr_address = fragP->fr_address; + ret = 1; + } + return ret; +} + +void +number_to_chars_bigendian (char *buf, valueT val, int n) +{ + if (n <= 0) + abort (); + while (n--) + { + buf[n] = val & 0xff; + val >>= 8; + } +} + +void +number_to_chars_littleendian (char *buf, valueT val, int n) +{ + if (n <= 0) + abort (); + while (n--) + { + *buf++ = val & 0xff; + val >>= 8; + } +} + +void +write_print_statistics (FILE *file) +{ + fprintf (file, "fixups: %d\n", n_fixups); +} + +/* For debugging. */ +extern int indent_level; + +void +print_fixup (fixS *fixp) +{ + indent_level = 1; + fprintf (stderr, "fix "); + fprintf_vma (stderr, (bfd_vma)((bfd_hostptr_t) fixp)); + fprintf (stderr, " %s:%d",fixp->fx_file, fixp->fx_line); + if (fixp->fx_pcrel) + fprintf (stderr, " pcrel"); + if (fixp->fx_pcrel_adjust) + fprintf (stderr, " pcrel_adjust=%d", fixp->fx_pcrel_adjust); + if (fixp->fx_im_disp) + { +#ifdef TC_NS32K + fprintf (stderr, " im_disp=%d", fixp->fx_im_disp); +#else + fprintf (stderr, " im_disp"); +#endif + } + if (fixp->fx_tcbit) + fprintf (stderr, " tcbit"); + if (fixp->fx_done) + fprintf (stderr, " done"); + fprintf (stderr, "\n size=%d frag=", fixp->fx_size); + fprintf_vma (stderr, (bfd_vma) ((bfd_hostptr_t) fixp->fx_frag)); + fprintf (stderr, " where=%ld offset=%lx addnumber=%lx", + (long) fixp->fx_where, + (unsigned long) fixp->fx_offset, + (unsigned long) fixp->fx_addnumber); + fprintf (stderr, "\n %s (%d)", bfd_get_reloc_code_name (fixp->fx_r_type), + fixp->fx_r_type); + if (fixp->fx_addsy) + { + fprintf (stderr, "\n +<"); + print_symbol_value_1 (stderr, fixp->fx_addsy); + fprintf (stderr, ">"); + } + if (fixp->fx_subsy) + { + fprintf (stderr, "\n -<"); + print_symbol_value_1 (stderr, fixp->fx_subsy); + fprintf (stderr, ">"); + } + fprintf (stderr, "\n"); +#ifdef TC_FIX_DATA_PRINT + TC_FIX_DATA_PRINT (stderr, fixp); +#endif +} diff --git a/contrib/toolchain/binutils/gas/write.h b/contrib/toolchain/binutils/gas/write.h new file mode 100644 index 0000000000..36de533b32 --- /dev/null +++ b/contrib/toolchain/binutils/gas/write.h @@ -0,0 +1,191 @@ +/* write.h + Copyright 1987, 1992, 1993, 1994, 1995, 1996, 1997, 1999, 2000, 2001, + 2002, 2003, 2005, 2006, 2007 + Free Software Foundation, Inc. + + This file is part of GAS, the GNU Assembler. + + GAS is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + GAS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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, 51 Franklin Street - Fifth Floor, Boston, MA + 02110-1301, USA. */ + +#ifndef __write_h__ +#define __write_h__ + +/* This is the name of a fake symbol which will never appear in the + assembler output. S_IS_LOCAL detects it because of the \001. */ +#ifndef FAKE_LABEL_NAME +#define FAKE_LABEL_NAME "L0\001" +#endif + +#include "bit_fix.h" + +/* + * FixSs may be built up in any order. + */ + +struct fix +{ + /* These small fields are grouped together for compactness of + this structure, and efficiency of access on some architectures. */ + + /* Is this a pc-relative relocation? */ + unsigned fx_pcrel : 1; + + /* Is this value an immediate displacement? */ + /* Only used on ns32k; merge it into TC_FIX_TYPE sometime. */ + unsigned fx_im_disp : 2; + + /* Some bits for the CPU specific code. */ + unsigned fx_tcbit : 1; + unsigned fx_tcbit2 : 1; + + /* Has this relocation already been applied? */ + unsigned fx_done : 1; + + /* Suppress overflow complaints on large addends. This is used + in the PowerPC ELF config to allow large addends on the + BFD_RELOC_{LO16,HI16,HI16_S} relocations. + + @@ Can this be determined from BFD? */ + unsigned fx_no_overflow : 1; + + /* The value is signed when checking for overflow. */ + unsigned fx_signed : 1; + + /* pc-relative offset adjust (only used by some CPU specific code) */ + signed char fx_pcrel_adjust; + + /* How many bytes are involved? */ + unsigned char fx_size; + + /* Which frag does this fix apply to? */ + fragS *fx_frag; + + /* Where is the first byte to fix up? */ + long fx_where; + + /* NULL or Symbol whose value we add in. */ + symbolS *fx_addsy; + + /* NULL or Symbol whose value we subtract. */ + symbolS *fx_subsy; + + /* Absolute number we add in. */ + valueT fx_offset; + + /* The value of dot when the fixup expression was parsed. */ + addressT fx_dot_value; + + /* The frag fx_dot_value is based on. */ + fragS *fx_dot_frag; + + /* Next fixS in linked list, or NULL. */ + struct fix *fx_next; + + /* If NULL, no bitfix's to do. */ + /* Only i960-coff and ns32k use this, and i960-coff stores an + integer. This can probably be folded into tc_fix_data, below. + @@ Alpha also uses it, but only to disable certain relocation + processing. */ + bit_fixS *fx_bit_fixP; + + bfd_reloc_code_real_type fx_r_type; + + /* This field is sort of misnamed. It appears to be a sort of random + scratch field, for use by the back ends. The main gas code doesn't + do anything but initialize it to zero. The use of it does need to + be coordinated between the cpu and format files, though. E.g., some + coff targets pass the `addend' field from the cpu file via this + field. I don't know why the `fx_offset' field above can't be used + for that; investigate later and document. KR */ + valueT fx_addnumber; + + /* The location of the instruction which created the reloc, used + in error messages. */ + char *fx_file; + unsigned fx_line; + +#ifdef USING_CGEN + struct { + /* CGEN_INSN entry for this instruction. */ + const struct cgen_insn *insn; + /* Target specific data, usually reloc number. */ + int opinfo; + /* Which ifield this fixup applies to. */ + struct cgen_maybe_multi_ifield * field; + /* is this field is the MSB field in a set? */ + int msb_field_p; + } fx_cgen; +#endif + +#ifdef TC_FIX_TYPE + /* Location where a backend can attach additional data + needed to perform fixups. */ + TC_FIX_TYPE tc_fix_data; +#endif +}; + +typedef struct fix fixS; + +struct reloc_list +{ + struct reloc_list *next; + union + { + struct + { + symbolS *offset_sym; + reloc_howto_type *howto; + symbolS *sym; + bfd_vma addend; + } a; + struct + { + asection *sec; + asymbol *s; + arelent r; + } b; + } u; + char *file; + unsigned int line; +}; + +extern int finalize_syms; +extern symbolS *abs_section_sym; +extern addressT dot_value; +extern fragS *dot_frag; +extern struct reloc_list* reloc_list; + +extern void append (char **charPP, char *fromP, unsigned long length); +extern void record_alignment (segT seg, int align); +extern int get_recorded_alignment (segT seg); +extern void subsegs_finish (void); +extern void write_object_file (void); +extern long relax_frag (segT, fragS *, long); +extern int relax_segment (struct frag *, segT, int); +extern void number_to_chars_littleendian (char *, valueT, int); +extern void number_to_chars_bigendian (char *, valueT, int); +extern fixS *fix_new + (fragS * frag, int where, int size, symbolS * add_symbol, + offsetT offset, int pcrel, bfd_reloc_code_real_type r_type); +extern fixS *fix_at_start + (fragS * frag, int size, symbolS * add_symbol, + offsetT offset, int pcrel, bfd_reloc_code_real_type r_type); +extern fixS *fix_new_exp + (fragS * frag, int where, int size, expressionS *exp, int pcrel, + bfd_reloc_code_real_type r_type); +extern void write_print_statistics (FILE *); + +#endif /* __write_h__ */