Subversion Repositories Kolibri OS

Compare Revisions

No changes between revisions

Regard whitespace Rev 5221 → Rev 5222

/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 $@ $<
 
/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 # <number> <filename> <garbage> 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;
}
/contrib/toolchain/binutils/gas/as
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes:
Added: svn:mime-type
+application/octet-stream
\ No newline at end of property
/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=<value> set the hash table size close to <value>\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=<FILE> is a valid switch but getopt would
normally reject it since --alternate does not take an argument. */
,{"a", optional_argument, NULL, 'a'}
/* Handle -al=<FILE>. */
,{"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);
}
/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 <stdio.h>
 
#ifdef STRING_WITH_STRINGS
#include <string.h>
#include <strings.h>
#else
#ifdef HAVE_STRING_H
#include <string.h>
#else
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif
#endif
#endif
 
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_TYPES_H
/* for size_t, pid_t */
#include <sys/types.h>
#endif
 
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
 
#include <stdarg.h>
 
#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<<OCTETS_PER_BYTE_POWER)
#endif
#if OCTETS_PER_BYTE != (1<<OCTETS_PER_BYTE_POWER)
#error "Octets per byte conflicts with its power-of-two definition!"
#endif
 
#endif /* GAS */
/contrib/toolchain/binutils/gas/asintl.h
0,0 → 1,52
/* asintl.h - gas-specific header for gettext code.
Copyright 1998, 1999, 2000, 2005, 2007 Free Software Foundation, Inc.
 
Written by Tom Tromey <tromey@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. */
 
#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 <locale.h>
#endif
 
#ifdef ENABLE_NLS
# include <libintl.h>
# 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
/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:
 
<flonum> ::= <optional-sign> <decimal-number> <optional-exponent>
<optional-sign> ::= '+' | '-' | {empty}
<decimal-number> ::= <integer>
| <integer> <radix-character>
| <integer> <radix-character> <integer>
| <radix-character> <integer>
 
<optional-exponent> ::= {empty}
| <exponent-character> <optional-sign> <integer>
 
<integer> ::= <digit> | <digit> <integer>
<digit> ::= '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
<exponent-character> ::= {one character from "string_of_decimal_exponent_marks"}
<radix-character> ::= {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 */
/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;
/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__ */
/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 <stdio.h>
#include "ansidecl.h"
#include "compress-debug.h"
 
#ifdef HAVE_ZLIB_H
#include <zlib.h>
#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 */
}
/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 */
/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 (&current_cframe->else_file_line.file,
&current_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 (&current_cframe->else_file_line.file,
&current_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);
}
}
/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;
}
/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);
}
/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 <fct_name>
.seh_endprologue
.seh_handler <handler>[,@unwind][,@except] (x64)
.seh_handler <handler>[,<handler_data>] (others)
.seh_handlerdata
.seh_eh
.seh_32/.seh_no32
.seh_endproc
.seh_setframe <reg>,<offset>
.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
 
/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
};
/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 */
/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;
}
/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], &regxmm));
 
if (i.tm.opcode_modifier.firstxmm0)
{
/* The first operand is implicit and must be xmm0. */
gas_assert (operand_type_equal (&i.types[0], &regxmm));
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], &regxmm)
|| operand_type_equal (&i.types[0], &regymm)
|| operand_type_equal (&i.types[0], &regzmm)));
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], &regxmm)
|| operand_type_equal (&i.tm.operand_types[dest], &regymm)
|| operand_type_equal (&i.tm.operand_types[dest], &regzmm)))));
 
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],
&regxmm)
|| operand_type_equal (&i.tm.operand_types[reg_slot],
&regymm)
|| operand_type_equal (&i.tm.operand_types[reg_slot],
&regzmm));
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],
&regxmm)
|| operand_type_equal (&i.tm.operand_types[reg_slot],
&regymm)
|| operand_type_equal (&i.tm.operand_types[reg_slot],
&regzmm));
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], &regxmm)
|| operand_type_equal (&i.tm.operand_types[nds],
&regymm)
|| operand_type_equal (&i.tm.operand_types[nds],
&regzmm));
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, &regxmm)
&& !operand_type_equal (&op, &regymm)
&& !operand_type_equal (&op, &regzmm)
&& !operand_type_equal (&op, &regmask)))
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 <disp> */
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);
/* <disp>(%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, &regxmm)
&& !operand_type_equal (type, &regymm)
&& !operand_type_equal (type, &regzmm)
&& !operand_type_equal (type, &regmask))
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
<symbol>@GOTOFF+<nnn>
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
<symbol>@SECREL32+<nnn>
 
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,
&reg16_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 */
/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 */
/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"
/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 <alloca.h> 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 <unistd.h> 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 <dlfcn.h> header file. */
/* #undef HAVE_DLFCN_H */
 
/* Define to 1 if you have the <errno.h> header file. */
#define HAVE_ERRNO_H 1
 
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
 
/* Define if your <locale.h> file defines LC_MESSAGES. */
/* #undef HAVE_LC_MESSAGES */
 
/* Define to 1 if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1
 
/* Define to 1 if you have the <locale.h> header file. */
#define HAVE_LOCALE_H 1
 
/* Define to 1 if you have the <memory.h> 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 <stdint.h> header file. */
#define HAVE_STDINT_H 1
 
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
 
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
 
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
 
/* Define if <sys/stat.h> has struct stat.st_mtim.tv_nsec */
/* #undef HAVE_ST_MTIM_TV_NSEC */
 
/* Define if <sys/stat.h> has struct stat.st_mtim.tv_sec */
/* #undef HAVE_ST_MTIM_TV_SEC */
 
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
 
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
 
/* Define to 1 if you have the <time.h> header file. */
#define HAVE_TIME_H 1
 
/* Define if <time.h> has struct tm.tm_gmtoff. */
/* #undef HAVE_TM_GMTOFF */
 
/* Define to 1 if you have the <unistd.h> 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 <zlib.h> 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 <string.h> and <strings.h>. */
#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
/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);
}
/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 <mludvig@suse.cz>
 
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 */
/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 <mludvig@suse.cz>
 
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 */
/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 <davidm@hpl.hp.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. */
 
/* 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 <limits.h>
#else
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifndef INT_MAX
#define INT_MAX (int) (((unsigned) (-1)) >> 1)
#endif
#endif
 
#include "dwarf2dbg.h"
#include <filenames.h>
 
#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.<foo> 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);
}
}
/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 */
/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 <ian@cygnus.com>. A
good deal of it comes directly from mips-tfile.c, by Michael
Meissner <meissner@osf.org>.
 
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 <stdio.h>
 
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 <stab.h>
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 <stab.h>
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 */
/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 <ian@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 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 */
/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 <ian@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 "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;
}
/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 <limits.h>
#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;
}
/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 *);
/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 */
}
/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
/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;
}
/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)
/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;
}
/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 */
/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=<NUMBER> 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 <RETURN> 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 */
/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 */
/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;
}
/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);
/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;
}
}
/contrib/toolchain/binutils/gas/itbl-cpu.h
0,0 → 1,0
#include "itbl-i386.h"
/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 <sac@cygnus.com>
 
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_<foo>
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 <time.h>
#include <stdarg.h>
 
#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
/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 */
/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
/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<whitespace> -> return 'Bxyza
%<expr> -> return string of decimal value of <expr>
"string" -> return string
(string) -> return (string-including-whitespaces)
xyx<whitespace> -> 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 = &macro->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 (&macro->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 (&macro->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", &macro->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;
}
/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
/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);
}
/contrib/toolchain/binutils/gas/obj-format.h
0,0 → 1,0
#include "obj-coff.h"
/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 */
/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 ()));
}
/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 */
/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-<CPU>.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 (&notes, 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, &macro))
{
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 "<built-in>"
 
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 <nn>"));
 
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 (&notes, c);
len++;
}
/* JF this next line is so demand_copy_C_string will return a
null terminated string. */
obstack_1grow (&notes, '\0');
retval = (char *) obstack_finish (&notes);
}
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 (&notes, *input_line_pointer);
++input_line_pointer;
++i;
}
 
obstack_1grow (&notes, '\0');
filename = (char *) obstack_finish (&notes);
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);
}
/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);
/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);
}
/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 <limits.h>
#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;
}
/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 */
/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 .<secname>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 (&notes, 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;
}
/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__ */
/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 */
/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 *);
/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 (&notes, name, name_length);
ret = (char *) obstack_finish (&notes);
 
#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_<something>). */
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 (&notes, 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 (&notes, 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 "<name>:".
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 (&notes, 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 (&notes, 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
/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
/contrib/toolchain/binutils/gas/targ-cpu.h
0,0 → 1,0
#include "tc-i386.h"
/contrib/toolchain/binutils/gas/targ-env.h
0,0 → 1,0
#include "te-pe.h"
/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
/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 (&notes, 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
}
/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__ */