/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 (¤t_cframe->else_file_line.file, |
¤t_cframe->else_file_line.line); |
current_cframe->dead_tree |= !current_cframe->ignoring; |
current_cframe->ignoring = current_cframe->dead_tree; |
} |
if (current_cframe == NULL || current_cframe->ignoring) |
{ |
while (! is_end_of_line[(unsigned char) *input_line_pointer]) |
++input_line_pointer; |
if (current_cframe == NULL) |
return; |
} |
else |
{ |
expressionS operand; |
int t; |
/* Leading whitespace is part of operand. */ |
SKIP_WHITESPACE (); |
expression_and_evaluate (&operand); |
if (operand.X_op != O_constant) |
as_bad (_("non-constant expression in \".elseif\" statement")); |
switch ((operatorT) arg) |
{ |
case O_eq: t = operand.X_add_number == 0; break; |
case O_ne: t = operand.X_add_number != 0; break; |
case O_lt: t = operand.X_add_number < 0; break; |
case O_le: t = operand.X_add_number <= 0; break; |
case O_ge: t = operand.X_add_number >= 0; break; |
case O_gt: t = operand.X_add_number > 0; break; |
default: |
abort (); |
return; |
} |
current_cframe->ignoring = current_cframe->dead_tree || ! t; |
} |
if (LISTING_SKIP_COND () |
&& (current_cframe->previous_cframe == NULL |
|| ! current_cframe->previous_cframe->ignoring)) |
{ |
if (! current_cframe->ignoring) |
listing_list (1); |
else |
listing_list (2); |
} |
demand_empty_rest_of_line (); |
} |
void |
s_endif (int arg ATTRIBUTE_UNUSED) |
{ |
struct conditional_frame *hold; |
if (current_cframe == NULL) |
{ |
as_bad (_("\".endif\" without \".if\"")); |
} |
else |
{ |
if (LISTING_SKIP_COND () |
&& current_cframe->ignoring |
&& (current_cframe->previous_cframe == NULL |
|| ! current_cframe->previous_cframe->ignoring)) |
listing_list (1); |
hold = current_cframe; |
current_cframe = current_cframe->previous_cframe; |
obstack_free (&cond_obstack, hold); |
} /* if one pop too many */ |
if (flag_mri) |
{ |
while (! is_end_of_line[(unsigned char) *input_line_pointer]) |
++input_line_pointer; |
} |
demand_empty_rest_of_line (); |
} |
void |
s_else (int arg ATTRIBUTE_UNUSED) |
{ |
if (current_cframe == NULL) |
{ |
as_bad (_("\".else\" without matching \".if\"")); |
} |
else if (current_cframe->else_seen) |
{ |
as_bad (_("duplicate \".else\"")); |
as_bad_where (current_cframe->else_file_line.file, |
current_cframe->else_file_line.line, |
_("here is the previous \".else\"")); |
as_bad_where (current_cframe->if_file_line.file, |
current_cframe->if_file_line.line, |
_("here is the previous \".if\"")); |
} |
else |
{ |
as_where (¤t_cframe->else_file_line.file, |
¤t_cframe->else_file_line.line); |
current_cframe->ignoring = |
current_cframe->dead_tree | !current_cframe->ignoring; |
if (LISTING_SKIP_COND () |
&& (current_cframe->previous_cframe == NULL |
|| ! current_cframe->previous_cframe->ignoring)) |
{ |
if (! current_cframe->ignoring) |
listing_list (1); |
else |
listing_list (2); |
} |
current_cframe->else_seen = 1; |
} |
if (flag_mri) |
{ |
while (! is_end_of_line[(unsigned char) *input_line_pointer]) |
++input_line_pointer; |
} |
demand_empty_rest_of_line (); |
} |
void |
s_ifeqs (int arg) |
{ |
char *s1, *s2; |
int len1, len2; |
int res; |
struct conditional_frame cframe; |
s1 = demand_copy_C_string (&len1); |
SKIP_WHITESPACE (); |
if (*input_line_pointer != ',') |
{ |
as_bad (_(".ifeqs syntax error")); |
ignore_rest_of_line (); |
return; |
} |
++input_line_pointer; |
s2 = demand_copy_C_string (&len2); |
res = len1 == len2 && strncmp (s1, s2, len1) == 0; |
initialize_cframe (&cframe); |
cframe.ignoring = cframe.dead_tree || ! (res ^ arg); |
current_cframe = ((struct conditional_frame *) |
obstack_copy (&cond_obstack, &cframe, sizeof (cframe))); |
if (LISTING_SKIP_COND () |
&& cframe.ignoring |
&& (cframe.previous_cframe == NULL |
|| ! cframe.previous_cframe->ignoring)) |
listing_list (2); |
demand_empty_rest_of_line (); |
} |
int |
ignore_input (void) |
{ |
char *s; |
s = input_line_pointer; |
if (NO_PSEUDO_DOT || flag_m68k_mri) |
{ |
if (s[-1] != '.') |
--s; |
} |
else |
{ |
if (s[-1] != '.') |
return (current_cframe != NULL) && (current_cframe->ignoring); |
} |
/* We cannot ignore certain pseudo ops. */ |
if (((s[0] == 'i' |
|| s[0] == 'I') |
&& (!strncasecmp (s, "if", 2) |
|| !strncasecmp (s, "ifdef", 5) |
|| !strncasecmp (s, "ifndef", 6))) |
|| ((s[0] == 'e' |
|| s[0] == 'E') |
&& (!strncasecmp (s, "else", 4) |
|| !strncasecmp (s, "endif", 5) |
|| !strncasecmp (s, "endc", 4)))) |
return 0; |
return (current_cframe != NULL) && (current_cframe->ignoring); |
} |
static void |
initialize_cframe (struct conditional_frame *cframe) |
{ |
memset (cframe, 0, sizeof (*cframe)); |
as_where (&cframe->if_file_line.file, |
&cframe->if_file_line.line); |
cframe->previous_cframe = current_cframe; |
cframe->dead_tree = current_cframe != NULL && current_cframe->ignoring; |
cframe->macro_nest = macro_nest; |
} |
/* Give an error if a conditional is unterminated inside a macro or |
the assembly as a whole. If NEST is non negative, we are being |
called because of the end of a macro expansion. If NEST is |
negative, we are being called at the of the input files. */ |
void |
cond_finish_check (int nest) |
{ |
if (current_cframe != NULL && current_cframe->macro_nest >= nest) |
{ |
if (nest >= 0) |
as_bad (_("end of macro inside conditional")); |
else |
as_bad (_("end of file inside conditional")); |
as_bad_where (current_cframe->if_file_line.file, |
current_cframe->if_file_line.line, |
_("here is the start of the unterminated conditional")); |
if (current_cframe->else_seen) |
as_bad_where (current_cframe->else_file_line.file, |
current_cframe->else_file_line.line, |
_("here is the \"else\" of the unterminated conditional")); |
} |
} |
/* This function is called when we exit out of a macro. We assume |
that any conditionals which began within the macro are correctly |
nested, and just pop them off the stack. */ |
void |
cond_exit_macro (int nest) |
{ |
while (current_cframe != NULL && current_cframe->macro_nest >= nest) |
{ |
struct conditional_frame *hold; |
hold = current_cframe; |
current_cframe = current_cframe->previous_cframe; |
obstack_free (&cond_obstack, hold); |
} |
} |
/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], ®xmm)); |
if (i.tm.opcode_modifier.firstxmm0) |
{ |
/* The first operand is implicit and must be xmm0. */ |
gas_assert (operand_type_equal (&i.types[0], ®xmm)); |
if (register_number (i.op[0].regs) != 0) |
return bad_implicit_operand (1); |
if (i.tm.opcode_modifier.vexsources == VEX3SOURCES) |
{ |
/* Keep xmm0 for instructions with VEX prefix and 3 |
sources. */ |
goto duplicate; |
} |
else |
{ |
/* We remove the first xmm0 and keep the number of |
operands unchanged, which in fact duplicates the |
destination. */ |
for (j = 1; j < i.operands; j++) |
{ |
i.op[j - 1] = i.op[j]; |
i.types[j - 1] = i.types[j]; |
i.tm.operand_types[j - 1] = i.tm.operand_types[j]; |
} |
} |
} |
else if (i.tm.opcode_modifier.implicit1stxmm0) |
{ |
gas_assert ((MAX_OPERANDS - 1) > dupl |
&& (i.tm.opcode_modifier.vexsources |
== VEX3SOURCES)); |
/* Add the implicit xmm0 for instructions with VEX prefix |
and 3 sources. */ |
for (j = i.operands; j > 0; j--) |
{ |
i.op[j] = i.op[j - 1]; |
i.types[j] = i.types[j - 1]; |
i.tm.operand_types[j] = i.tm.operand_types[j - 1]; |
} |
i.op[0].regs |
= (const reg_entry *) hash_find (reg_hash, "xmm0"); |
i.types[0] = regxmm; |
i.tm.operand_types[0] = regxmm; |
i.operands += 2; |
i.reg_operands += 2; |
i.tm.operands += 2; |
dupl++; |
dest++; |
i.op[dupl] = i.op[dest]; |
i.types[dupl] = i.types[dest]; |
i.tm.operand_types[dupl] = i.tm.operand_types[dest]; |
} |
else |
{ |
duplicate: |
i.operands++; |
i.reg_operands++; |
i.tm.operands++; |
i.op[dupl] = i.op[dest]; |
i.types[dupl] = i.types[dest]; |
i.tm.operand_types[dupl] = i.tm.operand_types[dest]; |
} |
if (i.tm.opcode_modifier.immext) |
process_immext (); |
} |
else if (i.tm.opcode_modifier.firstxmm0) |
{ |
unsigned int j; |
/* The first operand is implicit and must be xmm0/ymm0/zmm0. */ |
gas_assert (i.reg_operands |
&& (operand_type_equal (&i.types[0], ®xmm) |
|| operand_type_equal (&i.types[0], ®ymm) |
|| operand_type_equal (&i.types[0], ®zmm))); |
if (register_number (i.op[0].regs) != 0) |
return bad_implicit_operand (i.types[0].bitfield.regxmm); |
for (j = 1; j < i.operands; j++) |
{ |
i.op[j - 1] = i.op[j]; |
i.types[j - 1] = i.types[j]; |
/* We need to adjust fields in i.tm since they are used by |
build_modrm_byte. */ |
i.tm.operand_types [j - 1] = i.tm.operand_types [j]; |
} |
i.operands--; |
i.reg_operands--; |
i.tm.operands--; |
} |
else if (i.tm.opcode_modifier.regkludge) |
{ |
/* The imul $imm, %reg instruction is converted into |
imul $imm, %reg, %reg, and the clr %reg instruction |
is converted into xor %reg, %reg. */ |
unsigned int first_reg_op; |
if (operand_type_check (i.types[0], reg)) |
first_reg_op = 0; |
else |
first_reg_op = 1; |
/* Pretend we saw the extra register operand. */ |
gas_assert (i.reg_operands == 1 |
&& i.op[first_reg_op + 1].regs == 0); |
i.op[first_reg_op + 1].regs = i.op[first_reg_op].regs; |
i.types[first_reg_op + 1] = i.types[first_reg_op]; |
i.operands++; |
i.reg_operands++; |
} |
if (i.tm.opcode_modifier.shortform) |
{ |
if (i.types[0].bitfield.sreg2 |
|| i.types[0].bitfield.sreg3) |
{ |
if (i.tm.base_opcode == POP_SEG_SHORT |
&& i.op[0].regs->reg_num == 1) |
{ |
as_bad (_("you can't `pop %scs'"), register_prefix); |
return 0; |
} |
i.tm.base_opcode |= (i.op[0].regs->reg_num << 3); |
if ((i.op[0].regs->reg_flags & RegRex) != 0) |
i.rex |= REX_B; |
} |
else |
{ |
/* The register or float register operand is in operand |
0 or 1. */ |
unsigned int op; |
if (i.types[0].bitfield.floatreg |
|| operand_type_check (i.types[0], reg)) |
op = 0; |
else |
op = 1; |
/* Register goes in low 3 bits of opcode. */ |
i.tm.base_opcode |= i.op[op].regs->reg_num; |
if ((i.op[op].regs->reg_flags & RegRex) != 0) |
i.rex |= REX_B; |
if (!quiet_warnings && i.tm.opcode_modifier.ugh) |
{ |
/* Warn about some common errors, but press on regardless. |
The first case can be generated by gcc (<= 2.8.1). */ |
if (i.operands == 2) |
{ |
/* Reversed arguments on faddp, fsubp, etc. */ |
as_warn (_("translating to `%s %s%s,%s%s'"), i.tm.name, |
register_prefix, i.op[!intel_syntax].regs->reg_name, |
register_prefix, i.op[intel_syntax].regs->reg_name); |
} |
else |
{ |
/* Extraneous `l' suffix on fp insn. */ |
as_warn (_("translating to `%s %s%s'"), i.tm.name, |
register_prefix, i.op[0].regs->reg_name); |
} |
} |
} |
} |
else if (i.tm.opcode_modifier.modrm) |
{ |
/* The opcode is completed (modulo i.tm.extension_opcode which |
must be put into the modrm byte). Now, we make the modrm and |
index base bytes based on all the info we've collected. */ |
default_seg = build_modrm_byte (); |
} |
else if ((i.tm.base_opcode & ~0x3) == MOV_AX_DISP32) |
{ |
default_seg = &ds; |
} |
else if (i.tm.opcode_modifier.isstring) |
{ |
/* For the string instructions that allow a segment override |
on one of their operands, the default segment is ds. */ |
default_seg = &ds; |
} |
if (i.tm.base_opcode == 0x8d /* lea */ |
&& i.seg[0] |
&& !quiet_warnings) |
as_warn (_("segment override on `%s' is ineffectual"), i.tm.name); |
/* If a segment was explicitly specified, and the specified segment |
is not the default, use an opcode prefix to select it. If we |
never figured out what the default segment is, then default_seg |
will be zero at this point, and the specified segment prefix will |
always be used. */ |
if ((i.seg[0]) && (i.seg[0] != default_seg)) |
{ |
if (!add_prefix (i.seg[0]->seg_prefix)) |
return 0; |
} |
return 1; |
} |
static const seg_entry * |
build_modrm_byte (void) |
{ |
const seg_entry *default_seg = 0; |
unsigned int source, dest; |
int vex_3_sources; |
/* The first operand of instructions with VEX prefix and 3 sources |
must be VEX_Imm4. */ |
vex_3_sources = i.tm.opcode_modifier.vexsources == VEX3SOURCES; |
if (vex_3_sources) |
{ |
unsigned int nds, reg_slot; |
expressionS *exp; |
if (i.tm.opcode_modifier.veximmext |
&& i.tm.opcode_modifier.immext) |
{ |
dest = i.operands - 2; |
gas_assert (dest == 3); |
} |
else |
dest = i.operands - 1; |
nds = dest - 1; |
/* There are 2 kinds of instructions: |
1. 5 operands: 4 register operands or 3 register operands |
plus 1 memory operand plus one Vec_Imm4 operand, VexXDS, and |
VexW0 or VexW1. The destination must be either XMM, YMM or |
ZMM register. |
2. 4 operands: 4 register operands or 3 register operands |
plus 1 memory operand, VexXDS, and VexImmExt */ |
gas_assert ((i.reg_operands == 4 |
|| (i.reg_operands == 3 && i.mem_operands == 1)) |
&& i.tm.opcode_modifier.vexvvvv == VEXXDS |
&& (i.tm.opcode_modifier.veximmext |
|| (i.imm_operands == 1 |
&& i.types[0].bitfield.vec_imm4 |
&& (i.tm.opcode_modifier.vexw == VEXW0 |
|| i.tm.opcode_modifier.vexw == VEXW1) |
&& (operand_type_equal (&i.tm.operand_types[dest], ®xmm) |
|| operand_type_equal (&i.tm.operand_types[dest], ®ymm) |
|| operand_type_equal (&i.tm.operand_types[dest], ®zmm))))); |
if (i.imm_operands == 0) |
{ |
/* When there is no immediate operand, generate an 8bit |
immediate operand to encode the first operand. */ |
exp = &im_expressions[i.imm_operands++]; |
i.op[i.operands].imms = exp; |
i.types[i.operands] = imm8; |
i.operands++; |
/* If VexW1 is set, the first operand is the source and |
the second operand is encoded in the immediate operand. */ |
if (i.tm.opcode_modifier.vexw == VEXW1) |
{ |
source = 0; |
reg_slot = 1; |
} |
else |
{ |
source = 1; |
reg_slot = 0; |
} |
/* FMA swaps REG and NDS. */ |
if (i.tm.cpu_flags.bitfield.cpufma) |
{ |
unsigned int tmp; |
tmp = reg_slot; |
reg_slot = nds; |
nds = tmp; |
} |
gas_assert (operand_type_equal (&i.tm.operand_types[reg_slot], |
®xmm) |
|| operand_type_equal (&i.tm.operand_types[reg_slot], |
®ymm) |
|| operand_type_equal (&i.tm.operand_types[reg_slot], |
®zmm)); |
exp->X_op = O_constant; |
exp->X_add_number = register_number (i.op[reg_slot].regs) << 4; |
gas_assert ((i.op[reg_slot].regs->reg_flags & RegVRex) == 0); |
} |
else |
{ |
unsigned int imm_slot; |
if (i.tm.opcode_modifier.vexw == VEXW0) |
{ |
/* If VexW0 is set, the third operand is the source and |
the second operand is encoded in the immediate |
operand. */ |
source = 2; |
reg_slot = 1; |
} |
else |
{ |
/* VexW1 is set, the second operand is the source and |
the third operand is encoded in the immediate |
operand. */ |
source = 1; |
reg_slot = 2; |
} |
if (i.tm.opcode_modifier.immext) |
{ |
/* When ImmExt is set, the immdiate byte is the last |
operand. */ |
imm_slot = i.operands - 1; |
source--; |
reg_slot--; |
} |
else |
{ |
imm_slot = 0; |
/* Turn on Imm8 so that output_imm will generate it. */ |
i.types[imm_slot].bitfield.imm8 = 1; |
} |
gas_assert (operand_type_equal (&i.tm.operand_types[reg_slot], |
®xmm) |
|| operand_type_equal (&i.tm.operand_types[reg_slot], |
®ymm) |
|| operand_type_equal (&i.tm.operand_types[reg_slot], |
®zmm)); |
i.op[imm_slot].imms->X_add_number |
|= register_number (i.op[reg_slot].regs) << 4; |
gas_assert ((i.op[reg_slot].regs->reg_flags & RegVRex) == 0); |
} |
gas_assert (operand_type_equal (&i.tm.operand_types[nds], ®xmm) |
|| operand_type_equal (&i.tm.operand_types[nds], |
®ymm) |
|| operand_type_equal (&i.tm.operand_types[nds], |
®zmm)); |
i.vex.register_specifier = i.op[nds].regs; |
} |
else |
source = dest = 0; |
/* i.reg_operands MUST be the number of real register operands; |
implicit registers do not count. If there are 3 register |
operands, it must be a instruction with VexNDS. For a |
instruction with VexNDD, the destination register is encoded |
in VEX prefix. If there are 4 register operands, it must be |
a instruction with VEX prefix and 3 sources. */ |
if (i.mem_operands == 0 |
&& ((i.reg_operands == 2 |
&& i.tm.opcode_modifier.vexvvvv <= VEXXDS) |
|| (i.reg_operands == 3 |
&& i.tm.opcode_modifier.vexvvvv == VEXXDS) |
|| (i.reg_operands == 4 && vex_3_sources))) |
{ |
switch (i.operands) |
{ |
case 2: |
source = 0; |
break; |
case 3: |
/* When there are 3 operands, one of them may be immediate, |
which may be the first or the last operand. Otherwise, |
the first operand must be shift count register (cl) or it |
is an instruction with VexNDS. */ |
gas_assert (i.imm_operands == 1 |
|| (i.imm_operands == 0 |
&& (i.tm.opcode_modifier.vexvvvv == VEXXDS |
|| i.types[0].bitfield.shiftcount))); |
if (operand_type_check (i.types[0], imm) |
|| i.types[0].bitfield.shiftcount) |
source = 1; |
else |
source = 0; |
break; |
case 4: |
/* When there are 4 operands, the first two must be 8bit |
immediate operands. The source operand will be the 3rd |
one. |
For instructions with VexNDS, if the first operand |
an imm8, the source operand is the 2nd one. If the last |
operand is imm8, the source operand is the first one. */ |
gas_assert ((i.imm_operands == 2 |
&& i.types[0].bitfield.imm8 |
&& i.types[1].bitfield.imm8) |
|| (i.tm.opcode_modifier.vexvvvv == VEXXDS |
&& i.imm_operands == 1 |
&& (i.types[0].bitfield.imm8 |
|| i.types[i.operands - 1].bitfield.imm8 |
|| i.rounding))); |
if (i.imm_operands == 2) |
source = 2; |
else |
{ |
if (i.types[0].bitfield.imm8) |
source = 1; |
else |
source = 0; |
} |
break; |
case 5: |
if (i.tm.opcode_modifier.evex) |
{ |
/* For EVEX instructions, when there are 5 operands, the |
first one must be immediate operand. If the second one |
is immediate operand, the source operand is the 3th |
one. If the last one is immediate operand, the source |
operand is the 2nd one. */ |
gas_assert (i.imm_operands == 2 |
&& i.tm.opcode_modifier.sae |
&& operand_type_check (i.types[0], imm)); |
if (operand_type_check (i.types[1], imm)) |
source = 2; |
else if (operand_type_check (i.types[4], imm)) |
source = 1; |
else |
abort (); |
} |
break; |
default: |
abort (); |
} |
if (!vex_3_sources) |
{ |
dest = source + 1; |
/* RC/SAE operand could be between DEST and SRC. That happens |
when one operand is GPR and the other one is XMM/YMM/ZMM |
register. */ |
if (i.rounding && i.rounding->operand == (int) dest) |
dest++; |
if (i.tm.opcode_modifier.vexvvvv == VEXXDS) |
{ |
/* For instructions with VexNDS, the register-only source |
operand must be 32/64bit integer, XMM, YMM or ZMM |
register. It is encoded in VEX prefix. We need to |
clear RegMem bit before calling operand_type_equal. */ |
i386_operand_type op; |
unsigned int vvvv; |
/* Check register-only source operand when two source |
operands are swapped. */ |
if (!i.tm.operand_types[source].bitfield.baseindex |
&& i.tm.operand_types[dest].bitfield.baseindex) |
{ |
vvvv = source; |
source = dest; |
} |
else |
vvvv = dest; |
op = i.tm.operand_types[vvvv]; |
op.bitfield.regmem = 0; |
if ((dest + 1) >= i.operands |
|| (op.bitfield.reg32 != 1 |
&& !op.bitfield.reg64 != 1 |
&& !operand_type_equal (&op, ®xmm) |
&& !operand_type_equal (&op, ®ymm) |
&& !operand_type_equal (&op, ®zmm) |
&& !operand_type_equal (&op, ®mask))) |
abort (); |
i.vex.register_specifier = i.op[vvvv].regs; |
dest++; |
} |
} |
i.rm.mode = 3; |
/* One of the register operands will be encoded in the i.tm.reg |
field, the other in the combined i.tm.mode and i.tm.regmem |
fields. If no form of this instruction supports a memory |
destination operand, then we assume the source operand may |
sometimes be a memory operand and so we need to store the |
destination in the i.rm.reg field. */ |
if (!i.tm.operand_types[dest].bitfield.regmem |
&& operand_type_check (i.tm.operand_types[dest], anymem) == 0) |
{ |
i.rm.reg = i.op[dest].regs->reg_num; |
i.rm.regmem = i.op[source].regs->reg_num; |
if ((i.op[dest].regs->reg_flags & RegRex) != 0) |
i.rex |= REX_R; |
if ((i.op[dest].regs->reg_flags & RegVRex) != 0) |
i.vrex |= REX_R; |
if ((i.op[source].regs->reg_flags & RegRex) != 0) |
i.rex |= REX_B; |
if ((i.op[source].regs->reg_flags & RegVRex) != 0) |
i.vrex |= REX_B; |
} |
else |
{ |
i.rm.reg = i.op[source].regs->reg_num; |
i.rm.regmem = i.op[dest].regs->reg_num; |
if ((i.op[dest].regs->reg_flags & RegRex) != 0) |
i.rex |= REX_B; |
if ((i.op[dest].regs->reg_flags & RegVRex) != 0) |
i.vrex |= REX_B; |
if ((i.op[source].regs->reg_flags & RegRex) != 0) |
i.rex |= REX_R; |
if ((i.op[source].regs->reg_flags & RegVRex) != 0) |
i.vrex |= REX_R; |
} |
if (flag_code != CODE_64BIT && (i.rex & (REX_R | REX_B))) |
{ |
if (!i.types[0].bitfield.control |
&& !i.types[1].bitfield.control) |
abort (); |
i.rex &= ~(REX_R | REX_B); |
add_prefix (LOCK_PREFIX_OPCODE); |
} |
} |
else |
{ /* If it's not 2 reg operands... */ |
unsigned int mem; |
if (i.mem_operands) |
{ |
unsigned int fake_zero_displacement = 0; |
unsigned int op; |
for (op = 0; op < i.operands; op++) |
if (operand_type_check (i.types[op], anymem)) |
break; |
gas_assert (op < i.operands); |
if (i.tm.opcode_modifier.vecsib) |
{ |
if (i.index_reg->reg_num == RegEiz |
|| i.index_reg->reg_num == RegRiz) |
abort (); |
i.rm.regmem = ESCAPE_TO_TWO_BYTE_ADDRESSING; |
if (!i.base_reg) |
{ |
i.sib.base = NO_BASE_REGISTER; |
i.sib.scale = i.log2_scale_factor; |
/* No Vec_Disp8 if there is no base. */ |
i.types[op].bitfield.vec_disp8 = 0; |
i.types[op].bitfield.disp8 = 0; |
i.types[op].bitfield.disp16 = 0; |
i.types[op].bitfield.disp64 = 0; |
if (flag_code != CODE_64BIT) |
{ |
/* Must be 32 bit */ |
i.types[op].bitfield.disp32 = 1; |
i.types[op].bitfield.disp32s = 0; |
} |
else |
{ |
i.types[op].bitfield.disp32 = 0; |
i.types[op].bitfield.disp32s = 1; |
} |
} |
i.sib.index = i.index_reg->reg_num; |
if ((i.index_reg->reg_flags & RegRex) != 0) |
i.rex |= REX_X; |
if ((i.index_reg->reg_flags & RegVRex) != 0) |
i.vrex |= REX_X; |
} |
default_seg = &ds; |
if (i.base_reg == 0) |
{ |
i.rm.mode = 0; |
if (!i.disp_operands) |
{ |
fake_zero_displacement = 1; |
/* Instructions with VSIB byte need 32bit displacement |
if there is no base register. */ |
if (i.tm.opcode_modifier.vecsib) |
i.types[op].bitfield.disp32 = 1; |
} |
if (i.index_reg == 0) |
{ |
gas_assert (!i.tm.opcode_modifier.vecsib); |
/* Operand is just <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, ®xmm) |
&& !operand_type_equal (type, ®ymm) |
&& !operand_type_equal (type, ®zmm) |
&& !operand_type_equal (type, ®mask)) |
abort (); |
i.vex.register_specifier = i.op[vex_reg].regs; |
} |
/* Don't set OP operand twice. */ |
if (vex_reg != op) |
{ |
/* If there is an extension opcode to put here, the |
register number must be put into the regmem field. */ |
if (i.tm.extension_opcode != None) |
{ |
i.rm.regmem = i.op[op].regs->reg_num; |
if ((i.op[op].regs->reg_flags & RegRex) != 0) |
i.rex |= REX_B; |
if ((i.op[op].regs->reg_flags & RegVRex) != 0) |
i.vrex |= REX_B; |
} |
else |
{ |
i.rm.reg = i.op[op].regs->reg_num; |
if ((i.op[op].regs->reg_flags & RegRex) != 0) |
i.rex |= REX_R; |
if ((i.op[op].regs->reg_flags & RegVRex) != 0) |
i.vrex |= REX_R; |
} |
} |
/* Now, if no memory operand has set i.rm.mode = 0, 1, 2 we |
must set it to 3 to indicate this is a register operand |
in the regmem field. */ |
if (!i.mem_operands) |
i.rm.mode = 3; |
} |
/* Fill in i.rm.reg field with extension opcode (if any). */ |
if (i.tm.extension_opcode != None) |
i.rm.reg = i.tm.extension_opcode; |
} |
return default_seg; |
} |
static void |
output_branch (void) |
{ |
char *p; |
int size; |
int code16; |
int prefix; |
relax_substateT subtype; |
symbolS *sym; |
offsetT off; |
code16 = flag_code == CODE_16BIT ? CODE16 : 0; |
size = i.disp_encoding == disp_encoding_32bit ? BIG : SMALL; |
prefix = 0; |
if (i.prefix[DATA_PREFIX] != 0) |
{ |
prefix = 1; |
i.prefixes -= 1; |
code16 ^= CODE16; |
} |
/* Pentium4 branch hints. */ |
if (i.prefix[SEG_PREFIX] == CS_PREFIX_OPCODE /* not taken */ |
|| i.prefix[SEG_PREFIX] == DS_PREFIX_OPCODE /* taken */) |
{ |
prefix++; |
i.prefixes--; |
} |
if (i.prefix[REX_PREFIX] != 0) |
{ |
prefix++; |
i.prefixes--; |
} |
/* BND prefixed jump. */ |
if (i.prefix[BND_PREFIX] != 0) |
{ |
FRAG_APPEND_1_CHAR (i.prefix[BND_PREFIX]); |
i.prefixes -= 1; |
} |
if (i.prefixes != 0 && !intel_syntax) |
as_warn (_("skipping prefixes on this instruction")); |
/* It's always a symbol; End frag & setup for relax. |
Make sure there is enough room in this frag for the largest |
instruction we may generate in md_convert_frag. This is 2 |
bytes for the opcode and room for the prefix and largest |
displacement. */ |
frag_grow (prefix + 2 + 4); |
/* Prefix and 1 opcode byte go in fr_fix. */ |
p = frag_more (prefix + 1); |
if (i.prefix[DATA_PREFIX] != 0) |
*p++ = DATA_PREFIX_OPCODE; |
if (i.prefix[SEG_PREFIX] == CS_PREFIX_OPCODE |
|| i.prefix[SEG_PREFIX] == DS_PREFIX_OPCODE) |
*p++ = i.prefix[SEG_PREFIX]; |
if (i.prefix[REX_PREFIX] != 0) |
*p++ = i.prefix[REX_PREFIX]; |
*p = i.tm.base_opcode; |
if ((unsigned char) *p == JUMP_PC_RELATIVE) |
subtype = ENCODE_RELAX_STATE (UNCOND_JUMP, size); |
else if (cpu_arch_flags.bitfield.cpui386) |
subtype = ENCODE_RELAX_STATE (COND_JUMP, size); |
else |
subtype = ENCODE_RELAX_STATE (COND_JUMP86, size); |
subtype |= code16; |
sym = i.op[0].disps->X_add_symbol; |
off = i.op[0].disps->X_add_number; |
if (i.op[0].disps->X_op != O_constant |
&& i.op[0].disps->X_op != O_symbol) |
{ |
/* Handle complex expressions. */ |
sym = make_expr_symbol (i.op[0].disps); |
off = 0; |
} |
/* 1 possible extra opcode + 4 byte displacement go in var part. |
Pass reloc in fr_var. */ |
frag_var (rs_machine_dependent, 5, |
((!object_64bit |
|| i.reloc[0] != NO_RELOC |
|| (i.bnd_prefix == NULL && !add_bnd_prefix)) |
? i.reloc[0] |
: BFD_RELOC_X86_64_PC32_BND), |
subtype, sym, off, p); |
} |
static void |
output_jump (void) |
{ |
char *p; |
int size; |
fixS *fixP; |
if (i.tm.opcode_modifier.jumpbyte) |
{ |
/* This is a loop or jecxz type instruction. */ |
size = 1; |
if (i.prefix[ADDR_PREFIX] != 0) |
{ |
FRAG_APPEND_1_CHAR (ADDR_PREFIX_OPCODE); |
i.prefixes -= 1; |
} |
/* Pentium4 branch hints. */ |
if (i.prefix[SEG_PREFIX] == CS_PREFIX_OPCODE /* not taken */ |
|| i.prefix[SEG_PREFIX] == DS_PREFIX_OPCODE /* taken */) |
{ |
FRAG_APPEND_1_CHAR (i.prefix[SEG_PREFIX]); |
i.prefixes--; |
} |
} |
else |
{ |
int code16; |
code16 = 0; |
if (flag_code == CODE_16BIT) |
code16 = CODE16; |
if (i.prefix[DATA_PREFIX] != 0) |
{ |
FRAG_APPEND_1_CHAR (DATA_PREFIX_OPCODE); |
i.prefixes -= 1; |
code16 ^= CODE16; |
} |
size = 4; |
if (code16) |
size = 2; |
} |
if (i.prefix[REX_PREFIX] != 0) |
{ |
FRAG_APPEND_1_CHAR (i.prefix[REX_PREFIX]); |
i.prefixes -= 1; |
} |
/* BND prefixed jump. */ |
if (i.prefix[BND_PREFIX] != 0) |
{ |
FRAG_APPEND_1_CHAR (i.prefix[BND_PREFIX]); |
i.prefixes -= 1; |
} |
if (i.prefixes != 0 && !intel_syntax) |
as_warn (_("skipping prefixes on this instruction")); |
p = frag_more (i.tm.opcode_length + size); |
switch (i.tm.opcode_length) |
{ |
case 2: |
*p++ = i.tm.base_opcode >> 8; |
case 1: |
*p++ = i.tm.base_opcode; |
break; |
default: |
abort (); |
} |
fixP = fix_new_exp (frag_now, p - frag_now->fr_literal, size, |
i.op[0].disps, 1, reloc (size, 1, 1, |
(i.bnd_prefix != NULL |
|| add_bnd_prefix), |
i.reloc[0])); |
/* All jumps handled here are signed, but don't use a signed limit |
check for 32 and 16 bit jumps as we want to allow wrap around at |
4G and 64k respectively. */ |
if (size == 1) |
fixP->fx_signed = 1; |
} |
static void |
output_interseg_jump (void) |
{ |
char *p; |
int size; |
int prefix; |
int code16; |
code16 = 0; |
if (flag_code == CODE_16BIT) |
code16 = CODE16; |
prefix = 0; |
if (i.prefix[DATA_PREFIX] != 0) |
{ |
prefix = 1; |
i.prefixes -= 1; |
code16 ^= CODE16; |
} |
if (i.prefix[REX_PREFIX] != 0) |
{ |
prefix++; |
i.prefixes -= 1; |
} |
size = 4; |
if (code16) |
size = 2; |
if (i.prefixes != 0 && !intel_syntax) |
as_warn (_("skipping prefixes on this instruction")); |
/* 1 opcode; 2 segment; offset */ |
p = frag_more (prefix + 1 + 2 + size); |
if (i.prefix[DATA_PREFIX] != 0) |
*p++ = DATA_PREFIX_OPCODE; |
if (i.prefix[REX_PREFIX] != 0) |
*p++ = i.prefix[REX_PREFIX]; |
*p++ = i.tm.base_opcode; |
if (i.op[1].imms->X_op == O_constant) |
{ |
offsetT n = i.op[1].imms->X_add_number; |
if (size == 2 |
&& !fits_in_unsigned_word (n) |
&& !fits_in_signed_word (n)) |
{ |
as_bad (_("16-bit jump out of range")); |
return; |
} |
md_number_to_chars (p, n, size); |
} |
else |
fix_new_exp (frag_now, p - frag_now->fr_literal, size, |
i.op[1].imms, 0, reloc (size, 0, 0, 0, i.reloc[1])); |
if (i.op[0].imms->X_op != O_constant) |
as_bad (_("can't handle non absolute segment in `%s'"), |
i.tm.name); |
md_number_to_chars (p + size, (valueT) i.op[0].imms->X_add_number, 2); |
} |
static void |
output_insn (void) |
{ |
fragS *insn_start_frag; |
offsetT insn_start_off; |
/* Tie dwarf2 debug info to the address at the start of the insn. |
We can't do this after the insn has been output as the current |
frag may have been closed off. eg. by frag_var. */ |
dwarf2_emit_insn (0); |
insn_start_frag = frag_now; |
insn_start_off = frag_now_fix (); |
/* Output jumps. */ |
if (i.tm.opcode_modifier.jump) |
output_branch (); |
else if (i.tm.opcode_modifier.jumpbyte |
|| i.tm.opcode_modifier.jumpdword) |
output_jump (); |
else if (i.tm.opcode_modifier.jumpintersegment) |
output_interseg_jump (); |
else |
{ |
/* Output normal instructions here. */ |
char *p; |
unsigned char *q; |
unsigned int j; |
unsigned int prefix; |
/* Since the VEX/EVEX prefix contains the implicit prefix, we |
don't need the explicit prefix. */ |
if (!i.tm.opcode_modifier.vex && !i.tm.opcode_modifier.evex) |
{ |
switch (i.tm.opcode_length) |
{ |
case 3: |
if (i.tm.base_opcode & 0xff000000) |
{ |
prefix = (i.tm.base_opcode >> 24) & 0xff; |
goto check_prefix; |
} |
break; |
case 2: |
if ((i.tm.base_opcode & 0xff0000) != 0) |
{ |
prefix = (i.tm.base_opcode >> 16) & 0xff; |
if (i.tm.cpu_flags.bitfield.cpupadlock) |
{ |
check_prefix: |
if (prefix != REPE_PREFIX_OPCODE |
|| (i.prefix[REP_PREFIX] |
!= REPE_PREFIX_OPCODE)) |
add_prefix (prefix); |
} |
else |
add_prefix (prefix); |
} |
break; |
case 1: |
break; |
default: |
abort (); |
} |
/* The prefix bytes. */ |
for (j = ARRAY_SIZE (i.prefix), q = i.prefix; j > 0; j--, q++) |
if (*q) |
FRAG_APPEND_1_CHAR (*q); |
} |
else |
{ |
for (j = 0, q = i.prefix; j < ARRAY_SIZE (i.prefix); j++, q++) |
if (*q) |
switch (j) |
{ |
case REX_PREFIX: |
/* REX byte is encoded in VEX prefix. */ |
break; |
case SEG_PREFIX: |
case ADDR_PREFIX: |
FRAG_APPEND_1_CHAR (*q); |
break; |
default: |
/* There should be no other prefixes for instructions |
with VEX prefix. */ |
abort (); |
} |
/* For EVEX instructions i.vrex should become 0 after |
build_evex_prefix. For VEX instructions upper 16 registers |
aren't available, so VREX should be 0. */ |
if (i.vrex) |
abort (); |
/* Now the VEX prefix. */ |
p = frag_more (i.vex.length); |
for (j = 0; j < i.vex.length; j++) |
p[j] = i.vex.bytes[j]; |
} |
/* Now the opcode; be careful about word order here! */ |
if (i.tm.opcode_length == 1) |
{ |
FRAG_APPEND_1_CHAR (i.tm.base_opcode); |
} |
else |
{ |
switch (i.tm.opcode_length) |
{ |
case 4: |
p = frag_more (4); |
*p++ = (i.tm.base_opcode >> 24) & 0xff; |
*p++ = (i.tm.base_opcode >> 16) & 0xff; |
break; |
case 3: |
p = frag_more (3); |
*p++ = (i.tm.base_opcode >> 16) & 0xff; |
break; |
case 2: |
p = frag_more (2); |
break; |
default: |
abort (); |
break; |
} |
/* Put out high byte first: can't use md_number_to_chars! */ |
*p++ = (i.tm.base_opcode >> 8) & 0xff; |
*p = i.tm.base_opcode & 0xff; |
} |
/* Now the modrm byte and sib byte (if present). */ |
if (i.tm.opcode_modifier.modrm) |
{ |
FRAG_APPEND_1_CHAR ((i.rm.regmem << 0 |
| i.rm.reg << 3 |
| i.rm.mode << 6)); |
/* If i.rm.regmem == ESP (4) |
&& i.rm.mode != (Register mode) |
&& not 16 bit |
==> need second modrm byte. */ |
if (i.rm.regmem == ESCAPE_TO_TWO_BYTE_ADDRESSING |
&& i.rm.mode != 3 |
&& !(i.base_reg && i.base_reg->reg_type.bitfield.reg16)) |
FRAG_APPEND_1_CHAR ((i.sib.base << 0 |
| i.sib.index << 3 |
| i.sib.scale << 6)); |
} |
if (i.disp_operands) |
output_disp (insn_start_frag, insn_start_off); |
if (i.imm_operands) |
output_imm (insn_start_frag, insn_start_off); |
} |
#ifdef DEBUG386 |
if (flag_debug) |
{ |
pi ("" /*line*/, &i); |
} |
#endif /* DEBUG386 */ |
} |
/* Return the size of the displacement operand N. */ |
static int |
disp_size (unsigned int n) |
{ |
int size = 4; |
/* Vec_Disp8 has to be 8bit. */ |
if (i.types[n].bitfield.vec_disp8) |
size = 1; |
else if (i.types[n].bitfield.disp64) |
size = 8; |
else if (i.types[n].bitfield.disp8) |
size = 1; |
else if (i.types[n].bitfield.disp16) |
size = 2; |
return size; |
} |
/* Return the size of the immediate operand N. */ |
static int |
imm_size (unsigned int n) |
{ |
int size = 4; |
if (i.types[n].bitfield.imm64) |
size = 8; |
else if (i.types[n].bitfield.imm8 || i.types[n].bitfield.imm8s) |
size = 1; |
else if (i.types[n].bitfield.imm16) |
size = 2; |
return size; |
} |
static void |
output_disp (fragS *insn_start_frag, offsetT insn_start_off) |
{ |
char *p; |
unsigned int n; |
for (n = 0; n < i.operands; n++) |
{ |
if (i.types[n].bitfield.vec_disp8 |
|| operand_type_check (i.types[n], disp)) |
{ |
if (i.op[n].disps->X_op == O_constant) |
{ |
int size = disp_size (n); |
offsetT val = i.op[n].disps->X_add_number; |
if (i.types[n].bitfield.vec_disp8) |
val >>= i.memshift; |
val = offset_in_range (val, size); |
p = frag_more (size); |
md_number_to_chars (p, val, size); |
} |
else |
{ |
enum bfd_reloc_code_real reloc_type; |
int size = disp_size (n); |
int sign = i.types[n].bitfield.disp32s; |
int pcrel = (i.flags[n] & Operand_PCrel) != 0; |
/* We can't have 8 bit displacement here. */ |
gas_assert (!i.types[n].bitfield.disp8); |
/* The PC relative address is computed relative |
to the instruction boundary, so in case immediate |
fields follows, we need to adjust the value. */ |
if (pcrel && i.imm_operands) |
{ |
unsigned int n1; |
int sz = 0; |
for (n1 = 0; n1 < i.operands; n1++) |
if (operand_type_check (i.types[n1], imm)) |
{ |
/* Only one immediate is allowed for PC |
relative address. */ |
gas_assert (sz == 0); |
sz = imm_size (n1); |
i.op[n].disps->X_add_number -= sz; |
} |
/* We should find the immediate. */ |
gas_assert (sz != 0); |
} |
p = frag_more (size); |
reloc_type = reloc (size, pcrel, sign, |
(i.bnd_prefix != NULL |
|| add_bnd_prefix), |
i.reloc[n]); |
if (GOT_symbol |
&& GOT_symbol == i.op[n].disps->X_add_symbol |
&& (((reloc_type == BFD_RELOC_32 |
|| reloc_type == BFD_RELOC_X86_64_32S |
|| (reloc_type == BFD_RELOC_64 |
&& object_64bit)) |
&& (i.op[n].disps->X_op == O_symbol |
|| (i.op[n].disps->X_op == O_add |
&& ((symbol_get_value_expression |
(i.op[n].disps->X_op_symbol)->X_op) |
== O_subtract)))) |
|| reloc_type == BFD_RELOC_32_PCREL)) |
{ |
offsetT add; |
if (insn_start_frag == frag_now) |
add = (p - frag_now->fr_literal) - insn_start_off; |
else |
{ |
fragS *fr; |
add = insn_start_frag->fr_fix - insn_start_off; |
for (fr = insn_start_frag->fr_next; |
fr && fr != frag_now; fr = fr->fr_next) |
add += fr->fr_fix; |
add += p - frag_now->fr_literal; |
} |
if (!object_64bit) |
{ |
reloc_type = BFD_RELOC_386_GOTPC; |
i.op[n].imms->X_add_number += add; |
} |
else if (reloc_type == BFD_RELOC_64) |
reloc_type = BFD_RELOC_X86_64_GOTPC64; |
else |
/* Don't do the adjustment for x86-64, as there |
the pcrel addressing is relative to the _next_ |
insn, and that is taken care of in other code. */ |
reloc_type = BFD_RELOC_X86_64_GOTPC32; |
} |
fix_new_exp (frag_now, p - frag_now->fr_literal, size, |
i.op[n].disps, pcrel, reloc_type); |
} |
} |
} |
} |
static void |
output_imm (fragS *insn_start_frag, offsetT insn_start_off) |
{ |
char *p; |
unsigned int n; |
for (n = 0; n < i.operands; n++) |
{ |
/* Skip SAE/RC Imm operand in EVEX. They are already handled. */ |
if (i.rounding && (int) n == i.rounding->operand) |
continue; |
if (operand_type_check (i.types[n], imm)) |
{ |
if (i.op[n].imms->X_op == O_constant) |
{ |
int size = imm_size (n); |
offsetT val; |
val = offset_in_range (i.op[n].imms->X_add_number, |
size); |
p = frag_more (size); |
md_number_to_chars (p, val, size); |
} |
else |
{ |
/* Not absolute_section. |
Need a 32-bit fixup (don't support 8bit |
non-absolute imms). Try to support other |
sizes ... */ |
enum bfd_reloc_code_real reloc_type; |
int size = imm_size (n); |
int sign; |
if (i.types[n].bitfield.imm32s |
&& (i.suffix == QWORD_MNEM_SUFFIX |
|| (!i.suffix && i.tm.opcode_modifier.no_lsuf))) |
sign = 1; |
else |
sign = 0; |
p = frag_more (size); |
reloc_type = reloc (size, 0, sign, 0, i.reloc[n]); |
/* This is tough to explain. We end up with this one if we |
* have operands that look like |
* "_GLOBAL_OFFSET_TABLE_+[.-.L284]". The goal here is to |
* obtain the absolute address of the GOT, and it is strongly |
* preferable from a performance point of view to avoid using |
* a runtime relocation for this. The actual sequence of |
* instructions often look something like: |
* |
* call .L66 |
* .L66: |
* popl %ebx |
* addl $_GLOBAL_OFFSET_TABLE_+[.-.L66],%ebx |
* |
* The call and pop essentially return the absolute address |
* of the label .L66 and store it in %ebx. The linker itself |
* will ultimately change the first operand of the addl so |
* that %ebx points to the GOT, but to keep things simple, the |
* .o file must have this operand set so that it generates not |
* the absolute address of .L66, but the absolute address of |
* itself. This allows the linker itself simply treat a GOTPC |
* relocation as asking for a pcrel offset to the GOT to be |
* added in, and the addend of the relocation is stored in the |
* operand field for the instruction itself. |
* |
* Our job here is to fix the operand so that it would add |
* the correct offset so that %ebx would point to itself. The |
* thing that is tricky is that .-.L66 will point to the |
* beginning of the instruction, so we need to further modify |
* the operand so that it will point to itself. There are |
* other cases where you have something like: |
* |
* .long $_GLOBAL_OFFSET_TABLE_+[.-.L66] |
* |
* and here no correction would be required. Internally in |
* the assembler we treat operands of this form as not being |
* pcrel since the '.' is explicitly mentioned, and I wonder |
* whether it would simplify matters to do it this way. Who |
* knows. In earlier versions of the PIC patches, the |
* pcrel_adjust field was used to store the correction, but |
* since the expression is not pcrel, I felt it would be |
* confusing to do it this way. */ |
if ((reloc_type == BFD_RELOC_32 |
|| reloc_type == BFD_RELOC_X86_64_32S |
|| reloc_type == BFD_RELOC_64) |
&& GOT_symbol |
&& GOT_symbol == i.op[n].imms->X_add_symbol |
&& (i.op[n].imms->X_op == O_symbol |
|| (i.op[n].imms->X_op == O_add |
&& ((symbol_get_value_expression |
(i.op[n].imms->X_op_symbol)->X_op) |
== O_subtract)))) |
{ |
offsetT add; |
if (insn_start_frag == frag_now) |
add = (p - frag_now->fr_literal) - insn_start_off; |
else |
{ |
fragS *fr; |
add = insn_start_frag->fr_fix - insn_start_off; |
for (fr = insn_start_frag->fr_next; |
fr && fr != frag_now; fr = fr->fr_next) |
add += fr->fr_fix; |
add += p - frag_now->fr_literal; |
} |
if (!object_64bit) |
reloc_type = BFD_RELOC_386_GOTPC; |
else if (size == 4) |
reloc_type = BFD_RELOC_X86_64_GOTPC32; |
else if (size == 8) |
reloc_type = BFD_RELOC_X86_64_GOTPC64; |
i.op[n].imms->X_add_number += add; |
} |
fix_new_exp (frag_now, p - frag_now->fr_literal, size, |
i.op[n].imms, 0, reloc_type); |
} |
} |
} |
} |
/* x86_cons_fix_new is called via the expression parsing code when a |
reloc is needed. We use this hook to get the correct .got reloc. */ |
static enum bfd_reloc_code_real got_reloc = NO_RELOC; |
static int cons_sign = -1; |
void |
x86_cons_fix_new (fragS *frag, unsigned int off, unsigned int len, |
expressionS *exp) |
{ |
enum bfd_reloc_code_real r = reloc (len, 0, cons_sign, 0, got_reloc); |
got_reloc = NO_RELOC; |
#ifdef TE_PE |
if (exp->X_op == O_secrel) |
{ |
exp->X_op = O_symbol; |
r = BFD_RELOC_32_SECREL; |
} |
#endif |
fix_new_exp (frag, off, len, exp, 0, r); |
} |
/* Export the ABI address size for use by TC_ADDRESS_BYTES for the |
purpose of the `.dc.a' internal pseudo-op. */ |
int |
x86_address_bytes (void) |
{ |
if ((stdoutput->arch_info->mach & bfd_mach_x64_32)) |
return 4; |
return stdoutput->arch_info->bits_per_address / 8; |
} |
#if !(defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) || defined (OBJ_MACH_O)) \ |
|| defined (LEX_AT) |
# define lex_got(reloc, adjust, types, bnd_prefix) NULL |
#else |
/* Parse operands of the form |
<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, |
®16_inoutportreg) |
&& i.index_reg == 0 |
&& i.log2_scale_factor == 0 |
&& i.seg[i.mem_operands] == 0 |
&& !operand_type_check (i.types[this_operand], disp)) |
{ |
i.types[this_operand] = inoutportreg; |
return 1; |
} |
if (i386_index_check (operand_string) == 0) |
return 0; |
i.types[this_operand].bitfield.mem = 1; |
i.mem_operands++; |
} |
else |
{ |
/* It's not a memory operand; argh! */ |
as_bad (_("invalid char %s beginning operand %d `%s'"), |
output_invalid (*op_string), |
this_operand + 1, |
op_string); |
return 0; |
} |
return 1; /* Normal return. */ |
} |
/* Calculate the maximum variable size (i.e., excluding fr_fix) |
that an rs_machine_dependent frag may reach. */ |
unsigned int |
i386_frag_max_var (fragS *frag) |
{ |
/* The only relaxable frags are for jumps. |
Unconditional jumps can grow by 4 bytes and others by 5 bytes. */ |
gas_assert (frag->fr_type == rs_machine_dependent); |
return TYPE_FROM_RELAX_STATE (frag->fr_subtype) == UNCOND_JUMP ? 4 : 5; |
} |
/* md_estimate_size_before_relax() |
Called just before relax() for rs_machine_dependent frags. The x86 |
assembler uses these frags to handle variable size jump |
instructions. |
Any symbol that is now undefined will not become defined. |
Return the correct fr_subtype in the frag. |
Return the initial "guess for variable size of frag" to caller. |
The guess is actually the growth beyond the fixed part. Whatever |
we do to grow the fixed or variable part contributes to our |
returned value. */ |
int |
md_estimate_size_before_relax (fragS *fragP, segT segment) |
{ |
/* We've already got fragP->fr_subtype right; all we have to do is |
check for un-relaxable symbols. On an ELF system, we can't relax |
an externally visible symbol, because it may be overridden by a |
shared library. */ |
if (S_GET_SEGMENT (fragP->fr_symbol) != segment |
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
|| (IS_ELF |
&& (S_IS_EXTERNAL (fragP->fr_symbol) |
|| S_IS_WEAK (fragP->fr_symbol) |
|| ((symbol_get_bfdsym (fragP->fr_symbol)->flags |
& BSF_GNU_INDIRECT_FUNCTION)))) |
#endif |
#if defined (OBJ_COFF) && defined (TE_PE) |
|| (OUTPUT_FLAVOR == bfd_target_coff_flavour |
&& S_IS_WEAK (fragP->fr_symbol)) |
#endif |
) |
{ |
/* Symbol is undefined in this segment, or we need to keep a |
reloc so that weak symbols can be overridden. */ |
int size = (fragP->fr_subtype & CODE16) ? 2 : 4; |
enum bfd_reloc_code_real reloc_type; |
unsigned char *opcode; |
int old_fr_fix; |
if (fragP->fr_var != NO_RELOC) |
reloc_type = (enum bfd_reloc_code_real) fragP->fr_var; |
else if (size == 2) |
reloc_type = BFD_RELOC_16_PCREL; |
else |
reloc_type = BFD_RELOC_32_PCREL; |
old_fr_fix = fragP->fr_fix; |
opcode = (unsigned char *) fragP->fr_opcode; |
switch (TYPE_FROM_RELAX_STATE (fragP->fr_subtype)) |
{ |
case UNCOND_JUMP: |
/* Make jmp (0xeb) a (d)word displacement jump. */ |
opcode[0] = 0xe9; |
fragP->fr_fix += size; |
fix_new (fragP, old_fr_fix, size, |
fragP->fr_symbol, |
fragP->fr_offset, 1, |
reloc_type); |
break; |
case COND_JUMP86: |
if (size == 2 |
&& (!no_cond_jump_promotion || fragP->fr_var != NO_RELOC)) |
{ |
/* Negate the condition, and branch past an |
unconditional jump. */ |
opcode[0] ^= 1; |
opcode[1] = 3; |
/* Insert an unconditional jump. */ |
opcode[2] = 0xe9; |
/* We added two extra opcode bytes, and have a two byte |
offset. */ |
fragP->fr_fix += 2 + 2; |
fix_new (fragP, old_fr_fix + 2, 2, |
fragP->fr_symbol, |
fragP->fr_offset, 1, |
reloc_type); |
break; |
} |
/* Fall through. */ |
case COND_JUMP: |
if (no_cond_jump_promotion && fragP->fr_var == NO_RELOC) |
{ |
fixS *fixP; |
fragP->fr_fix += 1; |
fixP = fix_new (fragP, old_fr_fix, 1, |
fragP->fr_symbol, |
fragP->fr_offset, 1, |
BFD_RELOC_8_PCREL); |
fixP->fx_signed = 1; |
break; |
} |
/* This changes the byte-displacement jump 0x7N |
to the (d)word-displacement jump 0x0f,0x8N. */ |
opcode[1] = opcode[0] + 0x10; |
opcode[0] = TWO_BYTE_OPCODE_ESCAPE; |
/* We've added an opcode byte. */ |
fragP->fr_fix += 1 + size; |
fix_new (fragP, old_fr_fix + 1, size, |
fragP->fr_symbol, |
fragP->fr_offset, 1, |
reloc_type); |
break; |
default: |
BAD_CASE (fragP->fr_subtype); |
break; |
} |
frag_wane (fragP); |
return fragP->fr_fix - old_fr_fix; |
} |
/* Guess size depending on current relax state. Initially the relax |
state will correspond to a short jump and we return 1, because |
the variable part of the frag (the branch offset) is one byte |
long. However, we can relax a section more than once and in that |
case we must either set fr_subtype back to the unrelaxed state, |
or return the value for the appropriate branch. */ |
return md_relax_table[fragP->fr_subtype].rlx_length; |
} |
/* Called after relax() is finished. |
In: Address of frag. |
fr_type == rs_machine_dependent. |
fr_subtype is what the address relaxed to. |
Out: Any fixSs and constants are set up. |
Caller will turn frag into a ".space 0". */ |
void |
md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec ATTRIBUTE_UNUSED, |
fragS *fragP) |
{ |
unsigned char *opcode; |
unsigned char *where_to_put_displacement = NULL; |
offsetT target_address; |
offsetT opcode_address; |
unsigned int extension = 0; |
offsetT displacement_from_opcode_start; |
opcode = (unsigned char *) fragP->fr_opcode; |
/* Address we want to reach in file space. */ |
target_address = S_GET_VALUE (fragP->fr_symbol) + fragP->fr_offset; |
/* Address opcode resides at in file space. */ |
opcode_address = fragP->fr_address + fragP->fr_fix; |
/* Displacement from opcode start to fill into instruction. */ |
displacement_from_opcode_start = target_address - opcode_address; |
if ((fragP->fr_subtype & BIG) == 0) |
{ |
/* Don't have to change opcode. */ |
extension = 1; /* 1 opcode + 1 displacement */ |
where_to_put_displacement = &opcode[1]; |
} |
else |
{ |
if (no_cond_jump_promotion |
&& TYPE_FROM_RELAX_STATE (fragP->fr_subtype) != UNCOND_JUMP) |
as_warn_where (fragP->fr_file, fragP->fr_line, |
_("long jump required")); |
switch (fragP->fr_subtype) |
{ |
case ENCODE_RELAX_STATE (UNCOND_JUMP, BIG): |
extension = 4; /* 1 opcode + 4 displacement */ |
opcode[0] = 0xe9; |
where_to_put_displacement = &opcode[1]; |
break; |
case ENCODE_RELAX_STATE (UNCOND_JUMP, BIG16): |
extension = 2; /* 1 opcode + 2 displacement */ |
opcode[0] = 0xe9; |
where_to_put_displacement = &opcode[1]; |
break; |
case ENCODE_RELAX_STATE (COND_JUMP, BIG): |
case ENCODE_RELAX_STATE (COND_JUMP86, BIG): |
extension = 5; /* 2 opcode + 4 displacement */ |
opcode[1] = opcode[0] + 0x10; |
opcode[0] = TWO_BYTE_OPCODE_ESCAPE; |
where_to_put_displacement = &opcode[2]; |
break; |
case ENCODE_RELAX_STATE (COND_JUMP, BIG16): |
extension = 3; /* 2 opcode + 2 displacement */ |
opcode[1] = opcode[0] + 0x10; |
opcode[0] = TWO_BYTE_OPCODE_ESCAPE; |
where_to_put_displacement = &opcode[2]; |
break; |
case ENCODE_RELAX_STATE (COND_JUMP86, BIG16): |
extension = 4; |
opcode[0] ^= 1; |
opcode[1] = 3; |
opcode[2] = 0xe9; |
where_to_put_displacement = &opcode[3]; |
break; |
default: |
BAD_CASE (fragP->fr_subtype); |
break; |
} |
} |
/* If size if less then four we are sure that the operand fits, |
but if it's 4, then it could be that the displacement is larger |
then -/+ 2GB. */ |
if (DISP_SIZE_FROM_RELAX_STATE (fragP->fr_subtype) == 4 |
&& object_64bit |
&& ((addressT) (displacement_from_opcode_start - extension |
+ ((addressT) 1 << 31)) |
> (((addressT) 2 << 31) - 1))) |
{ |
as_bad_where (fragP->fr_file, fragP->fr_line, |
_("jump target out of range")); |
/* Make us emit 0. */ |
displacement_from_opcode_start = extension; |
} |
/* Now put displacement after opcode. */ |
md_number_to_chars ((char *) where_to_put_displacement, |
(valueT) (displacement_from_opcode_start - extension), |
DISP_SIZE_FROM_RELAX_STATE (fragP->fr_subtype)); |
fragP->fr_fix += extension; |
} |
/* Apply a fixup (fixP) to segment data, once it has been determined |
by our caller that we have all the info we need to fix it up. |
Parameter valP is the pointer to the value of the bits. |
On the 386, immediates, displacements, and data pointers are all in |
the same (little-endian) format, so we don't need to care about which |
we are handling. */ |
void |
md_apply_fix (fixS *fixP, valueT *valP, segT seg ATTRIBUTE_UNUSED) |
{ |
char *p = fixP->fx_where + fixP->fx_frag->fr_literal; |
valueT value = *valP; |
#if !defined (TE_Mach) |
if (fixP->fx_pcrel) |
{ |
switch (fixP->fx_r_type) |
{ |
default: |
break; |
case BFD_RELOC_64: |
fixP->fx_r_type = BFD_RELOC_64_PCREL; |
break; |
case BFD_RELOC_32: |
case BFD_RELOC_X86_64_32S: |
fixP->fx_r_type = BFD_RELOC_32_PCREL; |
break; |
case BFD_RELOC_16: |
fixP->fx_r_type = BFD_RELOC_16_PCREL; |
break; |
case BFD_RELOC_8: |
fixP->fx_r_type = BFD_RELOC_8_PCREL; |
break; |
} |
} |
if (fixP->fx_addsy != NULL |
&& (fixP->fx_r_type == BFD_RELOC_32_PCREL |
|| fixP->fx_r_type == BFD_RELOC_64_PCREL |
|| fixP->fx_r_type == BFD_RELOC_16_PCREL |
|| fixP->fx_r_type == BFD_RELOC_8_PCREL |
|| fixP->fx_r_type == BFD_RELOC_X86_64_PC32_BND) |
&& !use_rela_relocations) |
{ |
/* This is a hack. There should be a better way to handle this. |
This covers for the fact that bfd_install_relocation will |
subtract the current location (for partial_inplace, PC relative |
relocations); see more below. */ |
#ifndef OBJ_AOUT |
if (IS_ELF |
#ifdef TE_PE |
|| OUTPUT_FLAVOR == bfd_target_coff_flavour |
#endif |
) |
value += fixP->fx_where + fixP->fx_frag->fr_address; |
#endif |
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
if (IS_ELF) |
{ |
segT sym_seg = S_GET_SEGMENT (fixP->fx_addsy); |
if ((sym_seg == seg |
|| (symbol_section_p (fixP->fx_addsy) |
&& sym_seg != absolute_section)) |
&& !generic_force_reloc (fixP)) |
{ |
/* Yes, we add the values in twice. This is because |
bfd_install_relocation subtracts them out again. I think |
bfd_install_relocation is broken, but I don't dare change |
it. FIXME. */ |
value += fixP->fx_where + fixP->fx_frag->fr_address; |
} |
} |
#endif |
#if defined (OBJ_COFF) && defined (TE_PE) |
/* For some reason, the PE format does not store a |
section address offset for a PC relative symbol. */ |
if (S_GET_SEGMENT (fixP->fx_addsy) != seg |
|| S_IS_WEAK (fixP->fx_addsy)) |
value += md_pcrel_from (fixP); |
#endif |
} |
#if defined (OBJ_COFF) && defined (TE_PE) |
if (fixP->fx_addsy != NULL && S_IS_WEAK (fixP->fx_addsy)) |
{ |
value -= S_GET_VALUE (fixP->fx_addsy); |
} |
#endif |
/* Fix a few things - the dynamic linker expects certain values here, |
and we must not disappoint it. */ |
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
if (IS_ELF && fixP->fx_addsy) |
switch (fixP->fx_r_type) |
{ |
case BFD_RELOC_386_PLT32: |
case BFD_RELOC_X86_64_PLT32: |
case BFD_RELOC_X86_64_PLT32_BND: |
/* Make the jump instruction point to the address of the operand. At |
runtime we merely add the offset to the actual PLT entry. */ |
value = -4; |
break; |
case BFD_RELOC_386_TLS_GD: |
case BFD_RELOC_386_TLS_LDM: |
case BFD_RELOC_386_TLS_IE_32: |
case BFD_RELOC_386_TLS_IE: |
case BFD_RELOC_386_TLS_GOTIE: |
case BFD_RELOC_386_TLS_GOTDESC: |
case BFD_RELOC_X86_64_TLSGD: |
case BFD_RELOC_X86_64_TLSLD: |
case BFD_RELOC_X86_64_GOTTPOFF: |
case BFD_RELOC_X86_64_GOTPC32_TLSDESC: |
value = 0; /* Fully resolved at runtime. No addend. */ |
/* Fallthrough */ |
case BFD_RELOC_386_TLS_LE: |
case BFD_RELOC_386_TLS_LDO_32: |
case BFD_RELOC_386_TLS_LE_32: |
case BFD_RELOC_X86_64_DTPOFF32: |
case BFD_RELOC_X86_64_DTPOFF64: |
case BFD_RELOC_X86_64_TPOFF32: |
case BFD_RELOC_X86_64_TPOFF64: |
S_SET_THREAD_LOCAL (fixP->fx_addsy); |
break; |
case BFD_RELOC_386_TLS_DESC_CALL: |
case BFD_RELOC_X86_64_TLSDESC_CALL: |
value = 0; /* Fully resolved at runtime. No addend. */ |
S_SET_THREAD_LOCAL (fixP->fx_addsy); |
fixP->fx_done = 0; |
return; |
case BFD_RELOC_386_GOT32: |
case BFD_RELOC_X86_64_GOT32: |
value = 0; /* Fully resolved at runtime. No addend. */ |
break; |
case BFD_RELOC_VTABLE_INHERIT: |
case BFD_RELOC_VTABLE_ENTRY: |
fixP->fx_done = 0; |
return; |
default: |
break; |
} |
#endif /* defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) */ |
*valP = value; |
#endif /* !defined (TE_Mach) */ |
/* Are we finished with this relocation now? */ |
if (fixP->fx_addsy == NULL) |
fixP->fx_done = 1; |
#if defined (OBJ_COFF) && defined (TE_PE) |
else if (fixP->fx_addsy != NULL && S_IS_WEAK (fixP->fx_addsy)) |
{ |
fixP->fx_done = 0; |
/* Remember value for tc_gen_reloc. */ |
fixP->fx_addnumber = value; |
/* Clear out the frag for now. */ |
value = 0; |
} |
#endif |
else if (use_rela_relocations) |
{ |
fixP->fx_no_overflow = 1; |
/* Remember value for tc_gen_reloc. */ |
fixP->fx_addnumber = value; |
value = 0; |
} |
md_number_to_chars (p, value, fixP->fx_size); |
} |
char * |
md_atof (int type, char *litP, int *sizeP) |
{ |
/* This outputs the LITTLENUMs in REVERSE order; |
in accord with the bigendian 386. */ |
return ieee_md_atof (type, litP, sizeP, FALSE); |
} |
static char output_invalid_buf[sizeof (unsigned char) * 2 + 6]; |
static char * |
output_invalid (int c) |
{ |
if (ISPRINT (c)) |
snprintf (output_invalid_buf, sizeof (output_invalid_buf), |
"'%c'", c); |
else |
snprintf (output_invalid_buf, sizeof (output_invalid_buf), |
"(0x%x)", (unsigned char) c); |
return output_invalid_buf; |
} |
/* REG_STRING starts *before* REGISTER_PREFIX. */ |
static const reg_entry * |
parse_real_register (char *reg_string, char **end_op) |
{ |
char *s = reg_string; |
char *p; |
char reg_name_given[MAX_REG_NAME_SIZE + 1]; |
const reg_entry *r; |
/* Skip possible REGISTER_PREFIX and possible whitespace. */ |
if (*s == REGISTER_PREFIX) |
++s; |
if (is_space_char (*s)) |
++s; |
p = reg_name_given; |
while ((*p++ = register_chars[(unsigned char) *s]) != '\0') |
{ |
if (p >= reg_name_given + MAX_REG_NAME_SIZE) |
return (const reg_entry *) NULL; |
s++; |
} |
/* For naked regs, make sure that we are not dealing with an identifier. |
This prevents confusing an identifier like `eax_var' with register |
`eax'. */ |
if (allow_naked_reg && identifier_chars[(unsigned char) *s]) |
return (const reg_entry *) NULL; |
*end_op = s; |
r = (const reg_entry *) hash_find (reg_hash, reg_name_given); |
/* Handle floating point regs, allowing spaces in the (i) part. */ |
if (r == i386_regtab /* %st is first entry of table */) |
{ |
if (is_space_char (*s)) |
++s; |
if (*s == '(') |
{ |
++s; |
if (is_space_char (*s)) |
++s; |
if (*s >= '0' && *s <= '7') |
{ |
int fpr = *s - '0'; |
++s; |
if (is_space_char (*s)) |
++s; |
if (*s == ')') |
{ |
*end_op = s + 1; |
r = (const reg_entry *) hash_find (reg_hash, "st(0)"); |
know (r); |
return r + fpr; |
} |
} |
/* We have "%st(" then garbage. */ |
return (const reg_entry *) NULL; |
} |
} |
if (r == NULL || allow_pseudo_reg) |
return r; |
if (operand_type_all_zero (&r->reg_type)) |
return (const reg_entry *) NULL; |
if ((r->reg_type.bitfield.reg32 |
|| r->reg_type.bitfield.sreg3 |
|| r->reg_type.bitfield.control |
|| r->reg_type.bitfield.debug |
|| r->reg_type.bitfield.test) |
&& !cpu_arch_flags.bitfield.cpui386) |
return (const reg_entry *) NULL; |
if (r->reg_type.bitfield.floatreg |
&& !cpu_arch_flags.bitfield.cpu8087 |
&& !cpu_arch_flags.bitfield.cpu287 |
&& !cpu_arch_flags.bitfield.cpu387) |
return (const reg_entry *) NULL; |
if (r->reg_type.bitfield.regmmx && !cpu_arch_flags.bitfield.cpummx) |
return (const reg_entry *) NULL; |
if (r->reg_type.bitfield.regxmm && !cpu_arch_flags.bitfield.cpusse) |
return (const reg_entry *) NULL; |
if (r->reg_type.bitfield.regymm && !cpu_arch_flags.bitfield.cpuavx) |
return (const reg_entry *) NULL; |
if ((r->reg_type.bitfield.regzmm || r->reg_type.bitfield.regmask) |
&& !cpu_arch_flags.bitfield.cpuavx512f) |
return (const reg_entry *) NULL; |
/* Don't allow fake index register unless allow_index_reg isn't 0. */ |
if (!allow_index_reg |
&& (r->reg_num == RegEiz || r->reg_num == RegRiz)) |
return (const reg_entry *) NULL; |
/* Upper 16 vector register is only available with VREX in 64bit |
mode. */ |
if ((r->reg_flags & RegVRex)) |
{ |
if (!cpu_arch_flags.bitfield.cpuvrex |
|| flag_code != CODE_64BIT) |
return (const reg_entry *) NULL; |
i.need_vrex = 1; |
} |
if (((r->reg_flags & (RegRex64 | RegRex)) |
|| r->reg_type.bitfield.reg64) |
&& (!cpu_arch_flags.bitfield.cpulm |
|| !operand_type_equal (&r->reg_type, &control)) |
&& flag_code != CODE_64BIT) |
return (const reg_entry *) NULL; |
if (r->reg_type.bitfield.sreg3 && r->reg_num == RegFlat && !intel_syntax) |
return (const reg_entry *) NULL; |
return r; |
} |
/* REG_STRING starts *before* REGISTER_PREFIX. */ |
static const reg_entry * |
parse_register (char *reg_string, char **end_op) |
{ |
const reg_entry *r; |
if (*reg_string == REGISTER_PREFIX || allow_naked_reg) |
r = parse_real_register (reg_string, end_op); |
else |
r = NULL; |
if (!r) |
{ |
char *save = input_line_pointer; |
char c; |
symbolS *symbolP; |
input_line_pointer = reg_string; |
c = get_symbol_end (); |
symbolP = symbol_find (reg_string); |
if (symbolP && S_GET_SEGMENT (symbolP) == reg_section) |
{ |
const expressionS *e = symbol_get_value_expression (symbolP); |
know (e->X_op == O_register); |
know (e->X_add_number >= 0 |
&& (valueT) e->X_add_number < i386_regtab_size); |
r = i386_regtab + e->X_add_number; |
*end_op = input_line_pointer; |
} |
*input_line_pointer = c; |
input_line_pointer = save; |
} |
return r; |
} |
int |
i386_parse_name (char *name, expressionS *e, char *nextcharP) |
{ |
const reg_entry *r; |
char *end = input_line_pointer; |
*end = *nextcharP; |
r = parse_register (name, &input_line_pointer); |
if (r && end <= input_line_pointer) |
{ |
*nextcharP = *input_line_pointer; |
*input_line_pointer = 0; |
e->X_op = O_register; |
e->X_add_number = r - i386_regtab; |
return 1; |
} |
input_line_pointer = end; |
*end = 0; |
return intel_syntax ? i386_intel_parse_name (name, e) : 0; |
} |
void |
md_operand (expressionS *e) |
{ |
char *end; |
const reg_entry *r; |
switch (*input_line_pointer) |
{ |
case REGISTER_PREFIX: |
r = parse_real_register (input_line_pointer, &end); |
if (r) |
{ |
e->X_op = O_register; |
e->X_add_number = r - i386_regtab; |
input_line_pointer = end; |
} |
break; |
case '[': |
gas_assert (intel_syntax); |
end = input_line_pointer++; |
expression (e); |
if (*input_line_pointer == ']') |
{ |
++input_line_pointer; |
e->X_op_symbol = make_expr_symbol (e); |
e->X_add_symbol = NULL; |
e->X_add_number = 0; |
e->X_op = O_index; |
} |
else |
{ |
e->X_op = O_absent; |
input_line_pointer = end; |
} |
break; |
} |
} |
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
const char *md_shortopts = "kVQ:sqn"; |
#else |
const char *md_shortopts = "qn"; |
#endif |
#define OPTION_32 (OPTION_MD_BASE + 0) |
#define OPTION_64 (OPTION_MD_BASE + 1) |
#define OPTION_DIVIDE (OPTION_MD_BASE + 2) |
#define OPTION_MARCH (OPTION_MD_BASE + 3) |
#define OPTION_MTUNE (OPTION_MD_BASE + 4) |
#define OPTION_MMNEMONIC (OPTION_MD_BASE + 5) |
#define OPTION_MSYNTAX (OPTION_MD_BASE + 6) |
#define OPTION_MINDEX_REG (OPTION_MD_BASE + 7) |
#define OPTION_MNAKED_REG (OPTION_MD_BASE + 8) |
#define OPTION_MOLD_GCC (OPTION_MD_BASE + 9) |
#define OPTION_MSSE2AVX (OPTION_MD_BASE + 10) |
#define OPTION_MSSE_CHECK (OPTION_MD_BASE + 11) |
#define OPTION_MOPERAND_CHECK (OPTION_MD_BASE + 12) |
#define OPTION_MAVXSCALAR (OPTION_MD_BASE + 13) |
#define OPTION_X32 (OPTION_MD_BASE + 14) |
#define OPTION_MADD_BND_PREFIX (OPTION_MD_BASE + 15) |
#define OPTION_MEVEXLIG (OPTION_MD_BASE + 16) |
#define OPTION_MEVEXWIG (OPTION_MD_BASE + 17) |
struct option md_longopts[] = |
{ |
{"32", no_argument, NULL, OPTION_32}, |
#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \ |
|| defined (TE_PE) || defined (TE_PEP) || defined (OBJ_MACH_O)) |
{"64", no_argument, NULL, OPTION_64}, |
#endif |
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
{"x32", no_argument, NULL, OPTION_X32}, |
#endif |
{"divide", no_argument, NULL, OPTION_DIVIDE}, |
{"march", required_argument, NULL, OPTION_MARCH}, |
{"mtune", required_argument, NULL, OPTION_MTUNE}, |
{"mmnemonic", required_argument, NULL, OPTION_MMNEMONIC}, |
{"msyntax", required_argument, NULL, OPTION_MSYNTAX}, |
{"mindex-reg", no_argument, NULL, OPTION_MINDEX_REG}, |
{"mnaked-reg", no_argument, NULL, OPTION_MNAKED_REG}, |
{"mold-gcc", no_argument, NULL, OPTION_MOLD_GCC}, |
{"msse2avx", no_argument, NULL, OPTION_MSSE2AVX}, |
{"msse-check", required_argument, NULL, OPTION_MSSE_CHECK}, |
{"moperand-check", required_argument, NULL, OPTION_MOPERAND_CHECK}, |
{"mavxscalar", required_argument, NULL, OPTION_MAVXSCALAR}, |
{"madd-bnd-prefix", no_argument, NULL, OPTION_MADD_BND_PREFIX}, |
{"mevexlig", required_argument, NULL, OPTION_MEVEXLIG}, |
{"mevexwig", required_argument, NULL, OPTION_MEVEXWIG}, |
{NULL, no_argument, NULL, 0} |
}; |
size_t md_longopts_size = sizeof (md_longopts); |
int |
md_parse_option (int c, char *arg) |
{ |
unsigned int j; |
char *arch, *next; |
switch (c) |
{ |
case 'n': |
optimize_align_code = 0; |
break; |
case 'q': |
quiet_warnings = 1; |
break; |
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
/* -Qy, -Qn: SVR4 arguments controlling whether a .comment section |
should be emitted or not. FIXME: Not implemented. */ |
case 'Q': |
break; |
/* -V: SVR4 argument to print version ID. */ |
case 'V': |
print_version_id (); |
break; |
/* -k: Ignore for FreeBSD compatibility. */ |
case 'k': |
break; |
case 's': |
/* -s: On i386 Solaris, this tells the native assembler to use |
.stab instead of .stab.excl. We always use .stab anyhow. */ |
break; |
#endif |
#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \ |
|| defined (TE_PE) || defined (TE_PEP) || defined (OBJ_MACH_O)) |
case OPTION_64: |
{ |
const char **list, **l; |
list = bfd_target_list (); |
for (l = list; *l != NULL; l++) |
if (CONST_STRNEQ (*l, "elf64-x86-64") |
|| strcmp (*l, "coff-x86-64") == 0 |
|| strcmp (*l, "pe-x86-64") == 0 |
|| strcmp (*l, "pei-x86-64") == 0 |
|| strcmp (*l, "mach-o-x86-64") == 0) |
{ |
default_arch = "x86_64"; |
break; |
} |
if (*l == NULL) |
as_fatal (_("no compiled in support for x86_64")); |
free (list); |
} |
break; |
#endif |
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
case OPTION_X32: |
if (IS_ELF) |
{ |
const char **list, **l; |
list = bfd_target_list (); |
for (l = list; *l != NULL; l++) |
if (CONST_STRNEQ (*l, "elf32-x86-64")) |
{ |
default_arch = "x86_64:32"; |
break; |
} |
if (*l == NULL) |
as_fatal (_("no compiled in support for 32bit x86_64")); |
free (list); |
} |
else |
as_fatal (_("32bit x86_64 is only supported for ELF")); |
break; |
#endif |
case OPTION_32: |
default_arch = "i386"; |
break; |
case OPTION_DIVIDE: |
#ifdef SVR4_COMMENT_CHARS |
{ |
char *n, *t; |
const char *s; |
n = (char *) xmalloc (strlen (i386_comment_chars) + 1); |
t = n; |
for (s = i386_comment_chars; *s != '\0'; s++) |
if (*s != '/') |
*t++ = *s; |
*t = '\0'; |
i386_comment_chars = n; |
} |
#endif |
break; |
case OPTION_MARCH: |
arch = xstrdup (arg); |
do |
{ |
if (*arch == '.') |
as_fatal (_("invalid -march= option: `%s'"), arg); |
next = strchr (arch, '+'); |
if (next) |
*next++ = '\0'; |
for (j = 0; j < ARRAY_SIZE (cpu_arch); j++) |
{ |
if (strcmp (arch, cpu_arch [j].name) == 0) |
{ |
/* Processor. */ |
if (! cpu_arch[j].flags.bitfield.cpui386) |
continue; |
cpu_arch_name = cpu_arch[j].name; |
cpu_sub_arch_name = NULL; |
cpu_arch_flags = cpu_arch[j].flags; |
cpu_arch_isa = cpu_arch[j].type; |
cpu_arch_isa_flags = cpu_arch[j].flags; |
if (!cpu_arch_tune_set) |
{ |
cpu_arch_tune = cpu_arch_isa; |
cpu_arch_tune_flags = cpu_arch_isa_flags; |
} |
break; |
} |
else if (*cpu_arch [j].name == '.' |
&& strcmp (arch, cpu_arch [j].name + 1) == 0) |
{ |
/* ISA entension. */ |
i386_cpu_flags flags; |
if (!cpu_arch[j].negated) |
flags = cpu_flags_or (cpu_arch_flags, |
cpu_arch[j].flags); |
else |
flags = cpu_flags_and_not (cpu_arch_flags, |
cpu_arch[j].flags); |
if (!cpu_flags_equal (&flags, &cpu_arch_flags)) |
{ |
if (cpu_sub_arch_name) |
{ |
char *name = cpu_sub_arch_name; |
cpu_sub_arch_name = concat (name, |
cpu_arch[j].name, |
(const char *) NULL); |
free (name); |
} |
else |
cpu_sub_arch_name = xstrdup (cpu_arch[j].name); |
cpu_arch_flags = flags; |
cpu_arch_isa_flags = flags; |
} |
break; |
} |
} |
if (j >= ARRAY_SIZE (cpu_arch)) |
as_fatal (_("invalid -march= option: `%s'"), arg); |
arch = next; |
} |
while (next != NULL ); |
break; |
case OPTION_MTUNE: |
if (*arg == '.') |
as_fatal (_("invalid -mtune= option: `%s'"), arg); |
for (j = 0; j < ARRAY_SIZE (cpu_arch); j++) |
{ |
if (strcmp (arg, cpu_arch [j].name) == 0) |
{ |
cpu_arch_tune_set = 1; |
cpu_arch_tune = cpu_arch [j].type; |
cpu_arch_tune_flags = cpu_arch[j].flags; |
break; |
} |
} |
if (j >= ARRAY_SIZE (cpu_arch)) |
as_fatal (_("invalid -mtune= option: `%s'"), arg); |
break; |
case OPTION_MMNEMONIC: |
if (strcasecmp (arg, "att") == 0) |
intel_mnemonic = 0; |
else if (strcasecmp (arg, "intel") == 0) |
intel_mnemonic = 1; |
else |
as_fatal (_("invalid -mmnemonic= option: `%s'"), arg); |
break; |
case OPTION_MSYNTAX: |
if (strcasecmp (arg, "att") == 0) |
intel_syntax = 0; |
else if (strcasecmp (arg, "intel") == 0) |
intel_syntax = 1; |
else |
as_fatal (_("invalid -msyntax= option: `%s'"), arg); |
break; |
case OPTION_MINDEX_REG: |
allow_index_reg = 1; |
break; |
case OPTION_MNAKED_REG: |
allow_naked_reg = 1; |
break; |
case OPTION_MOLD_GCC: |
old_gcc = 1; |
break; |
case OPTION_MSSE2AVX: |
sse2avx = 1; |
break; |
case OPTION_MSSE_CHECK: |
if (strcasecmp (arg, "error") == 0) |
sse_check = check_error; |
else if (strcasecmp (arg, "warning") == 0) |
sse_check = check_warning; |
else if (strcasecmp (arg, "none") == 0) |
sse_check = check_none; |
else |
as_fatal (_("invalid -msse-check= option: `%s'"), arg); |
break; |
case OPTION_MOPERAND_CHECK: |
if (strcasecmp (arg, "error") == 0) |
operand_check = check_error; |
else if (strcasecmp (arg, "warning") == 0) |
operand_check = check_warning; |
else if (strcasecmp (arg, "none") == 0) |
operand_check = check_none; |
else |
as_fatal (_("invalid -moperand-check= option: `%s'"), arg); |
break; |
case OPTION_MAVXSCALAR: |
if (strcasecmp (arg, "128") == 0) |
avxscalar = vex128; |
else if (strcasecmp (arg, "256") == 0) |
avxscalar = vex256; |
else |
as_fatal (_("invalid -mavxscalar= option: `%s'"), arg); |
break; |
case OPTION_MADD_BND_PREFIX: |
add_bnd_prefix = 1; |
break; |
case OPTION_MEVEXLIG: |
if (strcmp (arg, "128") == 0) |
evexlig = evexl128; |
else if (strcmp (arg, "256") == 0) |
evexlig = evexl256; |
else if (strcmp (arg, "512") == 0) |
evexlig = evexl512; |
else |
as_fatal (_("invalid -mevexlig= option: `%s'"), arg); |
break; |
case OPTION_MEVEXWIG: |
if (strcmp (arg, "0") == 0) |
evexwig = evexw0; |
else if (strcmp (arg, "1") == 0) |
evexwig = evexw1; |
else |
as_fatal (_("invalid -mevexwig= option: `%s'"), arg); |
break; |
default: |
return 0; |
} |
return 1; |
} |
#define MESSAGE_TEMPLATE \ |
" " |
static void |
show_arch (FILE *stream, int ext, int check) |
{ |
static char message[] = MESSAGE_TEMPLATE; |
char *start = message + 27; |
char *p; |
int size = sizeof (MESSAGE_TEMPLATE); |
int left; |
const char *name; |
int len; |
unsigned int j; |
p = start; |
left = size - (start - message); |
for (j = 0; j < ARRAY_SIZE (cpu_arch); j++) |
{ |
/* Should it be skipped? */ |
if (cpu_arch [j].skip) |
continue; |
name = cpu_arch [j].name; |
len = cpu_arch [j].len; |
if (*name == '.') |
{ |
/* It is an extension. Skip if we aren't asked to show it. */ |
if (ext) |
{ |
name++; |
len--; |
} |
else |
continue; |
} |
else if (ext) |
{ |
/* It is an processor. Skip if we show only extension. */ |
continue; |
} |
else if (check && ! cpu_arch[j].flags.bitfield.cpui386) |
{ |
/* It is an impossible processor - skip. */ |
continue; |
} |
/* Reserve 2 spaces for ", " or ",\0" */ |
left -= len + 2; |
/* Check if there is any room. */ |
if (left >= 0) |
{ |
if (p != start) |
{ |
*p++ = ','; |
*p++ = ' '; |
} |
p = mempcpy (p, name, len); |
} |
else |
{ |
/* Output the current message now and start a new one. */ |
*p++ = ','; |
*p = '\0'; |
fprintf (stream, "%s\n", message); |
p = start; |
left = size - (start - message) - len - 2; |
gas_assert (left >= 0); |
p = mempcpy (p, name, len); |
} |
} |
*p = '\0'; |
fprintf (stream, "%s\n", message); |
} |
void |
md_show_usage (FILE *stream) |
{ |
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
fprintf (stream, _("\ |
-Q ignored\n\ |
-V print assembler version number\n\ |
-k ignored\n")); |
#endif |
fprintf (stream, _("\ |
-n Do not optimize code alignment\n\ |
-q quieten some warnings\n")); |
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
fprintf (stream, _("\ |
-s ignored\n")); |
#endif |
#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \ |
|| defined (TE_PE) || defined (TE_PEP)) |
fprintf (stream, _("\ |
--32/--64/--x32 generate 32bit/64bit/x32 code\n")); |
#endif |
#ifdef SVR4_COMMENT_CHARS |
fprintf (stream, _("\ |
--divide do not treat `/' as a comment character\n")); |
#else |
fprintf (stream, _("\ |
--divide ignored\n")); |
#endif |
fprintf (stream, _("\ |
-march=CPU[,+EXTENSION...]\n\ |
generate code for CPU and EXTENSION, CPU is one of:\n")); |
show_arch (stream, 0, 1); |
fprintf (stream, _("\ |
EXTENSION is combination of:\n")); |
show_arch (stream, 1, 0); |
fprintf (stream, _("\ |
-mtune=CPU optimize for CPU, CPU is one of:\n")); |
show_arch (stream, 0, 0); |
fprintf (stream, _("\ |
-msse2avx encode SSE instructions with VEX prefix\n")); |
fprintf (stream, _("\ |
-msse-check=[none|error|warning]\n\ |
check SSE instructions\n")); |
fprintf (stream, _("\ |
-moperand-check=[none|error|warning]\n\ |
check operand combinations for validity\n")); |
fprintf (stream, _("\ |
-mavxscalar=[128|256] encode scalar AVX instructions with specific vector\n\ |
length\n")); |
fprintf (stream, _("\ |
-mevexlig=[128|256|512] encode scalar EVEX instructions with specific vector\n\ |
length\n")); |
fprintf (stream, _("\ |
-mevexwig=[0|1] encode EVEX instructions with specific EVEX.W value\n\ |
for EVEX.W bit ignored instructions\n")); |
fprintf (stream, _("\ |
-mmnemonic=[att|intel] use AT&T/Intel mnemonic\n")); |
fprintf (stream, _("\ |
-msyntax=[att|intel] use AT&T/Intel syntax\n")); |
fprintf (stream, _("\ |
-mindex-reg support pseudo index registers\n")); |
fprintf (stream, _("\ |
-mnaked-reg don't require `%%' prefix for registers\n")); |
fprintf (stream, _("\ |
-mold-gcc support old (<= 2.8.1) versions of gcc\n")); |
fprintf (stream, _("\ |
-madd-bnd-prefix add BND prefix for all valid branches\n")); |
} |
#if ((defined (OBJ_MAYBE_COFF) && defined (OBJ_MAYBE_AOUT)) \ |
|| defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) \ |
|| defined (TE_PE) || defined (TE_PEP) || defined (OBJ_MACH_O)) |
/* Pick the target format to use. */ |
const char * |
i386_target_format (void) |
{ |
if (!strncmp (default_arch, "x86_64", 6)) |
{ |
update_code_flag (CODE_64BIT, 1); |
if (default_arch[6] == '\0') |
x86_elf_abi = X86_64_ABI; |
else |
x86_elf_abi = X86_64_X32_ABI; |
} |
else if (!strcmp (default_arch, "i386")) |
update_code_flag (CODE_32BIT, 1); |
else |
as_fatal (_("unknown architecture")); |
if (cpu_flags_all_zero (&cpu_arch_isa_flags)) |
cpu_arch_isa_flags = cpu_arch[flag_code == CODE_64BIT].flags; |
if (cpu_flags_all_zero (&cpu_arch_tune_flags)) |
cpu_arch_tune_flags = cpu_arch[flag_code == CODE_64BIT].flags; |
switch (OUTPUT_FLAVOR) |
{ |
#if defined (OBJ_MAYBE_AOUT) || defined (OBJ_AOUT) |
case bfd_target_aout_flavour: |
return AOUT_TARGET_FORMAT; |
#endif |
#if defined (OBJ_MAYBE_COFF) || defined (OBJ_COFF) |
# if defined (TE_PE) || defined (TE_PEP) |
case bfd_target_coff_flavour: |
return flag_code == CODE_64BIT ? "pe-x86-64" : "pe-i386"; |
# elif defined (TE_GO32) |
case bfd_target_coff_flavour: |
return "coff-go32"; |
# else |
case bfd_target_coff_flavour: |
return "coff-i386"; |
# endif |
#endif |
#if defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF) |
case bfd_target_elf_flavour: |
{ |
const char *format; |
switch (x86_elf_abi) |
{ |
default: |
format = ELF_TARGET_FORMAT; |
break; |
case X86_64_ABI: |
use_rela_relocations = 1; |
object_64bit = 1; |
format = ELF_TARGET_FORMAT64; |
break; |
case X86_64_X32_ABI: |
use_rela_relocations = 1; |
object_64bit = 1; |
disallow_64bit_reloc = 1; |
format = ELF_TARGET_FORMAT32; |
break; |
} |
if (cpu_arch_isa == PROCESSOR_L1OM) |
{ |
if (x86_elf_abi != X86_64_ABI) |
as_fatal (_("Intel L1OM is 64bit only")); |
return ELF_TARGET_L1OM_FORMAT; |
} |
if (cpu_arch_isa == PROCESSOR_K1OM) |
{ |
if (x86_elf_abi != X86_64_ABI) |
as_fatal (_("Intel K1OM is 64bit only")); |
return ELF_TARGET_K1OM_FORMAT; |
} |
else |
return format; |
} |
#endif |
#if defined (OBJ_MACH_O) |
case bfd_target_mach_o_flavour: |
if (flag_code == CODE_64BIT) |
{ |
use_rela_relocations = 1; |
object_64bit = 1; |
return "mach-o-x86-64"; |
} |
else |
return "mach-o-i386"; |
#endif |
default: |
abort (); |
return NULL; |
} |
} |
#endif /* OBJ_MAYBE_ more than one */ |
#if (defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF)) |
void |
i386_elf_emit_arch_note (void) |
{ |
if (IS_ELF && cpu_arch_name != NULL) |
{ |
char *p; |
asection *seg = now_seg; |
subsegT subseg = now_subseg; |
Elf_Internal_Note i_note; |
Elf_External_Note e_note; |
asection *note_secp; |
int len; |
/* Create the .note section. */ |
note_secp = subseg_new (".note", 0); |
bfd_set_section_flags (stdoutput, |
note_secp, |
SEC_HAS_CONTENTS | SEC_READONLY); |
/* Process the arch string. */ |
len = strlen (cpu_arch_name); |
i_note.namesz = len + 1; |
i_note.descsz = 0; |
i_note.type = NT_ARCH; |
p = frag_more (sizeof (e_note.namesz)); |
md_number_to_chars (p, (valueT) i_note.namesz, sizeof (e_note.namesz)); |
p = frag_more (sizeof (e_note.descsz)); |
md_number_to_chars (p, (valueT) i_note.descsz, sizeof (e_note.descsz)); |
p = frag_more (sizeof (e_note.type)); |
md_number_to_chars (p, (valueT) i_note.type, sizeof (e_note.type)); |
p = frag_more (len + 1); |
strcpy (p, cpu_arch_name); |
frag_align (2, 0, 0); |
subseg_set (seg, subseg); |
} |
} |
#endif |
symbolS * |
md_undefined_symbol (char *name) |
{ |
if (name[0] == GLOBAL_OFFSET_TABLE_NAME[0] |
&& name[1] == GLOBAL_OFFSET_TABLE_NAME[1] |
&& name[2] == GLOBAL_OFFSET_TABLE_NAME[2] |
&& strcmp (name, GLOBAL_OFFSET_TABLE_NAME) == 0) |
{ |
if (!GOT_symbol) |
{ |
if (symbol_find (name)) |
as_bad (_("GOT already in symbol table")); |
GOT_symbol = symbol_new (name, undefined_section, |
(valueT) 0, &zero_address_frag); |
}; |
return GOT_symbol; |
} |
return 0; |
} |
/* Round up a section size to the appropriate boundary. */ |
valueT |
md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size) |
{ |
#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT)) |
if (OUTPUT_FLAVOR == bfd_target_aout_flavour) |
{ |
/* For a.out, force the section size to be aligned. If we don't do |
this, BFD will align it for us, but it will not write out the |
final bytes of the section. This may be a bug in BFD, but it is |
easier to fix it here since that is how the other a.out targets |
work. */ |
int align; |
align = bfd_get_section_alignment (stdoutput, segment); |
size = ((size + (1 << align) - 1) & ((valueT) -1 << align)); |
} |
#endif |
return size; |
} |
/* On the i386, PC-relative offsets are relative to the start of the |
next instruction. That is, the address of the offset, plus its |
size, since the offset is always the last part of the insn. */ |
long |
md_pcrel_from (fixS *fixP) |
{ |
return fixP->fx_size + fixP->fx_where + fixP->fx_frag->fr_address; |
} |
#ifndef I386COFF |
static void |
s_bss (int ignore ATTRIBUTE_UNUSED) |
{ |
int temp; |
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
if (IS_ELF) |
obj_elf_section_change_hook (); |
#endif |
temp = get_absolute_expression (); |
subseg_set (bss_section, (subsegT) temp); |
demand_empty_rest_of_line (); |
} |
#endif |
void |
i386_validate_fix (fixS *fixp) |
{ |
if (fixp->fx_subsy && fixp->fx_subsy == GOT_symbol) |
{ |
if (fixp->fx_r_type == BFD_RELOC_32_PCREL) |
{ |
if (!object_64bit) |
abort (); |
fixp->fx_r_type = BFD_RELOC_X86_64_GOTPCREL; |
} |
else |
{ |
if (!object_64bit) |
fixp->fx_r_type = BFD_RELOC_386_GOTOFF; |
else |
fixp->fx_r_type = BFD_RELOC_X86_64_GOTOFF64; |
} |
fixp->fx_subsy = 0; |
} |
} |
arelent * |
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp) |
{ |
arelent *rel; |
bfd_reloc_code_real_type code; |
switch (fixp->fx_r_type) |
{ |
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
case BFD_RELOC_SIZE32: |
case BFD_RELOC_SIZE64: |
if (S_IS_DEFINED (fixp->fx_addsy) |
&& !S_IS_EXTERNAL (fixp->fx_addsy)) |
{ |
/* Resolve size relocation against local symbol to size of |
the symbol plus addend. */ |
valueT value = S_GET_SIZE (fixp->fx_addsy) + fixp->fx_offset; |
if (fixp->fx_r_type == BFD_RELOC_SIZE32 |
&& !fits_in_unsigned_long (value)) |
as_bad_where (fixp->fx_file, fixp->fx_line, |
_("symbol size computation overflow")); |
fixp->fx_addsy = NULL; |
fixp->fx_subsy = NULL; |
md_apply_fix (fixp, (valueT *) &value, NULL); |
return NULL; |
} |
#endif |
case BFD_RELOC_X86_64_PLT32: |
case BFD_RELOC_X86_64_PLT32_BND: |
case BFD_RELOC_X86_64_GOT32: |
case BFD_RELOC_X86_64_GOTPCREL: |
case BFD_RELOC_386_PLT32: |
case BFD_RELOC_386_GOT32: |
case BFD_RELOC_386_GOTOFF: |
case BFD_RELOC_386_GOTPC: |
case BFD_RELOC_386_TLS_GD: |
case BFD_RELOC_386_TLS_LDM: |
case BFD_RELOC_386_TLS_LDO_32: |
case BFD_RELOC_386_TLS_IE_32: |
case BFD_RELOC_386_TLS_IE: |
case BFD_RELOC_386_TLS_GOTIE: |
case BFD_RELOC_386_TLS_LE_32: |
case BFD_RELOC_386_TLS_LE: |
case BFD_RELOC_386_TLS_GOTDESC: |
case BFD_RELOC_386_TLS_DESC_CALL: |
case BFD_RELOC_X86_64_TLSGD: |
case BFD_RELOC_X86_64_TLSLD: |
case BFD_RELOC_X86_64_DTPOFF32: |
case BFD_RELOC_X86_64_DTPOFF64: |
case BFD_RELOC_X86_64_GOTTPOFF: |
case BFD_RELOC_X86_64_TPOFF32: |
case BFD_RELOC_X86_64_TPOFF64: |
case BFD_RELOC_X86_64_GOTOFF64: |
case BFD_RELOC_X86_64_GOTPC32: |
case BFD_RELOC_X86_64_GOT64: |
case BFD_RELOC_X86_64_GOTPCREL64: |
case BFD_RELOC_X86_64_GOTPC64: |
case BFD_RELOC_X86_64_GOTPLT64: |
case BFD_RELOC_X86_64_PLTOFF64: |
case BFD_RELOC_X86_64_GOTPC32_TLSDESC: |
case BFD_RELOC_X86_64_TLSDESC_CALL: |
case BFD_RELOC_RVA: |
case BFD_RELOC_VTABLE_ENTRY: |
case BFD_RELOC_VTABLE_INHERIT: |
#ifdef TE_PE |
case BFD_RELOC_32_SECREL: |
#endif |
code = fixp->fx_r_type; |
break; |
case BFD_RELOC_X86_64_32S: |
if (!fixp->fx_pcrel) |
{ |
/* Don't turn BFD_RELOC_X86_64_32S into BFD_RELOC_32. */ |
code = fixp->fx_r_type; |
break; |
} |
default: |
if (fixp->fx_pcrel) |
{ |
switch (fixp->fx_size) |
{ |
default: |
as_bad_where (fixp->fx_file, fixp->fx_line, |
_("can not do %d byte pc-relative relocation"), |
fixp->fx_size); |
code = BFD_RELOC_32_PCREL; |
break; |
case 1: code = BFD_RELOC_8_PCREL; break; |
case 2: code = BFD_RELOC_16_PCREL; break; |
case 4: |
code = (fixp->fx_r_type == BFD_RELOC_X86_64_PC32_BND |
? fixp-> fx_r_type : BFD_RELOC_32_PCREL); |
break; |
#ifdef BFD64 |
case 8: code = BFD_RELOC_64_PCREL; break; |
#endif |
} |
} |
else |
{ |
switch (fixp->fx_size) |
{ |
default: |
as_bad_where (fixp->fx_file, fixp->fx_line, |
_("can not do %d byte relocation"), |
fixp->fx_size); |
code = BFD_RELOC_32; |
break; |
case 1: code = BFD_RELOC_8; break; |
case 2: code = BFD_RELOC_16; break; |
case 4: code = BFD_RELOC_32; break; |
#ifdef BFD64 |
case 8: code = BFD_RELOC_64; break; |
#endif |
} |
} |
break; |
} |
if ((code == BFD_RELOC_32 |
|| code == BFD_RELOC_32_PCREL |
|| code == BFD_RELOC_X86_64_32S) |
&& GOT_symbol |
&& fixp->fx_addsy == GOT_symbol) |
{ |
if (!object_64bit) |
code = BFD_RELOC_386_GOTPC; |
else |
code = BFD_RELOC_X86_64_GOTPC32; |
} |
if ((code == BFD_RELOC_64 || code == BFD_RELOC_64_PCREL) |
&& GOT_symbol |
&& fixp->fx_addsy == GOT_symbol) |
{ |
code = BFD_RELOC_X86_64_GOTPC64; |
} |
rel = (arelent *) xmalloc (sizeof (arelent)); |
rel->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *)); |
*rel->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); |
rel->address = fixp->fx_frag->fr_address + fixp->fx_where; |
if (!use_rela_relocations) |
{ |
/* HACK: Since i386 ELF uses Rel instead of Rela, encode the |
vtable entry to be used in the relocation's section offset. */ |
if (fixp->fx_r_type == BFD_RELOC_VTABLE_ENTRY) |
rel->address = fixp->fx_offset; |
#if defined (OBJ_COFF) && defined (TE_PE) |
else if (fixp->fx_addsy && S_IS_WEAK (fixp->fx_addsy)) |
rel->addend = fixp->fx_addnumber - (S_GET_VALUE (fixp->fx_addsy) * 2); |
else |
#endif |
rel->addend = 0; |
} |
/* Use the rela in 64bit mode. */ |
else |
{ |
if (disallow_64bit_reloc) |
switch (code) |
{ |
case BFD_RELOC_X86_64_DTPOFF64: |
case BFD_RELOC_X86_64_TPOFF64: |
case BFD_RELOC_64_PCREL: |
case BFD_RELOC_X86_64_GOTOFF64: |
case BFD_RELOC_X86_64_GOT64: |
case BFD_RELOC_X86_64_GOTPCREL64: |
case BFD_RELOC_X86_64_GOTPC64: |
case BFD_RELOC_X86_64_GOTPLT64: |
case BFD_RELOC_X86_64_PLTOFF64: |
as_bad_where (fixp->fx_file, fixp->fx_line, |
_("cannot represent relocation type %s in x32 mode"), |
bfd_get_reloc_code_name (code)); |
break; |
default: |
break; |
} |
if (!fixp->fx_pcrel) |
rel->addend = fixp->fx_offset; |
else |
switch (code) |
{ |
case BFD_RELOC_X86_64_PLT32: |
case BFD_RELOC_X86_64_PLT32_BND: |
case BFD_RELOC_X86_64_GOT32: |
case BFD_RELOC_X86_64_GOTPCREL: |
case BFD_RELOC_X86_64_TLSGD: |
case BFD_RELOC_X86_64_TLSLD: |
case BFD_RELOC_X86_64_GOTTPOFF: |
case BFD_RELOC_X86_64_GOTPC32_TLSDESC: |
case BFD_RELOC_X86_64_TLSDESC_CALL: |
rel->addend = fixp->fx_offset - fixp->fx_size; |
break; |
default: |
rel->addend = (section->vma |
- fixp->fx_size |
+ fixp->fx_addnumber |
+ md_pcrel_from (fixp)); |
break; |
} |
} |
rel->howto = bfd_reloc_type_lookup (stdoutput, code); |
if (rel->howto == NULL) |
{ |
as_bad_where (fixp->fx_file, fixp->fx_line, |
_("cannot represent relocation type %s"), |
bfd_get_reloc_code_name (code)); |
/* Set howto to a garbage value so that we can keep going. */ |
rel->howto = bfd_reloc_type_lookup (stdoutput, BFD_RELOC_32); |
gas_assert (rel->howto != NULL); |
} |
return rel; |
} |
#include "tc-i386-intel.c" |
void |
tc_x86_parse_to_dw2regnum (expressionS *exp) |
{ |
int saved_naked_reg; |
char saved_register_dot; |
saved_naked_reg = allow_naked_reg; |
allow_naked_reg = 1; |
saved_register_dot = register_chars['.']; |
register_chars['.'] = '.'; |
allow_pseudo_reg = 1; |
expression_and_evaluate (exp); |
allow_pseudo_reg = 0; |
register_chars['.'] = saved_register_dot; |
allow_naked_reg = saved_naked_reg; |
if (exp->X_op == O_register && exp->X_add_number >= 0) |
{ |
if ((addressT) exp->X_add_number < i386_regtab_size) |
{ |
exp->X_op = O_constant; |
exp->X_add_number = i386_regtab[exp->X_add_number] |
.dw2_regnum[flag_code >> 1]; |
} |
else |
exp->X_op = O_illegal; |
} |
} |
void |
tc_x86_frame_initial_instructions (void) |
{ |
static unsigned int sp_regno[2]; |
if (!sp_regno[flag_code >> 1]) |
{ |
char *saved_input = input_line_pointer; |
char sp[][4] = {"esp", "rsp"}; |
expressionS exp; |
input_line_pointer = sp[flag_code >> 1]; |
tc_x86_parse_to_dw2regnum (&exp); |
gas_assert (exp.X_op == O_constant); |
sp_regno[flag_code >> 1] = exp.X_add_number; |
input_line_pointer = saved_input; |
} |
cfi_add_CFA_def_cfa (sp_regno[flag_code >> 1], -x86_cie_data_alignment); |
cfi_add_CFA_offset (x86_dwarf2_return_column, x86_cie_data_alignment); |
} |
int |
x86_dwarf2_addr_size (void) |
{ |
#if defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF) |
if (x86_elf_abi == X86_64_X32_ABI) |
return 4; |
#endif |
return bfd_arch_bits_per_address (stdoutput) / 8; |
} |
int |
i386_elf_section_type (const char *str, size_t len) |
{ |
if (flag_code == CODE_64BIT |
&& len == sizeof ("unwind") - 1 |
&& strncmp (str, "unwind", 6) == 0) |
return SHT_X86_64_UNWIND; |
return -1; |
} |
#ifdef TE_SOLARIS |
void |
i386_solaris_fix_up_eh_frame (segT sec) |
{ |
if (flag_code == CODE_64BIT) |
elf_section_type (sec) = SHT_X86_64_UNWIND; |
} |
#endif |
#ifdef TE_PE |
void |
tc_pe_dwarf2_emit_offset (symbolS *symbol, unsigned int size) |
{ |
expressionS exp; |
exp.X_op = O_secrel; |
exp.X_add_symbol = symbol; |
exp.X_add_number = 0; |
emit_expr (&exp, size); |
} |
#endif |
#if defined (OBJ_ELF) || defined (OBJ_MAYBE_ELF) |
/* For ELF on x86-64, add support for SHF_X86_64_LARGE. */ |
bfd_vma |
x86_64_section_letter (int letter, char **ptr_msg) |
{ |
if (flag_code == CODE_64BIT) |
{ |
if (letter == 'l') |
return SHF_X86_64_LARGE; |
*ptr_msg = _("bad .section directive: want a,l,w,x,M,S,G,T in string"); |
} |
else |
*ptr_msg = _("bad .section directive: want a,w,x,M,S,G,T in string"); |
return -1; |
} |
bfd_vma |
x86_64_section_word (char *str, size_t len) |
{ |
if (len == 5 && flag_code == CODE_64BIT && CONST_STRNEQ (str, "large")) |
return SHF_X86_64_LARGE; |
return -1; |
} |
static void |
handle_large_common (int small ATTRIBUTE_UNUSED) |
{ |
if (flag_code != CODE_64BIT) |
{ |
s_comm_internal (0, elf_common_parse); |
as_warn (_(".largecomm supported only in 64bit mode, producing .comm")); |
} |
else |
{ |
static segT lbss_section; |
asection *saved_com_section_ptr = elf_com_section_ptr; |
asection *saved_bss_section = bss_section; |
if (lbss_section == NULL) |
{ |
flagword applicable; |
segT seg = now_seg; |
subsegT subseg = now_subseg; |
/* The .lbss section is for local .largecomm symbols. */ |
lbss_section = subseg_new (".lbss", 0); |
applicable = bfd_applicable_section_flags (stdoutput); |
bfd_set_section_flags (stdoutput, lbss_section, |
applicable & SEC_ALLOC); |
seg_info (lbss_section)->bss = 1; |
subseg_set (seg, subseg); |
} |
elf_com_section_ptr = &_bfd_elf_large_com_section; |
bss_section = lbss_section; |
s_comm_internal (0, elf_common_parse); |
elf_com_section_ptr = saved_com_section_ptr; |
bss_section = saved_bss_section; |
} |
} |
#endif /* OBJ_ELF || OBJ_MAYBE_ELF */ |
/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 = ¯o->formals; |
const char *name; |
idx = sb_skip_white (idx, in); |
while (idx < in->len) |
{ |
formal_entry *formal = new_formal (); |
size_t cidx; |
idx = get_token (idx, in, &formal->name); |
if (formal->name.len == 0) |
{ |
if (macro->formal_count) |
--idx; |
del_formal (formal); /* 'formal' goes out of scope. */ |
break; |
} |
idx = sb_skip_white (idx, in); |
/* This is a formal. */ |
name = sb_terminate (&formal->name); |
if (! macro_mri |
&& idx < in->len |
&& in->ptr[idx] == ':' |
&& (! is_name_beginner (':') |
|| idx + 1 >= in->len |
|| ! is_part_of_name (in->ptr[idx + 1]))) |
{ |
/* Got a qualifier. */ |
sb qual; |
sb_new (&qual); |
idx = get_token (sb_skip_white (idx + 1, in), in, &qual); |
sb_terminate (&qual); |
if (qual.len == 0) |
as_bad_where (macro->file, |
macro->line, |
_("Missing parameter qualifier for `%s' in macro `%s'"), |
name, |
macro->name); |
else if (strcmp (qual.ptr, "req") == 0) |
formal->type = FORMAL_REQUIRED; |
else if (strcmp (qual.ptr, "vararg") == 0) |
formal->type = FORMAL_VARARG; |
else |
as_bad_where (macro->file, |
macro->line, |
_("`%s' is not a valid parameter qualifier for `%s' in macro `%s'"), |
qual.ptr, |
name, |
macro->name); |
sb_kill (&qual); |
idx = sb_skip_white (idx, in); |
} |
if (idx < in->len && in->ptr[idx] == '=') |
{ |
/* Got a default. */ |
idx = get_any_string (idx + 1, in, &formal->def); |
idx = sb_skip_white (idx, in); |
if (formal->type == FORMAL_REQUIRED) |
{ |
sb_reset (&formal->def); |
as_warn_where (macro->file, |
macro->line, |
_("Pointless default value for required parameter `%s' in macro `%s'"), |
name, |
macro->name); |
} |
} |
/* Add to macro's hash table. */ |
if (! hash_find (macro->formal_hash, name)) |
hash_jam (macro->formal_hash, name, formal); |
else |
as_bad_where (macro->file, |
macro->line, |
_("A parameter named `%s' already exists for macro `%s'"), |
name, |
macro->name); |
formal->index = macro->formal_count++; |
*p = formal; |
p = &formal->next; |
if (formal->type == FORMAL_VARARG) |
break; |
cidx = idx; |
idx = sb_skip_comma (idx, in); |
if (idx != cidx && idx >= in->len) |
{ |
idx = cidx; |
break; |
} |
} |
if (macro_mri) |
{ |
formal_entry *formal = new_formal (); |
/* Add a special NARG formal, which macro_expand will set to the |
number of arguments. */ |
/* The same MRI assemblers which treat '@' characters also use |
the name $NARG. At least until we find an exception. */ |
if (macro_strip_at) |
name = "$NARG"; |
else |
name = "NARG"; |
sb_add_string (&formal->name, name); |
/* Add to macro's hash table. */ |
if (hash_find (macro->formal_hash, name)) |
as_bad_where (macro->file, |
macro->line, |
_("Reserved word `%s' used as parameter in macro `%s'"), |
name, |
macro->name); |
hash_jam (macro->formal_hash, name, formal); |
formal->index = NARG_INDEX; |
*p = formal; |
} |
return idx; |
} |
/* Free the memory allocated to a macro. */ |
static void |
free_macro (macro_entry *macro) |
{ |
formal_entry *formal; |
for (formal = macro->formals; formal; ) |
{ |
formal_entry *f; |
f = formal; |
formal = formal->next; |
del_formal (f); |
} |
hash_die (macro->formal_hash); |
sb_kill (¯o->sub); |
free (macro); |
} |
/* Define a new macro. Returns NULL on success, otherwise returns an |
error message. If NAMEP is not NULL, *NAMEP is set to the name of |
the macro which was defined. */ |
const char * |
define_macro (size_t idx, sb *in, sb *label, |
size_t (*get_line) (sb *), |
char *file, unsigned int line, |
const char **namep) |
{ |
macro_entry *macro; |
sb name; |
const char *error = NULL; |
macro = (macro_entry *) xmalloc (sizeof (macro_entry)); |
sb_new (¯o->sub); |
sb_new (&name); |
macro->file = file; |
macro->line = line; |
macro->formal_count = 0; |
macro->formals = 0; |
macro->formal_hash = hash_new_sized (7); |
idx = sb_skip_white (idx, in); |
if (! buffer_and_nest ("MACRO", "ENDM", ¯o->sub, get_line)) |
error = _("unexpected end of file in macro `%s' definition"); |
if (label != NULL && label->len != 0) |
{ |
sb_add_sb (&name, label); |
macro->name = sb_terminate (&name); |
if (idx < in->len && in->ptr[idx] == '(') |
{ |
/* It's the label: MACRO (formals,...) sort */ |
idx = do_formals (macro, idx + 1, in); |
if (idx < in->len && in->ptr[idx] == ')') |
idx = sb_skip_white (idx + 1, in); |
else if (!error) |
error = _("missing `)' after formals in macro definition `%s'"); |
} |
else |
{ |
/* It's the label: MACRO formals,... sort */ |
idx = do_formals (macro, idx, in); |
} |
} |
else |
{ |
size_t cidx; |
idx = get_token (idx, in, &name); |
macro->name = sb_terminate (&name); |
if (name.len == 0) |
error = _("Missing macro name"); |
cidx = sb_skip_white (idx, in); |
idx = sb_skip_comma (cidx, in); |
if (idx == cidx || idx < in->len) |
idx = do_formals (macro, idx, in); |
else |
idx = cidx; |
} |
if (!error && idx < in->len) |
error = _("Bad parameter list for macro `%s'"); |
/* And stick it in the macro hash table. */ |
for (idx = 0; idx < name.len; idx++) |
name.ptr[idx] = TOLOWER (name.ptr[idx]); |
if (hash_find (macro_hash, macro->name)) |
error = _("Macro `%s' was already defined"); |
if (!error) |
error = hash_jam (macro_hash, macro->name, (void *) macro); |
if (namep != NULL) |
*namep = macro->name; |
if (!error) |
macro_defined = 1; |
else |
free_macro (macro); |
return error; |
} |
/* Scan a token, and then skip KIND. */ |
static size_t |
get_apost_token (size_t idx, sb *in, sb *name, int kind) |
{ |
idx = get_token (idx, in, name); |
if (idx < in->len |
&& in->ptr[idx] == kind |
&& (! macro_mri || macro_strip_at) |
&& (! macro_strip_at || kind == '@')) |
idx++; |
return idx; |
} |
/* Substitute the actual value for a formal parameter. */ |
static size_t |
sub_actual (size_t start, sb *in, sb *t, struct hash_control *formal_hash, |
int kind, sb *out, int copyifnotthere) |
{ |
size_t src; |
formal_entry *ptr; |
src = get_apost_token (start, in, t, kind); |
/* See if it's in the macro's hash table, unless this is |
macro_strip_at and kind is '@' and the token did not end in '@'. */ |
if (macro_strip_at |
&& kind == '@' |
&& (src == start || in->ptr[src - 1] != '@')) |
ptr = NULL; |
else |
ptr = (formal_entry *) hash_find (formal_hash, sb_terminate (t)); |
if (ptr) |
{ |
if (ptr->actual.len) |
{ |
sb_add_sb (out, &ptr->actual); |
} |
else |
{ |
sb_add_sb (out, &ptr->def); |
} |
} |
else if (kind == '&') |
{ |
/* Doing this permits people to use & in macro bodies. */ |
sb_add_char (out, '&'); |
sb_add_sb (out, t); |
if (src != start && in->ptr[src - 1] == '&') |
sb_add_char (out, '&'); |
} |
else if (copyifnotthere) |
{ |
sb_add_sb (out, t); |
} |
else |
{ |
sb_add_char (out, '\\'); |
sb_add_sb (out, t); |
} |
return src; |
} |
/* Expand the body of a macro. */ |
static const char * |
macro_expand_body (sb *in, sb *out, formal_entry *formals, |
struct hash_control *formal_hash, const macro_entry *macro) |
{ |
sb t; |
size_t src = 0; |
int inquote = 0, macro_line = 0; |
formal_entry *loclist = NULL; |
const char *err = NULL; |
sb_new (&t); |
while (src < in->len && !err) |
{ |
if (in->ptr[src] == '&') |
{ |
sb_reset (&t); |
if (macro_mri) |
{ |
if (src + 1 < in->len && in->ptr[src + 1] == '&') |
src = sub_actual (src + 2, in, &t, formal_hash, '\'', out, 1); |
else |
sb_add_char (out, in->ptr[src++]); |
} |
else |
{ |
/* Permit macro parameter substition delineated with |
an '&' prefix and optional '&' suffix. */ |
src = sub_actual (src + 1, in, &t, formal_hash, '&', out, 0); |
} |
} |
else if (in->ptr[src] == '\\') |
{ |
src++; |
if (src < in->len && in->ptr[src] == '(') |
{ |
/* Sub in till the next ')' literally. */ |
src++; |
while (src < in->len && in->ptr[src] != ')') |
{ |
sb_add_char (out, in->ptr[src++]); |
} |
if (src < in->len) |
src++; |
else if (!macro) |
err = _("missing `)'"); |
else |
as_bad_where (macro->file, macro->line + macro_line, _("missing `)'")); |
} |
else if (src < in->len && in->ptr[src] == '@') |
{ |
/* Sub in the macro invocation number. */ |
char buffer[10]; |
src++; |
sprintf (buffer, "%d", macro_number); |
sb_add_string (out, buffer); |
} |
else if (src < in->len && in->ptr[src] == '&') |
{ |
/* This is a preprocessor variable name, we don't do them |
here. */ |
sb_add_char (out, '\\'); |
sb_add_char (out, '&'); |
src++; |
} |
else if (macro_mri && src < in->len && ISALNUM (in->ptr[src])) |
{ |
int ind; |
formal_entry *f; |
if (ISDIGIT (in->ptr[src])) |
ind = in->ptr[src] - '0'; |
else if (ISUPPER (in->ptr[src])) |
ind = in->ptr[src] - 'A' + 10; |
else |
ind = in->ptr[src] - 'a' + 10; |
++src; |
for (f = formals; f != NULL; f = f->next) |
{ |
if (f->index == ind - 1) |
{ |
if (f->actual.len != 0) |
sb_add_sb (out, &f->actual); |
else |
sb_add_sb (out, &f->def); |
break; |
} |
} |
} |
else |
{ |
sb_reset (&t); |
src = sub_actual (src, in, &t, formal_hash, '\'', out, 0); |
} |
} |
else if ((macro_alternate || macro_mri) |
&& is_name_beginner (in->ptr[src]) |
&& (! inquote |
|| ! macro_strip_at |
|| (src > 0 && in->ptr[src - 1] == '@'))) |
{ |
if (! macro |
|| src + 5 >= in->len |
|| strncasecmp (in->ptr + src, "LOCAL", 5) != 0 |
|| ! ISWHITE (in->ptr[src + 5]) |
/* PR 11507: Skip keyword LOCAL if it is found inside a quoted string. */ |
|| inquote) |
{ |
sb_reset (&t); |
src = sub_actual (src, in, &t, formal_hash, |
(macro_strip_at && inquote) ? '@' : '\'', |
out, 1); |
} |
else |
{ |
src = sb_skip_white (src + 5, in); |
while (in->ptr[src] != '\n') |
{ |
const char *name; |
formal_entry *f = new_formal (); |
src = get_token (src, in, &f->name); |
name = sb_terminate (&f->name); |
if (! hash_find (formal_hash, name)) |
{ |
static int loccnt; |
char buf[20]; |
f->index = LOCAL_INDEX; |
f->next = loclist; |
loclist = f; |
sprintf (buf, IS_ELF ? ".LL%04x" : "LL%04x", ++loccnt); |
sb_add_string (&f->actual, buf); |
err = hash_jam (formal_hash, name, f); |
if (err != NULL) |
break; |
} |
else |
{ |
as_bad_where (macro->file, |
macro->line + macro_line, |
_("`%s' was already used as parameter (or another local) name"), |
name); |
del_formal (f); |
} |
src = sb_skip_comma (src, in); |
} |
} |
} |
else if (in->ptr[src] == '"' |
|| (macro_mri && in->ptr[src] == '\'')) |
{ |
inquote = !inquote; |
sb_add_char (out, in->ptr[src++]); |
} |
else if (in->ptr[src] == '@' && macro_strip_at) |
{ |
++src; |
if (src < in->len |
&& in->ptr[src] == '@') |
{ |
sb_add_char (out, '@'); |
++src; |
} |
} |
else if (macro_mri |
&& in->ptr[src] == '=' |
&& src + 1 < in->len |
&& in->ptr[src + 1] == '=') |
{ |
formal_entry *ptr; |
sb_reset (&t); |
src = get_token (src + 2, in, &t); |
ptr = (formal_entry *) hash_find (formal_hash, sb_terminate (&t)); |
if (ptr == NULL) |
{ |
/* FIXME: We should really return a warning string here, |
but we can't, because the == might be in the MRI |
comment field, and, since the nature of the MRI |
comment field depends upon the exact instruction |
being used, we don't have enough information here to |
figure out whether it is or not. Instead, we leave |
the == in place, which should cause a syntax error if |
it is not in a comment. */ |
sb_add_char (out, '='); |
sb_add_char (out, '='); |
sb_add_sb (out, &t); |
} |
else |
{ |
if (ptr->actual.len) |
{ |
sb_add_string (out, "-1"); |
} |
else |
{ |
sb_add_char (out, '0'); |
} |
} |
} |
else |
{ |
if (in->ptr[src] == '\n') |
++macro_line; |
sb_add_char (out, in->ptr[src++]); |
} |
} |
sb_kill (&t); |
while (loclist != NULL) |
{ |
formal_entry *f; |
const char *name; |
f = loclist->next; |
name = sb_terminate (&loclist->name); |
hash_delete (formal_hash, name, f == NULL); |
del_formal (loclist); |
loclist = f; |
} |
return err; |
} |
/* Assign values to the formal parameters of a macro, and expand the |
body. */ |
static const char * |
macro_expand (size_t idx, sb *in, macro_entry *m, sb *out) |
{ |
sb t; |
formal_entry *ptr; |
formal_entry *f; |
int is_keyword = 0; |
int narg = 0; |
const char *err = NULL; |
sb_new (&t); |
/* Reset any old value the actuals may have. */ |
for (f = m->formals; f; f = f->next) |
sb_reset (&f->actual); |
f = m->formals; |
while (f != NULL && f->index < 0) |
f = f->next; |
if (macro_mri) |
{ |
/* The macro may be called with an optional qualifier, which may |
be referred to in the macro body as \0. */ |
if (idx < in->len && in->ptr[idx] == '.') |
{ |
/* The Microtec assembler ignores this if followed by a white space. |
(Macro invocation with empty extension) */ |
idx++; |
if ( idx < in->len |
&& in->ptr[idx] != ' ' |
&& in->ptr[idx] != '\t') |
{ |
formal_entry *n = new_formal (); |
n->index = QUAL_INDEX; |
n->next = m->formals; |
m->formals = n; |
idx = get_any_string (idx, in, &n->actual); |
} |
} |
} |
/* Peel off the actuals and store them away in the hash tables' actuals. */ |
idx = sb_skip_white (idx, in); |
while (idx < in->len) |
{ |
size_t scan; |
/* Look and see if it's a positional or keyword arg. */ |
scan = idx; |
while (scan < in->len |
&& !ISSEP (in->ptr[scan]) |
&& !(macro_mri && in->ptr[scan] == '\'') |
&& (!macro_alternate && in->ptr[scan] != '=')) |
scan++; |
if (scan < in->len && !macro_alternate && in->ptr[scan] == '=') |
{ |
is_keyword = 1; |
/* It's OK to go from positional to keyword. */ |
/* This is a keyword arg, fetch the formal name and |
then the actual stuff. */ |
sb_reset (&t); |
idx = get_token (idx, in, &t); |
if (in->ptr[idx] != '=') |
{ |
err = _("confusion in formal parameters"); |
break; |
} |
/* Lookup the formal in the macro's list. */ |
ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t)); |
if (!ptr) |
{ |
as_bad (_("Parameter named `%s' does not exist for macro `%s'"), |
t.ptr, |
m->name); |
sb_reset (&t); |
idx = get_any_string (idx + 1, in, &t); |
} |
else |
{ |
/* Insert this value into the right place. */ |
if (ptr->actual.len) |
{ |
as_warn (_("Value for parameter `%s' of macro `%s' was already specified"), |
ptr->name.ptr, |
m->name); |
sb_reset (&ptr->actual); |
} |
idx = get_any_string (idx + 1, in, &ptr->actual); |
if (ptr->actual.len > 0) |
++narg; |
} |
} |
else |
{ |
if (is_keyword) |
{ |
err = _("can't mix positional and keyword arguments"); |
break; |
} |
if (!f) |
{ |
formal_entry **pf; |
int c; |
if (!macro_mri) |
{ |
err = _("too many positional arguments"); |
break; |
} |
f = new_formal (); |
c = -1; |
for (pf = &m->formals; *pf != NULL; pf = &(*pf)->next) |
if ((*pf)->index >= c) |
c = (*pf)->index + 1; |
if (c == -1) |
c = 0; |
*pf = f; |
f->index = c; |
} |
if (f->type != FORMAL_VARARG) |
idx = get_any_string (idx, in, &f->actual); |
else |
{ |
sb_add_buffer (&f->actual, in->ptr + idx, in->len - idx); |
idx = in->len; |
} |
if (f->actual.len > 0) |
++narg; |
do |
{ |
f = f->next; |
} |
while (f != NULL && f->index < 0); |
} |
if (! macro_mri) |
idx = sb_skip_comma (idx, in); |
else |
{ |
if (in->ptr[idx] == ',') |
++idx; |
if (ISWHITE (in->ptr[idx])) |
break; |
} |
} |
if (! err) |
{ |
for (ptr = m->formals; ptr; ptr = ptr->next) |
{ |
if (ptr->type == FORMAL_REQUIRED && ptr->actual.len == 0) |
as_bad (_("Missing value for required parameter `%s' of macro `%s'"), |
ptr->name.ptr, |
m->name); |
} |
if (macro_mri) |
{ |
char buffer[20]; |
sb_reset (&t); |
sb_add_string (&t, macro_strip_at ? "$NARG" : "NARG"); |
ptr = (formal_entry *) hash_find (m->formal_hash, sb_terminate (&t)); |
sprintf (buffer, "%d", narg); |
sb_add_string (&ptr->actual, buffer); |
} |
err = macro_expand_body (&m->sub, out, m->formals, m->formal_hash, m); |
} |
/* Discard any unnamed formal arguments. */ |
if (macro_mri) |
{ |
formal_entry **pf; |
pf = &m->formals; |
while (*pf != NULL) |
{ |
if ((*pf)->name.len != 0) |
pf = &(*pf)->next; |
else |
{ |
f = (*pf)->next; |
del_formal (*pf); |
*pf = f; |
} |
} |
} |
sb_kill (&t); |
if (!err) |
macro_number++; |
return err; |
} |
/* Check for a macro. If one is found, put the expansion into |
*EXPAND. Return 1 if a macro is found, 0 otherwise. */ |
int |
check_macro (const char *line, sb *expand, |
const char **error, macro_entry **info) |
{ |
const char *s; |
char *copy, *cls; |
macro_entry *macro; |
sb line_sb; |
if (! is_name_beginner (*line) |
&& (! macro_mri || *line != '.')) |
return 0; |
s = line + 1; |
while (is_part_of_name (*s)) |
++s; |
if (is_name_ender (*s)) |
++s; |
copy = (char *) alloca (s - line + 1); |
memcpy (copy, line, s - line); |
copy[s - line] = '\0'; |
for (cls = copy; *cls != '\0'; cls ++) |
*cls = TOLOWER (*cls); |
macro = (macro_entry *) hash_find (macro_hash, copy); |
if (macro == NULL) |
return 0; |
/* Wrap the line up in an sb. */ |
sb_new (&line_sb); |
while (*s != '\0' && *s != '\n' && *s != '\r') |
sb_add_char (&line_sb, *s++); |
sb_new (expand); |
*error = macro_expand (0, &line_sb, macro, expand); |
sb_kill (&line_sb); |
/* Export the macro information if requested. */ |
if (info) |
*info = macro; |
return 1; |
} |
/* Delete a macro. */ |
void |
delete_macro (const char *name) |
{ |
char *copy; |
size_t i, len; |
macro_entry *macro; |
len = strlen (name); |
copy = (char *) alloca (len + 1); |
for (i = 0; i < len; ++i) |
copy[i] = TOLOWER (name[i]); |
copy[i] = '\0'; |
/* We can only ask hash_delete to free memory if we are deleting |
macros in reverse order to their definition. |
So just clear out the entry. */ |
if ((macro = (macro_entry *) hash_find (macro_hash, copy)) != NULL) |
{ |
hash_jam (macro_hash, copy, NULL); |
free_macro (macro); |
} |
else |
as_warn (_("Attempt to purge non-existant macro `%s'"), copy); |
} |
/* Handle the MRI IRP and IRPC pseudo-ops. These are handled as a |
combined macro definition and execution. This returns NULL on |
success, or an error message otherwise. */ |
const char * |
expand_irp (int irpc, size_t idx, sb *in, sb *out, size_t (*get_line) (sb *)) |
{ |
sb sub; |
formal_entry f; |
struct hash_control *h; |
const char *err; |
idx = sb_skip_white (idx, in); |
sb_new (&sub); |
if (! buffer_and_nest (NULL, "ENDR", &sub, get_line)) |
return _("unexpected end of file in irp or irpc"); |
sb_new (&f.name); |
sb_new (&f.def); |
sb_new (&f.actual); |
idx = get_token (idx, in, &f.name); |
if (f.name.len == 0) |
return _("missing model parameter"); |
h = hash_new (); |
err = hash_jam (h, sb_terminate (&f.name), &f); |
if (err != NULL) |
return err; |
f.index = 1; |
f.next = NULL; |
f.type = FORMAL_OPTIONAL; |
sb_reset (out); |
idx = sb_skip_comma (idx, in); |
if (idx >= in->len) |
{ |
/* Expand once with a null string. */ |
err = macro_expand_body (&sub, out, &f, h, 0); |
} |
else |
{ |
bfd_boolean in_quotes = FALSE; |
if (irpc && in->ptr[idx] == '"') |
{ |
in_quotes = TRUE; |
++idx; |
} |
while (idx < in->len) |
{ |
if (!irpc) |
idx = get_any_string (idx, in, &f.actual); |
else |
{ |
if (in->ptr[idx] == '"') |
{ |
size_t nxt; |
if (irpc) |
in_quotes = ! in_quotes; |
nxt = sb_skip_white (idx + 1, in); |
if (nxt >= in->len) |
{ |
idx = nxt; |
break; |
} |
} |
sb_reset (&f.actual); |
sb_add_char (&f.actual, in->ptr[idx]); |
++idx; |
} |
err = macro_expand_body (&sub, out, &f, h, 0); |
if (err != NULL) |
break; |
if (!irpc) |
idx = sb_skip_comma (idx, in); |
else if (! in_quotes) |
idx = sb_skip_white (idx, in); |
} |
} |
hash_die (h); |
sb_kill (&f.actual); |
sb_kill (&f.def); |
sb_kill (&f.name); |
sb_kill (&sub); |
return err; |
} |
/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 (¬es, chunksize); |
obstack_begin (&cond_obstack, chunksize); |
/* Use machine dependent syntax. */ |
for (p = line_separator_chars; *p; p++) |
is_end_of_line[(unsigned char) *p] = 2; |
/* Use more. FIXME-SOMEDAY. */ |
if (flag_mri) |
lex_type['?'] = 3; |
} |
#ifndef TC_ADDRESS_BYTES |
#define TC_ADDRESS_BYTES address_bytes |
static inline int |
address_bytes (void) |
{ |
/* Choose smallest of 1, 2, 4, 8 bytes that is large enough to |
contain an address. */ |
int n = (stdoutput->arch_info->bits_per_address - 1) / 8; |
n |= n >> 1; |
n |= n >> 2; |
n += 1; |
return n; |
} |
#endif |
/* Set up pseudo-op tables. */ |
static struct hash_control *po_hash; |
static const pseudo_typeS potable[] = { |
{"abort", s_abort, 0}, |
{"align", s_align_ptwo, 0}, |
{"altmacro", s_altmacro, 1}, |
{"ascii", stringer, 8+0}, |
{"asciz", stringer, 8+1}, |
{"balign", s_align_bytes, 0}, |
{"balignw", s_align_bytes, -2}, |
{"balignl", s_align_bytes, -4}, |
/* block */ |
#ifdef HANDLE_BUNDLE |
{"bundle_align_mode", s_bundle_align_mode, 0}, |
{"bundle_lock", s_bundle_lock, 0}, |
{"bundle_unlock", s_bundle_unlock, 0}, |
#endif |
{"byte", cons, 1}, |
{"comm", s_comm, 0}, |
{"common", s_mri_common, 0}, |
{"common.s", s_mri_common, 1}, |
{"data", s_data, 0}, |
{"dc", cons, 2}, |
#ifdef TC_ADDRESS_BYTES |
{"dc.a", cons, 0}, |
#endif |
{"dc.b", cons, 1}, |
{"dc.d", float_cons, 'd'}, |
{"dc.l", cons, 4}, |
{"dc.s", float_cons, 'f'}, |
{"dc.w", cons, 2}, |
{"dc.x", float_cons, 'x'}, |
{"dcb", s_space, 2}, |
{"dcb.b", s_space, 1}, |
{"dcb.d", s_float_space, 'd'}, |
{"dcb.l", s_space, 4}, |
{"dcb.s", s_float_space, 'f'}, |
{"dcb.w", s_space, 2}, |
{"dcb.x", s_float_space, 'x'}, |
{"ds", s_space, 2}, |
{"ds.b", s_space, 1}, |
{"ds.d", s_space, 8}, |
{"ds.l", s_space, 4}, |
{"ds.p", s_space, 12}, |
{"ds.s", s_space, 4}, |
{"ds.w", s_space, 2}, |
{"ds.x", s_space, 12}, |
{"debug", s_ignore, 0}, |
#ifdef S_SET_DESC |
{"desc", s_desc, 0}, |
#endif |
/* dim */ |
{"double", float_cons, 'd'}, |
/* dsect */ |
{"eject", listing_eject, 0}, /* Formfeed listing. */ |
{"else", s_else, 0}, |
{"elsec", s_else, 0}, |
{"elseif", s_elseif, (int) O_ne}, |
{"end", s_end, 0}, |
{"endc", s_endif, 0}, |
{"endfunc", s_func, 1}, |
{"endif", s_endif, 0}, |
{"endm", s_bad_end, 0}, |
{"endr", s_bad_end, 1}, |
/* endef */ |
{"equ", s_set, 0}, |
{"equiv", s_set, 1}, |
{"eqv", s_set, -1}, |
{"err", s_err, 0}, |
{"error", s_errwarn, 1}, |
{"exitm", s_mexit, 0}, |
/* extend */ |
{"extern", s_ignore, 0}, /* We treat all undef as ext. */ |
{"appfile", s_app_file, 1}, |
{"appline", s_app_line, 1}, |
{"fail", s_fail, 0}, |
{"file", s_app_file, 0}, |
{"fill", s_fill, 0}, |
{"float", float_cons, 'f'}, |
{"format", s_ignore, 0}, |
{"func", s_func, 0}, |
{"global", s_globl, 0}, |
{"globl", s_globl, 0}, |
{"hword", cons, 2}, |
{"if", s_if, (int) O_ne}, |
{"ifb", s_ifb, 1}, |
{"ifc", s_ifc, 0}, |
{"ifdef", s_ifdef, 0}, |
{"ifeq", s_if, (int) O_eq}, |
{"ifeqs", s_ifeqs, 0}, |
{"ifge", s_if, (int) O_ge}, |
{"ifgt", s_if, (int) O_gt}, |
{"ifle", s_if, (int) O_le}, |
{"iflt", s_if, (int) O_lt}, |
{"ifnb", s_ifb, 0}, |
{"ifnc", s_ifc, 1}, |
{"ifndef", s_ifdef, 1}, |
{"ifne", s_if, (int) O_ne}, |
{"ifnes", s_ifeqs, 1}, |
{"ifnotdef", s_ifdef, 1}, |
{"incbin", s_incbin, 0}, |
{"include", s_include, 0}, |
{"int", cons, 4}, |
{"irp", s_irp, 0}, |
{"irep", s_irp, 0}, |
{"irpc", s_irp, 1}, |
{"irepc", s_irp, 1}, |
{"lcomm", s_lcomm, 0}, |
{"lflags", s_ignore, 0}, /* Listing flags. */ |
{"linefile", s_app_line, 0}, |
{"linkonce", s_linkonce, 0}, |
{"list", listing_list, 1}, /* Turn listing on. */ |
{"llen", listing_psize, 1}, |
{"long", cons, 4}, |
{"lsym", s_lsym, 0}, |
{"macro", s_macro, 0}, |
{"mexit", s_mexit, 0}, |
{"mri", s_mri, 0}, |
{".mri", s_mri, 0}, /* Special case so .mri works in MRI mode. */ |
{"name", s_ignore, 0}, |
{"noaltmacro", s_altmacro, 0}, |
{"noformat", s_ignore, 0}, |
{"nolist", listing_list, 0}, /* Turn listing off. */ |
{"nopage", listing_nopage, 0}, |
{"octa", cons, 16}, |
{"offset", s_struct, 0}, |
{"org", s_org, 0}, |
{"p2align", s_align_ptwo, 0}, |
{"p2alignw", s_align_ptwo, -2}, |
{"p2alignl", s_align_ptwo, -4}, |
{"page", listing_eject, 0}, |
{"plen", listing_psize, 0}, |
{"print", s_print, 0}, |
{"psize", listing_psize, 0}, /* Set paper size. */ |
{"purgem", s_purgem, 0}, |
{"quad", cons, 8}, |
{"reloc", s_reloc, 0}, |
{"rep", s_rept, 0}, |
{"rept", s_rept, 0}, |
{"rva", s_rva, 4}, |
{"sbttl", listing_title, 1}, /* Subtitle of listing. */ |
/* scl */ |
/* sect */ |
{"set", s_set, 0}, |
{"short", cons, 2}, |
{"single", float_cons, 'f'}, |
/* size */ |
{"space", s_space, 0}, |
{"skip", s_space, 0}, |
{"sleb128", s_leb128, 1}, |
{"spc", s_ignore, 0}, |
{"stabd", s_stab, 'd'}, |
{"stabn", s_stab, 'n'}, |
{"stabs", s_stab, 's'}, |
{"string", stringer, 8+1}, |
{"string8", stringer, 8+1}, |
{"string16", stringer, 16+1}, |
{"string32", stringer, 32+1}, |
{"string64", stringer, 64+1}, |
{"struct", s_struct, 0}, |
/* tag */ |
{"text", s_text, 0}, |
/* This is for gcc to use. It's only just been added (2/94), so gcc |
won't be able to use it for a while -- probably a year or more. |
But once this has been released, check with gcc maintainers |
before deleting it or even changing the spelling. */ |
{"this_GCC_requires_the_GNU_assembler", s_ignore, 0}, |
/* If we're folding case -- done for some targets, not necessarily |
all -- the above string in an input file will be converted to |
this one. Match it either way... */ |
{"this_gcc_requires_the_gnu_assembler", s_ignore, 0}, |
{"title", listing_title, 0}, /* Listing title. */ |
{"ttl", listing_title, 0}, |
/* type */ |
{"uleb128", s_leb128, 0}, |
/* use */ |
/* val */ |
{"xcom", s_comm, 0}, |
{"xdef", s_globl, 0}, |
{"xref", s_ignore, 0}, |
{"xstabs", s_xstab, 's'}, |
{"warning", s_errwarn, 0}, |
{"weakref", s_weakref, 0}, |
{"word", cons, 2}, |
{"zero", s_space, 0}, |
{NULL, NULL, 0} /* End sentinel. */ |
}; |
static offsetT |
get_absolute_expr (expressionS *exp) |
{ |
expression_and_evaluate (exp); |
if (exp->X_op != O_constant) |
{ |
if (exp->X_op != O_absent) |
as_bad (_("bad or irreducible absolute expression")); |
exp->X_add_number = 0; |
} |
return exp->X_add_number; |
} |
offsetT |
get_absolute_expression (void) |
{ |
expressionS exp; |
return get_absolute_expr (&exp); |
} |
static int pop_override_ok = 0; |
static const char *pop_table_name; |
void |
pop_insert (const pseudo_typeS *table) |
{ |
const char *errtxt; |
const pseudo_typeS *pop; |
for (pop = table; pop->poc_name; pop++) |
{ |
errtxt = hash_insert (po_hash, pop->poc_name, (char *) pop); |
if (errtxt && (!pop_override_ok || strcmp (errtxt, "exists"))) |
as_fatal (_("error constructing %s pseudo-op table: %s"), pop_table_name, |
errtxt); |
} |
} |
#ifndef md_pop_insert |
#define md_pop_insert() pop_insert(md_pseudo_table) |
#endif |
#ifndef obj_pop_insert |
#define obj_pop_insert() pop_insert(obj_pseudo_table) |
#endif |
#ifndef cfi_pop_insert |
#define cfi_pop_insert() pop_insert(cfi_pseudo_table) |
#endif |
static void |
pobegin (void) |
{ |
po_hash = hash_new (); |
/* Do the target-specific pseudo ops. */ |
pop_table_name = "md"; |
md_pop_insert (); |
/* Now object specific. Skip any that were in the target table. */ |
pop_table_name = "obj"; |
pop_override_ok = 1; |
obj_pop_insert (); |
/* Now portable ones. Skip any that we've seen already. */ |
pop_table_name = "standard"; |
pop_insert (potable); |
/* Now CFI ones. */ |
pop_table_name = "cfi"; |
pop_override_ok = 1; |
cfi_pop_insert (); |
} |
#define HANDLE_CONDITIONAL_ASSEMBLY() \ |
if (ignore_input ()) \ |
{ \ |
char *eol = find_end_of_line (input_line_pointer, flag_m68k_mri); \ |
input_line_pointer = (input_line_pointer <= buffer_limit \ |
&& eol >= buffer_limit) \ |
? buffer_limit \ |
: eol + 1; \ |
continue; \ |
} |
/* This function is used when scrubbing the characters between #APP |
and #NO_APP. */ |
static char *scrub_string; |
static char *scrub_string_end; |
static size_t |
scrub_from_string (char *buf, size_t buflen) |
{ |
size_t copy; |
copy = scrub_string_end - scrub_string; |
if (copy > buflen) |
copy = buflen; |
memcpy (buf, scrub_string, copy); |
scrub_string += copy; |
return copy; |
} |
/* Helper function of read_a_source_file, which tries to expand a macro. */ |
static int |
try_macro (char term, const char *line) |
{ |
sb out; |
const char *err; |
macro_entry *macro; |
if (check_macro (line, &out, &err, ¯o)) |
{ |
if (err != NULL) |
as_bad ("%s", err); |
*input_line_pointer++ = term; |
input_scrub_include_sb (&out, |
input_line_pointer, 1); |
sb_kill (&out); |
buffer_limit = |
input_scrub_next_buffer (&input_line_pointer); |
#ifdef md_macro_info |
md_macro_info (macro); |
#endif |
return 1; |
} |
return 0; |
} |
#ifdef HANDLE_BUNDLE |
/* Start a new instruction bundle. Returns the rs_align_code frag that |
will be used to align the new bundle. */ |
static fragS * |
start_bundle (void) |
{ |
fragS *frag = frag_now; |
frag_align_code (0, 0); |
while (frag->fr_type != rs_align_code) |
frag = frag->fr_next; |
gas_assert (frag != frag_now); |
return frag; |
} |
/* Calculate the maximum size after relaxation of the region starting |
at the given frag and extending through frag_now (which is unfinished). */ |
static unsigned int |
pending_bundle_size (fragS *frag) |
{ |
unsigned int offset = frag->fr_fix; |
unsigned int size = 0; |
gas_assert (frag != frag_now); |
gas_assert (frag->fr_type == rs_align_code); |
while (frag != frag_now) |
{ |
/* This should only happen in what will later become an error case. */ |
if (frag == NULL) |
return 0; |
size += frag->fr_fix; |
if (frag->fr_type == rs_machine_dependent) |
size += md_frag_max_var (frag); |
frag = frag->fr_next; |
} |
gas_assert (frag == frag_now); |
size += frag_now_fix (); |
if (frag->fr_type == rs_machine_dependent) |
size += md_frag_max_var (frag); |
gas_assert (size >= offset); |
return size - offset; |
} |
/* Finish off the frag created to ensure bundle alignment. */ |
static void |
finish_bundle (fragS *frag, unsigned int size) |
{ |
gas_assert (bundle_align_p2 > 0); |
gas_assert (frag->fr_type == rs_align_code); |
if (size > 1) |
{ |
/* If there is more than a single byte, then we need to set up the |
alignment frag. Otherwise we leave it at its initial state from |
calling frag_align_code (0, 0), so that it does nothing. */ |
frag->fr_offset = bundle_align_p2; |
frag->fr_subtype = size - 1; |
} |
/* We do this every time rather than just in s_bundle_align_mode |
so that we catch any affected section without needing hooks all |
over for all paths that do section changes. It's cheap enough. */ |
record_alignment (now_seg, bundle_align_p2 - OCTETS_PER_BYTE_POWER); |
} |
/* Assemble one instruction. This takes care of the bundle features |
around calling md_assemble. */ |
static void |
assemble_one (char *line) |
{ |
fragS *insn_start_frag = NULL; |
if (bundle_lock_frchain != NULL && bundle_lock_frchain != frchain_now) |
{ |
as_bad (_("cannot change section or subsection inside .bundle_lock")); |
/* Clearing this serves as a marker that we have already complained. */ |
bundle_lock_frchain = NULL; |
} |
if (bundle_lock_frchain == NULL && bundle_align_p2 > 0) |
insn_start_frag = start_bundle (); |
md_assemble (line); |
if (bundle_lock_frchain != NULL) |
{ |
/* Make sure this hasn't pushed the locked sequence |
past the bundle size. */ |
unsigned int bundle_size = pending_bundle_size (bundle_lock_frag); |
if (bundle_size > (1U << bundle_align_p2)) |
as_bad (_("\ |
.bundle_lock sequence at %u bytes but .bundle_align_mode limit is %u bytes"), |
bundle_size, 1U << bundle_align_p2); |
} |
else if (bundle_align_p2 > 0) |
{ |
unsigned int insn_size = pending_bundle_size (insn_start_frag); |
if (insn_size > (1U << bundle_align_p2)) |
as_bad (_("\ |
single instruction is %u bytes long but .bundle_align_mode limit is %u"), |
(unsigned int) insn_size, 1U << bundle_align_p2); |
finish_bundle (insn_start_frag, insn_size); |
} |
} |
#else /* !HANDLE_BUNDLE */ |
# define assemble_one(line) md_assemble(line) |
#endif /* HANDLE_BUNDLE */ |
/* We read the file, putting things into a web that represents what we |
have been reading. */ |
void |
read_a_source_file (char *name) |
{ |
char c; |
char *s; /* String of symbol, '\0' appended. */ |
int temp; |
pseudo_typeS *pop; |
#ifdef WARN_COMMENTS |
found_comment = 0; |
#endif |
buffer = input_scrub_new_file (name); |
listing_file (name); |
listing_newline (NULL); |
register_dependency (name); |
/* Generate debugging information before we've read anything in to denote |
this file as the "main" source file and not a subordinate one |
(e.g. N_SO vs N_SOL in stabs). */ |
generate_file_debug (); |
while ((buffer_limit = input_scrub_next_buffer (&input_line_pointer)) != 0) |
{ /* We have another line to parse. */ |
#ifndef NO_LISTING |
/* In order to avoid listing macro expansion lines with labels |
multiple times, keep track of which line was last issued. */ |
static char *last_eol; |
last_eol = NULL; |
#endif |
while (input_line_pointer < buffer_limit) |
{ |
bfd_boolean was_new_line; |
/* We have more of this buffer to parse. */ |
/* We now have input_line_pointer->1st char of next line. |
If input_line_pointer [-1] == '\n' then we just |
scanned another line: so bump line counters. */ |
was_new_line = is_end_of_line[(unsigned char) input_line_pointer[-1]]; |
if (was_new_line) |
{ |
symbol_set_value_now (&dot_symbol); |
#ifdef md_start_line_hook |
md_start_line_hook (); |
#endif |
if (input_line_pointer[-1] == '\n') |
bump_line_counters (); |
} |
#ifndef NO_LISTING |
/* If listing is on, and we are expanding a macro, then give |
the listing code the contents of the expanded line. */ |
if (listing) |
{ |
if ((listing & LISTING_MACEXP) && macro_nest > 0) |
{ |
/* Find the end of the current expanded macro line. */ |
s = find_end_of_line (input_line_pointer, flag_m68k_mri); |
if (s != last_eol) |
{ |
char *copy; |
int len; |
last_eol = s; |
/* Copy it for safe keeping. Also give an indication of |
how much macro nesting is involved at this point. */ |
len = s - input_line_pointer; |
copy = (char *) xmalloc (len + macro_nest + 2); |
memset (copy, '>', macro_nest); |
copy[macro_nest] = ' '; |
memcpy (copy + macro_nest + 1, input_line_pointer, len); |
copy[macro_nest + 1 + len] = '\0'; |
/* Install the line with the listing facility. */ |
listing_newline (copy); |
} |
} |
else |
listing_newline (NULL); |
} |
#endif |
if (was_new_line) |
{ |
line_label = NULL; |
if (LABELS_WITHOUT_COLONS || flag_m68k_mri) |
{ |
/* Text at the start of a line must be a label, we |
run down and stick a colon in. */ |
if (is_name_beginner (*input_line_pointer)) |
{ |
char *line_start = input_line_pointer; |
int mri_line_macro; |
HANDLE_CONDITIONAL_ASSEMBLY (); |
c = get_symbol_end (); |
/* In MRI mode, the EQU and MACRO pseudoops must |
be handled specially. */ |
mri_line_macro = 0; |
if (flag_m68k_mri) |
{ |
char *rest = input_line_pointer + 1; |
if (*rest == ':') |
++rest; |
if (*rest == ' ' || *rest == '\t') |
++rest; |
if ((strncasecmp (rest, "EQU", 3) == 0 |
|| strncasecmp (rest, "SET", 3) == 0) |
&& (rest[3] == ' ' || rest[3] == '\t')) |
{ |
input_line_pointer = rest + 3; |
equals (line_start, |
strncasecmp (rest, "SET", 3) == 0); |
continue; |
} |
if (strncasecmp (rest, "MACRO", 5) == 0 |
&& (rest[5] == ' ' |
|| rest[5] == '\t' |
|| is_end_of_line[(unsigned char) rest[5]])) |
mri_line_macro = 1; |
} |
/* In MRI mode, we need to handle the MACRO |
pseudo-op specially: we don't want to put the |
symbol in the symbol table. */ |
if (!mri_line_macro |
#ifdef TC_START_LABEL_WITHOUT_COLON |
&& TC_START_LABEL_WITHOUT_COLON(c, |
input_line_pointer) |
#endif |
) |
line_label = colon (line_start); |
else |
line_label = symbol_create (line_start, |
absolute_section, |
(valueT) 0, |
&zero_address_frag); |
*input_line_pointer = c; |
if (c == ':') |
input_line_pointer++; |
} |
} |
} |
/* We are at the beginning of a line, or similar place. |
We expect a well-formed assembler statement. |
A "symbol-name:" is a statement. |
Depending on what compiler is used, the order of these tests |
may vary to catch most common case 1st. |
Each test is independent of all other tests at the (top) |
level. */ |
do |
c = *input_line_pointer++; |
while (c == '\t' || c == ' ' || c == '\f'); |
/* C is the 1st significant character. |
Input_line_pointer points after that character. */ |
if (is_name_beginner (c)) |
{ |
/* Want user-defined label or pseudo/opcode. */ |
HANDLE_CONDITIONAL_ASSEMBLY (); |
s = --input_line_pointer; |
c = get_symbol_end (); /* name's delimiter. */ |
/* C is character after symbol. |
That character's place in the input line is now '\0'. |
S points to the beginning of the symbol. |
[In case of pseudo-op, s->'.'.] |
Input_line_pointer->'\0' where c was. */ |
if (TC_START_LABEL (c, s, input_line_pointer)) |
{ |
if (flag_m68k_mri) |
{ |
char *rest = input_line_pointer + 1; |
/* In MRI mode, \tsym: set 0 is permitted. */ |
if (*rest == ':') |
++rest; |
if (*rest == ' ' || *rest == '\t') |
++rest; |
if ((strncasecmp (rest, "EQU", 3) == 0 |
|| strncasecmp (rest, "SET", 3) == 0) |
&& (rest[3] == ' ' || rest[3] == '\t')) |
{ |
input_line_pointer = rest + 3; |
equals (s, 1); |
continue; |
} |
} |
line_label = colon (s); /* User-defined label. */ |
/* Put ':' back for error messages' sake. */ |
*input_line_pointer++ = ':'; |
#ifdef tc_check_label |
tc_check_label (line_label); |
#endif |
/* Input_line_pointer->after ':'. */ |
SKIP_WHITESPACE (); |
} |
else if ((c == '=' && input_line_pointer[1] == '=') |
|| ((c == ' ' || c == '\t') |
&& input_line_pointer[1] == '=' |
&& input_line_pointer[2] == '=')) |
{ |
equals (s, -1); |
demand_empty_rest_of_line (); |
} |
else if ((c == '=' |
|| ((c == ' ' || c == '\t') |
&& input_line_pointer[1] == '=')) |
#ifdef TC_EQUAL_IN_INSN |
&& !TC_EQUAL_IN_INSN (c, s) |
#endif |
) |
{ |
equals (s, 1); |
demand_empty_rest_of_line (); |
} |
else |
{ |
/* Expect pseudo-op or machine instruction. */ |
pop = NULL; |
#ifndef TC_CASE_SENSITIVE |
{ |
char *s2 = s; |
strncpy (original_case_string, s2, sizeof (original_case_string)); |
original_case_string[sizeof (original_case_string) - 1] = 0; |
while (*s2) |
{ |
*s2 = TOLOWER (*s2); |
s2++; |
} |
} |
#endif |
if (NO_PSEUDO_DOT || flag_m68k_mri) |
{ |
/* The MRI assembler uses pseudo-ops without |
a period. */ |
pop = (pseudo_typeS *) hash_find (po_hash, s); |
if (pop != NULL && pop->poc_handler == NULL) |
pop = NULL; |
} |
if (pop != NULL |
|| (!flag_m68k_mri && *s == '.')) |
{ |
/* PSEUDO - OP. |
WARNING: c has next char, which may be end-of-line. |
We lookup the pseudo-op table with s+1 because we |
already know that the pseudo-op begins with a '.'. */ |
if (pop == NULL) |
pop = (pseudo_typeS *) hash_find (po_hash, s + 1); |
if (pop && !pop->poc_handler) |
pop = NULL; |
/* In MRI mode, we may need to insert an |
automatic alignment directive. What a hack |
this is. */ |
if (mri_pending_align |
&& (pop == NULL |
|| !((pop->poc_handler == cons |
&& pop->poc_val == 1) |
|| (pop->poc_handler == s_space |
&& pop->poc_val == 1) |
#ifdef tc_conditional_pseudoop |
|| tc_conditional_pseudoop (pop) |
#endif |
|| pop->poc_handler == s_if |
|| pop->poc_handler == s_ifdef |
|| pop->poc_handler == s_ifc |
|| pop->poc_handler == s_ifeqs |
|| pop->poc_handler == s_else |
|| pop->poc_handler == s_endif |
|| pop->poc_handler == s_globl |
|| pop->poc_handler == s_ignore))) |
{ |
do_align (1, (char *) NULL, 0, 0); |
mri_pending_align = 0; |
if (line_label != NULL) |
{ |
symbol_set_frag (line_label, frag_now); |
S_SET_VALUE (line_label, frag_now_fix ()); |
} |
} |
/* Print the error msg now, while we still can. */ |
if (pop == NULL) |
{ |
char *end = input_line_pointer; |
*input_line_pointer = c; |
s_ignore (0); |
c = *--input_line_pointer; |
*input_line_pointer = '\0'; |
if (! macro_defined || ! try_macro (c, s)) |
{ |
*end = '\0'; |
as_bad (_("unknown pseudo-op: `%s'"), s); |
*input_line_pointer++ = c; |
} |
continue; |
} |
/* Put it back for error messages etc. */ |
*input_line_pointer = c; |
/* The following skip of whitespace is compulsory. |
A well shaped space is sometimes all that separates |
keyword from operands. */ |
if (c == ' ' || c == '\t') |
input_line_pointer++; |
/* Input_line is restored. |
Input_line_pointer->1st non-blank char |
after pseudo-operation. */ |
(*pop->poc_handler) (pop->poc_val); |
/* If that was .end, just get out now. */ |
if (pop->poc_handler == s_end) |
goto quit; |
} |
else |
{ |
/* WARNING: c has char, which may be end-of-line. */ |
/* Also: input_line_pointer->`\0` where c was. */ |
*input_line_pointer = c; |
input_line_pointer = _find_end_of_line (input_line_pointer, flag_m68k_mri, 1, 0); |
c = *input_line_pointer; |
*input_line_pointer = '\0'; |
generate_lineno_debug (); |
if (macro_defined && try_macro (c, s)) |
continue; |
if (mri_pending_align) |
{ |
do_align (1, (char *) NULL, 0, 0); |
mri_pending_align = 0; |
if (line_label != NULL) |
{ |
symbol_set_frag (line_label, frag_now); |
S_SET_VALUE (line_label, frag_now_fix ()); |
} |
} |
assemble_one (s); /* Assemble 1 instruction. */ |
*input_line_pointer++ = c; |
/* We resume loop AFTER the end-of-line from |
this instruction. */ |
} |
} |
continue; |
} |
/* Empty statement? */ |
if (is_end_of_line[(unsigned char) c]) |
continue; |
if ((LOCAL_LABELS_DOLLAR || LOCAL_LABELS_FB) && ISDIGIT (c)) |
{ |
/* local label ("4:") */ |
char *backup = input_line_pointer; |
HANDLE_CONDITIONAL_ASSEMBLY (); |
temp = c - '0'; |
/* Read the whole number. */ |
while (ISDIGIT (*input_line_pointer)) |
{ |
temp = (temp * 10) + *input_line_pointer - '0'; |
++input_line_pointer; |
} |
if (LOCAL_LABELS_DOLLAR |
&& *input_line_pointer == '$' |
&& *(input_line_pointer + 1) == ':') |
{ |
input_line_pointer += 2; |
if (dollar_label_defined (temp)) |
{ |
as_fatal (_("label \"%d$\" redefined"), temp); |
} |
define_dollar_label (temp); |
colon (dollar_label_name (temp, 0)); |
continue; |
} |
if (LOCAL_LABELS_FB |
&& *input_line_pointer++ == ':') |
{ |
fb_label_instance_inc (temp); |
colon (fb_label_name (temp, 0)); |
continue; |
} |
input_line_pointer = backup; |
} /* local label ("4:") */ |
if (c && strchr (line_comment_chars, c)) |
{ /* Its a comment. Better say APP or NO_APP. */ |
sb sbuf; |
char *ends; |
char *new_buf; |
char *new_tmp; |
unsigned int new_length; |
char *tmp_buf = 0; |
s = input_line_pointer; |
if (strncmp (s, "APP\n", 4)) |
{ |
/* We ignore it. */ |
ignore_rest_of_line (); |
continue; |
} |
bump_line_counters (); |
s += 4; |
ends = strstr (s, "#NO_APP\n"); |
if (!ends) |
{ |
unsigned int tmp_len; |
unsigned int num; |
/* The end of the #APP wasn't in this buffer. We |
keep reading in buffers until we find the #NO_APP |
that goes with this #APP There is one. The specs |
guarantee it... */ |
tmp_len = buffer_limit - s; |
tmp_buf = (char *) xmalloc (tmp_len + 1); |
memcpy (tmp_buf, s, tmp_len); |
do |
{ |
new_tmp = input_scrub_next_buffer (&buffer); |
if (!new_tmp) |
break; |
else |
buffer_limit = new_tmp; |
input_line_pointer = buffer; |
ends = strstr (buffer, "#NO_APP\n"); |
if (ends) |
num = ends - buffer; |
else |
num = buffer_limit - buffer; |
tmp_buf = (char *) xrealloc (tmp_buf, tmp_len + num); |
memcpy (tmp_buf + tmp_len, buffer, num); |
tmp_len += num; |
} |
while (!ends); |
input_line_pointer = ends ? ends + 8 : NULL; |
s = tmp_buf; |
ends = s + tmp_len; |
} |
else |
{ |
input_line_pointer = ends + 8; |
} |
scrub_string = s; |
scrub_string_end = ends; |
new_length = ends - s; |
new_buf = (char *) xmalloc (new_length); |
new_tmp = new_buf; |
for (;;) |
{ |
size_t space; |
size_t size; |
space = (new_buf + new_length) - new_tmp; |
size = do_scrub_chars (scrub_from_string, new_tmp, space); |
if (size < space) |
{ |
new_tmp[size] = 0; |
break; |
} |
new_buf = (char *) xrealloc (new_buf, new_length + 100); |
new_tmp = new_buf + new_length; |
new_length += 100; |
} |
if (tmp_buf) |
free (tmp_buf); |
/* We've "scrubbed" input to the preferred format. In the |
process we may have consumed the whole of the remaining |
file (and included files). We handle this formatted |
input similar to that of macro expansion, letting |
actual macro expansion (possibly nested) and other |
input expansion work. Beware that in messages, line |
numbers and possibly file names will be incorrect. */ |
new_length = strlen (new_buf); |
sb_build (&sbuf, new_length); |
sb_add_buffer (&sbuf, new_buf, new_length); |
input_scrub_include_sb (&sbuf, input_line_pointer, 0); |
sb_kill (&sbuf); |
buffer_limit = input_scrub_next_buffer (&input_line_pointer); |
free (new_buf); |
continue; |
} |
HANDLE_CONDITIONAL_ASSEMBLY (); |
#ifdef tc_unrecognized_line |
if (tc_unrecognized_line (c)) |
continue; |
#endif |
input_line_pointer--; |
/* Report unknown char as error. */ |
demand_empty_rest_of_line (); |
} |
} |
quit: |
symbol_set_value_now (&dot_symbol); |
#ifdef HANDLE_BUNDLE |
if (bundle_lock_frag != NULL) |
{ |
as_bad_where (bundle_lock_frag->fr_file, bundle_lock_frag->fr_line, |
_(".bundle_lock with no matching .bundle_unlock")); |
bundle_lock_frag = NULL; |
bundle_lock_frchain = NULL; |
bundle_lock_depth = 0; |
} |
#endif |
#ifdef md_cleanup |
md_cleanup (); |
#endif |
/* Close the input file. */ |
input_scrub_close (); |
#ifdef WARN_COMMENTS |
{ |
if (warn_comment && found_comment) |
as_warn_where (found_comment_file, found_comment, |
"first comment found here"); |
} |
#endif |
} |
/* Convert O_constant expression EXP into the equivalent O_big representation. |
Take the sign of the number from SIGN rather than X_add_number. */ |
static void |
convert_to_bignum (expressionS *exp, int sign) |
{ |
valueT value; |
unsigned int i; |
value = exp->X_add_number; |
for (i = 0; i < sizeof (exp->X_add_number) / CHARS_PER_LITTLENUM; i++) |
{ |
generic_bignum[i] = value & LITTLENUM_MASK; |
value >>= LITTLENUM_NUMBER_OF_BITS; |
} |
/* Add a sequence of sign bits if the top bit of X_add_number is not |
the sign of the original value. */ |
if ((exp->X_add_number < 0) == !sign) |
generic_bignum[i++] = sign ? LITTLENUM_MASK : 0; |
exp->X_op = O_big; |
exp->X_add_number = i; |
} |
/* For most MRI pseudo-ops, the line actually ends at the first |
nonquoted space. This function looks for that point, stuffs a null |
in, and sets *STOPCP to the character that used to be there, and |
returns the location. |
Until I hear otherwise, I am going to assume that this is only true |
for the m68k MRI assembler. */ |
char * |
mri_comment_field (char *stopcp) |
{ |
char *s; |
#ifdef TC_M68K |
int inquote = 0; |
know (flag_m68k_mri); |
for (s = input_line_pointer; |
((!is_end_of_line[(unsigned char) *s] && *s != ' ' && *s != '\t') |
|| inquote); |
s++) |
{ |
if (*s == '\'') |
inquote = !inquote; |
} |
#else |
for (s = input_line_pointer; |
!is_end_of_line[(unsigned char) *s]; |
s++) |
; |
#endif |
*stopcp = *s; |
*s = '\0'; |
return s; |
} |
/* Skip to the end of an MRI comment field. */ |
void |
mri_comment_end (char *stop, int stopc) |
{ |
know (flag_mri); |
input_line_pointer = stop; |
*stop = stopc; |
while (!is_end_of_line[(unsigned char) *input_line_pointer]) |
++input_line_pointer; |
} |
void |
s_abort (int ignore ATTRIBUTE_UNUSED) |
{ |
as_fatal (_(".abort detected. Abandoning ship.")); |
} |
/* Guts of .align directive. N is the power of two to which to align. |
FILL may be NULL, or it may point to the bytes of the fill pattern. |
LEN is the length of whatever FILL points to, if anything. MAX is |
the maximum number of characters to skip when doing the alignment, |
or 0 if there is no maximum. */ |
static void |
do_align (int n, char *fill, int len, int max) |
{ |
if (now_seg == absolute_section) |
{ |
if (fill != NULL) |
while (len-- > 0) |
if (*fill++ != '\0') |
{ |
as_warn (_("ignoring fill value in absolute section")); |
break; |
} |
fill = NULL; |
len = 0; |
} |
#ifdef md_flush_pending_output |
md_flush_pending_output (); |
#endif |
#ifdef md_do_align |
md_do_align (n, fill, len, max, just_record_alignment); |
#endif |
/* Only make a frag if we HAVE to... */ |
if (n != 0 && !need_pass_2) |
{ |
if (fill == NULL) |
{ |
if (subseg_text_p (now_seg)) |
frag_align_code (n, max); |
else |
frag_align (n, 0, max); |
} |
else if (len <= 1) |
frag_align (n, *fill, max); |
else |
frag_align_pattern (n, fill, len, max); |
} |
#ifdef md_do_align |
just_record_alignment: ATTRIBUTE_UNUSED_LABEL |
#endif |
record_alignment (now_seg, n - OCTETS_PER_BYTE_POWER); |
} |
/* Handle the .align pseudo-op. A positive ARG is a default alignment |
(in bytes). A negative ARG is the negative of the length of the |
fill pattern. BYTES_P is non-zero if the alignment value should be |
interpreted as the byte boundary, rather than the power of 2. */ |
#ifndef TC_ALIGN_LIMIT |
#define TC_ALIGN_LIMIT (stdoutput->arch_info->bits_per_address - 1) |
#endif |
static void |
s_align (int arg, int bytes_p) |
{ |
unsigned int align_limit = TC_ALIGN_LIMIT; |
unsigned int align; |
char *stop = NULL; |
char stopc = 0; |
offsetT fill = 0; |
int max; |
int fill_p; |
if (flag_mri) |
stop = mri_comment_field (&stopc); |
if (is_end_of_line[(unsigned char) *input_line_pointer]) |
{ |
if (arg < 0) |
align = 0; |
else |
align = arg; /* Default value from pseudo-op table. */ |
} |
else |
{ |
align = get_absolute_expression (); |
SKIP_WHITESPACE (); |
} |
if (bytes_p) |
{ |
/* Convert to a power of 2. */ |
if (align != 0) |
{ |
unsigned int i; |
for (i = 0; (align & 1) == 0; align >>= 1, ++i) |
; |
if (align != 1) |
as_bad (_("alignment not a power of 2")); |
align = i; |
} |
} |
if (align > align_limit) |
{ |
align = align_limit; |
as_warn (_("alignment too large: %u assumed"), align); |
} |
if (*input_line_pointer != ',') |
{ |
fill_p = 0; |
max = 0; |
} |
else |
{ |
++input_line_pointer; |
if (*input_line_pointer == ',') |
fill_p = 0; |
else |
{ |
fill = get_absolute_expression (); |
SKIP_WHITESPACE (); |
fill_p = 1; |
} |
if (*input_line_pointer != ',') |
max = 0; |
else |
{ |
++input_line_pointer; |
max = get_absolute_expression (); |
} |
} |
if (!fill_p) |
{ |
if (arg < 0) |
as_warn (_("expected fill pattern missing")); |
do_align (align, (char *) NULL, 0, max); |
} |
else |
{ |
int fill_len; |
if (arg >= 0) |
fill_len = 1; |
else |
fill_len = -arg; |
if (fill_len <= 1) |
{ |
char fill_char; |
fill_char = fill; |
do_align (align, &fill_char, fill_len, max); |
} |
else |
{ |
char ab[16]; |
if ((size_t) fill_len > sizeof ab) |
abort (); |
md_number_to_chars (ab, fill, fill_len); |
do_align (align, ab, fill_len, max); |
} |
} |
demand_empty_rest_of_line (); |
if (flag_mri) |
mri_comment_end (stop, stopc); |
} |
/* Handle the .align pseudo-op on machines where ".align 4" means |
align to a 4 byte boundary. */ |
void |
s_align_bytes (int arg) |
{ |
s_align (arg, 1); |
} |
/* Handle the .align pseudo-op on machines where ".align 4" means align |
to a 2**4 boundary. */ |
void |
s_align_ptwo (int arg) |
{ |
s_align (arg, 0); |
} |
/* Switch in and out of alternate macro mode. */ |
void |
s_altmacro (int on) |
{ |
demand_empty_rest_of_line (); |
macro_set_alternate (on); |
} |
/* Read a symbol name from input_line_pointer. |
Stores the symbol name in a buffer and returns a pointer to this buffer. |
The buffer is xalloc'ed. It is the caller's responsibility to free |
this buffer. |
The name is not left in the i_l_p buffer as it may need processing |
to handle escape characters. |
Advances i_l_p to the next non-whitespace character. |
If a symbol name could not be read, the routine issues an error |
messages, skips to the end of the line and returns NULL. */ |
static char * |
read_symbol_name (void) |
{ |
char * name; |
char * start; |
char c; |
c = *input_line_pointer++; |
if (c == '"') |
{ |
#define SYM_NAME_CHUNK_LEN 128 |
ptrdiff_t len = SYM_NAME_CHUNK_LEN; |
char * name_end; |
unsigned int C; |
start = name = xmalloc (len + 1); |
name_end = name + SYM_NAME_CHUNK_LEN; |
while (is_a_char (C = next_char_of_string ())) |
{ |
if (name >= name_end) |
{ |
ptrdiff_t sofar; |
sofar = name - start; |
len += SYM_NAME_CHUNK_LEN; |
start = xrealloc (start, len + 1); |
name_end = start + len; |
name = start + sofar; |
} |
*name++ = (char) C; |
} |
*name = 0; |
/* Since quoted symbol names can contain non-ASCII characters, |
check the string and warn if it cannot be recognised by the |
current character set. */ |
if (mbstowcs (NULL, name, len) == (size_t) -1) |
as_warn (_("symbol name not recognised in the current locale")); |
} |
else if (is_name_beginner (c) || c == '\001') |
{ |
ptrdiff_t len; |
name = input_line_pointer - 1; |
/* We accept \001 in a name in case this is |
being called with a constructed string. */ |
while (is_part_of_name (c = *input_line_pointer++) |
|| c == '\001') |
; |
len = (input_line_pointer - name) - 1; |
start = xmalloc (len + 1); |
memcpy (start, name, len); |
start[len] = 0; |
/* Skip a name ender char if one is present. */ |
if (! is_name_ender (c)) |
--input_line_pointer; |
} |
else |
name = start = NULL; |
if (name == start) |
{ |
as_bad (_("expected symbol name")); |
ignore_rest_of_line (); |
return NULL; |
} |
SKIP_WHITESPACE (); |
return start; |
} |
symbolS * |
s_comm_internal (int param, |
symbolS *(*comm_parse_extra) (int, symbolS *, addressT)) |
{ |
char *name; |
offsetT temp, size; |
symbolS *symbolP = NULL; |
char *stop = NULL; |
char stopc = 0; |
expressionS exp; |
if (flag_mri) |
stop = mri_comment_field (&stopc); |
if ((name = read_symbol_name ()) == NULL) |
goto out; |
/* Accept an optional comma after the name. The comma used to be |
required, but Irix 5 cc does not generate it for .lcomm. */ |
if (*input_line_pointer == ',') |
input_line_pointer++; |
temp = get_absolute_expr (&exp); |
size = temp; |
size &= ((offsetT) 2 << (stdoutput->arch_info->bits_per_address - 1)) - 1; |
if (exp.X_op == O_absent) |
{ |
as_bad (_("missing size expression")); |
ignore_rest_of_line (); |
goto out; |
} |
else if (temp != size || !exp.X_unsigned) |
{ |
as_warn (_("size (%ld) out of range, ignored"), (long) temp); |
ignore_rest_of_line (); |
goto out; |
} |
symbolP = symbol_find_or_make (name); |
if ((S_IS_DEFINED (symbolP) || symbol_equated_p (symbolP)) |
&& !S_IS_COMMON (symbolP)) |
{ |
if (!S_IS_VOLATILE (symbolP)) |
{ |
symbolP = NULL; |
as_bad (_("symbol `%s' is already defined"), name); |
ignore_rest_of_line (); |
goto out; |
} |
symbolP = symbol_clone (symbolP, 1); |
S_SET_SEGMENT (symbolP, undefined_section); |
S_SET_VALUE (symbolP, 0); |
symbol_set_frag (symbolP, &zero_address_frag); |
S_CLEAR_VOLATILE (symbolP); |
} |
size = S_GET_VALUE (symbolP); |
if (size == 0) |
size = temp; |
else if (size != temp) |
as_warn (_("size of \"%s\" is already %ld; not changing to %ld"), |
name, (long) size, (long) temp); |
if (comm_parse_extra != NULL) |
symbolP = (*comm_parse_extra) (param, symbolP, size); |
else |
{ |
S_SET_VALUE (symbolP, (valueT) size); |
S_SET_EXTERNAL (symbolP); |
S_SET_SEGMENT (symbolP, bfd_com_section_ptr); |
} |
demand_empty_rest_of_line (); |
out: |
if (flag_mri) |
mri_comment_end (stop, stopc); |
if (name != NULL) |
free (name); |
return symbolP; |
} |
void |
s_comm (int ignore) |
{ |
s_comm_internal (ignore, NULL); |
} |
/* The MRI COMMON pseudo-op. We handle this by creating a common |
symbol with the appropriate name. We make s_space do the right |
thing by increasing the size. */ |
void |
s_mri_common (int small ATTRIBUTE_UNUSED) |
{ |
char *name; |
char c; |
char *alc = NULL; |
symbolS *sym; |
offsetT align; |
char *stop = NULL; |
char stopc = 0; |
if (!flag_mri) |
{ |
s_comm (0); |
return; |
} |
stop = mri_comment_field (&stopc); |
SKIP_WHITESPACE (); |
name = input_line_pointer; |
if (!ISDIGIT (*name)) |
c = get_symbol_end (); |
else |
{ |
do |
{ |
++input_line_pointer; |
} |
while (ISDIGIT (*input_line_pointer)); |
c = *input_line_pointer; |
*input_line_pointer = '\0'; |
if (line_label != NULL) |
{ |
alc = (char *) xmalloc (strlen (S_GET_NAME (line_label)) |
+ (input_line_pointer - name) |
+ 1); |
sprintf (alc, "%s%s", name, S_GET_NAME (line_label)); |
name = alc; |
} |
} |
sym = symbol_find_or_make (name); |
*input_line_pointer = c; |
if (alc != NULL) |
free (alc); |
if (*input_line_pointer != ',') |
align = 0; |
else |
{ |
++input_line_pointer; |
align = get_absolute_expression (); |
} |
if (S_IS_DEFINED (sym) && !S_IS_COMMON (sym)) |
{ |
as_bad (_("symbol `%s' is already defined"), S_GET_NAME (sym)); |
ignore_rest_of_line (); |
mri_comment_end (stop, stopc); |
return; |
} |
S_SET_EXTERNAL (sym); |
S_SET_SEGMENT (sym, bfd_com_section_ptr); |
mri_common_symbol = sym; |
#ifdef S_SET_ALIGN |
if (align != 0) |
S_SET_ALIGN (sym, align); |
#else |
(void) align; |
#endif |
if (line_label != NULL) |
{ |
expressionS exp; |
exp.X_op = O_symbol; |
exp.X_add_symbol = sym; |
exp.X_add_number = 0; |
symbol_set_value_expression (line_label, &exp); |
symbol_set_frag (line_label, &zero_address_frag); |
S_SET_SEGMENT (line_label, expr_section); |
} |
/* FIXME: We just ignore the small argument, which distinguishes |
COMMON and COMMON.S. I don't know what we can do about it. */ |
/* Ignore the type and hptype. */ |
if (*input_line_pointer == ',') |
input_line_pointer += 2; |
if (*input_line_pointer == ',') |
input_line_pointer += 2; |
demand_empty_rest_of_line (); |
mri_comment_end (stop, stopc); |
} |
void |
s_data (int ignore ATTRIBUTE_UNUSED) |
{ |
segT section; |
int temp; |
temp = get_absolute_expression (); |
if (flag_readonly_data_in_text) |
{ |
section = text_section; |
temp += 1000; |
} |
else |
section = data_section; |
subseg_set (section, (subsegT) temp); |
demand_empty_rest_of_line (); |
} |
/* Handle the .appfile pseudo-op. This is automatically generated by |
do_scrub_chars when a preprocessor # line comment is seen with a |
file name. This default definition may be overridden by the object |
or CPU specific pseudo-ops. This function is also the default |
definition for .file; the APPFILE argument is 1 for .appfile, 0 for |
.file. */ |
void |
s_app_file_string (char *file, int appfile ATTRIBUTE_UNUSED) |
{ |
#ifdef LISTING |
if (listing) |
listing_source_file (file); |
#endif |
register_dependency (file); |
#ifdef obj_app_file |
obj_app_file (file, appfile); |
#endif |
} |
void |
s_app_file (int appfile) |
{ |
char *s; |
int length; |
/* Some assemblers tolerate immediately following '"'. */ |
if ((s = demand_copy_string (&length)) != 0) |
{ |
int may_omit |
= (!new_logical_line_flags (s, -1, 1) && appfile); |
/* In MRI mode, the preprocessor may have inserted an extraneous |
backquote. */ |
if (flag_m68k_mri |
&& *input_line_pointer == '\'' |
&& is_end_of_line[(unsigned char) input_line_pointer[1]]) |
++input_line_pointer; |
demand_empty_rest_of_line (); |
if (!may_omit) |
s_app_file_string (s, appfile); |
} |
} |
static int |
get_linefile_number (int *flag) |
{ |
SKIP_WHITESPACE (); |
if (*input_line_pointer < '0' || *input_line_pointer > '9') |
return 0; |
*flag = get_absolute_expression (); |
return 1; |
} |
/* Handle the .appline pseudo-op. This is automatically generated by |
do_scrub_chars when a preprocessor # line comment is seen. This |
default definition may be overridden by the object or CPU specific |
pseudo-ops. */ |
void |
s_app_line (int appline) |
{ |
char *file = NULL; |
int l; |
/* The given number is that of the next line. */ |
if (appline) |
l = get_absolute_expression (); |
else if (!get_linefile_number (&l)) |
{ |
ignore_rest_of_line (); |
return; |
} |
l--; |
if (l < -1) |
/* Some of the back ends can't deal with non-positive line numbers. |
Besides, it's silly. GCC however will generate a line number of |
zero when it is pre-processing builtins for assembler-with-cpp files: |
# 0 "<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 (¬es, c); |
len++; |
} |
/* JF this next line is so demand_copy_C_string will return a |
null terminated string. */ |
obstack_1grow (¬es, '\0'); |
retval = (char *) obstack_finish (¬es); |
} |
else |
{ |
as_bad (_("missing string")); |
retval = NULL; |
ignore_rest_of_line (); |
} |
*lenP = len; |
return (retval); |
} |
/* In: Input_line_pointer->next character. |
Do: Skip input_line_pointer over all whitespace. |
Out: 1 if input_line_pointer->end-of-line. */ |
int |
is_it_end_of_statement (void) |
{ |
SKIP_WHITESPACE (); |
return (is_end_of_line[(unsigned char) *input_line_pointer]); |
} |
void |
equals (char *sym_name, int reassign) |
{ |
char *stop = NULL; |
char stopc = 0; |
input_line_pointer++; |
if (*input_line_pointer == '=') |
input_line_pointer++; |
if (reassign < 0 && *input_line_pointer == '=') |
input_line_pointer++; |
while (*input_line_pointer == ' ' || *input_line_pointer == '\t') |
input_line_pointer++; |
if (flag_mri) |
stop = mri_comment_field (&stopc); |
assign_symbol (sym_name, reassign >= 0 ? !reassign : reassign); |
if (flag_mri) |
{ |
demand_empty_rest_of_line (); |
mri_comment_end (stop, stopc); |
} |
} |
/* .incbin -- include a file verbatim at the current location. */ |
void |
s_incbin (int x ATTRIBUTE_UNUSED) |
{ |
FILE * binfile; |
char * path; |
char * filename; |
char * binfrag; |
long skip = 0; |
long count = 0; |
long bytes; |
int len; |
#ifdef md_flush_pending_output |
md_flush_pending_output (); |
#endif |
#ifdef md_cons_align |
md_cons_align (1); |
#endif |
SKIP_WHITESPACE (); |
filename = demand_copy_string (& len); |
if (filename == NULL) |
return; |
SKIP_WHITESPACE (); |
/* Look for optional skip and count. */ |
if (* input_line_pointer == ',') |
{ |
++ input_line_pointer; |
skip = get_absolute_expression (); |
SKIP_WHITESPACE (); |
if (* input_line_pointer == ',') |
{ |
++ input_line_pointer; |
count = get_absolute_expression (); |
if (count == 0) |
as_warn (_(".incbin count zero, ignoring `%s'"), filename); |
SKIP_WHITESPACE (); |
} |
} |
demand_empty_rest_of_line (); |
/* Try opening absolute path first, then try include dirs. */ |
binfile = fopen (filename, FOPEN_RB); |
if (binfile == NULL) |
{ |
int i; |
path = (char *) xmalloc ((unsigned long) len + include_dir_maxlen + 5); |
for (i = 0; i < include_dir_count; i++) |
{ |
sprintf (path, "%s/%s", include_dirs[i], filename); |
binfile = fopen (path, FOPEN_RB); |
if (binfile != NULL) |
break; |
} |
if (binfile == NULL) |
as_bad (_("file not found: %s"), filename); |
} |
else |
path = xstrdup (filename); |
if (binfile) |
{ |
long file_len; |
register_dependency (path); |
/* Compute the length of the file. */ |
if (fseek (binfile, 0, SEEK_END) != 0) |
{ |
as_bad (_("seek to end of .incbin file failed `%s'"), path); |
goto done; |
} |
file_len = ftell (binfile); |
/* If a count was not specified use the remainder of the file. */ |
if (count == 0) |
count = file_len - skip; |
if (skip < 0 || count < 0 || file_len < 0 || skip + count > file_len) |
{ |
as_bad (_("skip (%ld) or count (%ld) invalid for file size (%ld)"), |
skip, count, file_len); |
goto done; |
} |
if (fseek (binfile, skip, SEEK_SET) != 0) |
{ |
as_bad (_("could not skip to %ld in file `%s'"), skip, path); |
goto done; |
} |
/* Allocate frag space and store file contents in it. */ |
binfrag = frag_more (count); |
bytes = fread (binfrag, 1, count, binfile); |
if (bytes < count) |
as_warn (_("truncated file `%s', %ld of %ld bytes read"), |
path, bytes, count); |
} |
done: |
if (binfile != NULL) |
fclose (binfile); |
if (path) |
free (path); |
} |
/* .include -- include a file at this point. */ |
void |
s_include (int arg ATTRIBUTE_UNUSED) |
{ |
char *filename; |
int i; |
FILE *try_file; |
char *path; |
if (!flag_m68k_mri) |
{ |
filename = demand_copy_string (&i); |
if (filename == NULL) |
{ |
/* demand_copy_string has already printed an error and |
called ignore_rest_of_line. */ |
return; |
} |
} |
else |
{ |
SKIP_WHITESPACE (); |
i = 0; |
while (!is_end_of_line[(unsigned char) *input_line_pointer] |
&& *input_line_pointer != ' ' |
&& *input_line_pointer != '\t') |
{ |
obstack_1grow (¬es, *input_line_pointer); |
++input_line_pointer; |
++i; |
} |
obstack_1grow (¬es, '\0'); |
filename = (char *) obstack_finish (¬es); |
while (!is_end_of_line[(unsigned char) *input_line_pointer]) |
++input_line_pointer; |
} |
demand_empty_rest_of_line (); |
path = (char *) xmalloc ((unsigned long) i |
+ include_dir_maxlen + 5 /* slop */ ); |
for (i = 0; i < include_dir_count; i++) |
{ |
strcpy (path, include_dirs[i]); |
strcat (path, "/"); |
strcat (path, filename); |
if (0 != (try_file = fopen (path, FOPEN_RT))) |
{ |
fclose (try_file); |
goto gotit; |
} |
} |
free (path); |
path = filename; |
gotit: |
/* malloc Storage leak when file is found on path. FIXME-SOMEDAY. */ |
register_dependency (path); |
input_scrub_insert_file (path); |
} |
void |
add_include_dir (char *path) |
{ |
int i; |
if (include_dir_count == 0) |
{ |
include_dirs = (char **) xmalloc (2 * sizeof (*include_dirs)); |
include_dirs[0] = "."; /* Current dir. */ |
include_dir_count = 2; |
} |
else |
{ |
include_dir_count++; |
include_dirs = |
(char **) realloc (include_dirs, |
include_dir_count * sizeof (*include_dirs)); |
} |
include_dirs[include_dir_count - 1] = path; /* New one. */ |
i = strlen (path); |
if (i > include_dir_maxlen) |
include_dir_maxlen = i; |
} |
/* Output debugging information to denote the source file. */ |
static void |
generate_file_debug (void) |
{ |
if (debug_type == DEBUG_STABS) |
stabs_generate_asm_file (); |
} |
/* Output line number debugging information for the current source line. */ |
void |
generate_lineno_debug (void) |
{ |
switch (debug_type) |
{ |
case DEBUG_UNSPECIFIED: |
case DEBUG_NONE: |
case DEBUG_DWARF: |
break; |
case DEBUG_STABS: |
stabs_generate_asm_lineno (); |
break; |
case DEBUG_ECOFF: |
ecoff_generate_asm_lineno (); |
break; |
case DEBUG_DWARF2: |
/* ??? We could here indicate to dwarf2dbg.c that something |
has changed. However, since there is additional backend |
support that is required (calling dwarf2_emit_insn), we |
let dwarf2dbg.c call as_where on its own. */ |
break; |
} |
} |
/* Output debugging information to mark a function entry point or end point. |
END_P is zero for .func, and non-zero for .endfunc. */ |
void |
s_func (int end_p) |
{ |
do_s_func (end_p, NULL); |
} |
/* Subroutine of s_func so targets can choose a different default prefix. |
If DEFAULT_PREFIX is NULL, use the target's "leading char". */ |
static void |
do_s_func (int end_p, const char *default_prefix) |
{ |
/* Record the current function so that we can issue an error message for |
misplaced .func,.endfunc, and also so that .endfunc needs no |
arguments. */ |
static char *current_name; |
static char *current_label; |
if (end_p) |
{ |
if (current_name == NULL) |
{ |
as_bad (_("missing .func")); |
ignore_rest_of_line (); |
return; |
} |
if (debug_type == DEBUG_STABS) |
stabs_generate_asm_endfunc (current_name, current_label); |
current_name = current_label = NULL; |
} |
else /* ! end_p */ |
{ |
char *name, *label; |
char delim1, delim2; |
if (current_name != NULL) |
{ |
as_bad (_(".endfunc missing for previous .func")); |
ignore_rest_of_line (); |
return; |
} |
name = input_line_pointer; |
delim1 = get_symbol_end (); |
name = xstrdup (name); |
*input_line_pointer = delim1; |
SKIP_WHITESPACE (); |
if (*input_line_pointer != ',') |
{ |
if (default_prefix) |
{ |
if (asprintf (&label, "%s%s", default_prefix, name) == -1) |
as_fatal ("%s", xstrerror (errno)); |
} |
else |
{ |
char leading_char = bfd_get_symbol_leading_char (stdoutput); |
/* Missing entry point, use function's name with the leading |
char prepended. */ |
if (leading_char) |
{ |
if (asprintf (&label, "%c%s", leading_char, name) == -1) |
as_fatal ("%s", xstrerror (errno)); |
} |
else |
label = name; |
} |
} |
else |
{ |
++input_line_pointer; |
SKIP_WHITESPACE (); |
label = input_line_pointer; |
delim2 = get_symbol_end (); |
label = xstrdup (label); |
*input_line_pointer = delim2; |
} |
if (debug_type == DEBUG_STABS) |
stabs_generate_asm_func (name, label); |
current_name = name; |
current_label = label; |
} |
demand_empty_rest_of_line (); |
} |
#ifdef HANDLE_BUNDLE |
void |
s_bundle_align_mode (int arg ATTRIBUTE_UNUSED) |
{ |
unsigned int align = get_absolute_expression (); |
SKIP_WHITESPACE (); |
demand_empty_rest_of_line (); |
if (align > (unsigned int) TC_ALIGN_LIMIT) |
as_fatal (_(".bundle_align_mode alignment too large (maximum %u)"), |
(unsigned int) TC_ALIGN_LIMIT); |
if (bundle_lock_frag != NULL) |
{ |
as_bad (_("cannot change .bundle_align_mode inside .bundle_lock")); |
return; |
} |
bundle_align_p2 = align; |
} |
void |
s_bundle_lock (int arg ATTRIBUTE_UNUSED) |
{ |
demand_empty_rest_of_line (); |
if (bundle_align_p2 == 0) |
{ |
as_bad (_(".bundle_lock is meaningless without .bundle_align_mode")); |
return; |
} |
if (bundle_lock_depth == 0) |
{ |
bundle_lock_frchain = frchain_now; |
bundle_lock_frag = start_bundle (); |
} |
++bundle_lock_depth; |
} |
void |
s_bundle_unlock (int arg ATTRIBUTE_UNUSED) |
{ |
unsigned int size; |
demand_empty_rest_of_line (); |
if (bundle_lock_frag == NULL) |
{ |
as_bad (_(".bundle_unlock without preceding .bundle_lock")); |
return; |
} |
gas_assert (bundle_align_p2 > 0); |
gas_assert (bundle_lock_depth > 0); |
if (--bundle_lock_depth > 0) |
return; |
size = pending_bundle_size (bundle_lock_frag); |
if (size > (1U << bundle_align_p2)) |
as_bad (_(".bundle_lock sequence is %u bytes, but bundle size only %u"), |
size, 1 << bundle_align_p2); |
else |
finish_bundle (bundle_lock_frag, size); |
bundle_lock_frag = NULL; |
bundle_lock_frchain = NULL; |
} |
#endif /* HANDLE_BUNDLE */ |
void |
s_ignore (int arg ATTRIBUTE_UNUSED) |
{ |
ignore_rest_of_line (); |
} |
void |
read_print_statistics (FILE *file) |
{ |
hash_print_statistics (file, "pseudo-op table", po_hash); |
} |
/* Inserts the given line into the input stream. |
This call avoids macro/conditionals nesting checking, since the contents of |
the line are assumed to replace the contents of a line already scanned. |
An appropriate use of this function would be substitution of input lines when |
called by md_start_line_hook(). The given line is assumed to already be |
properly scrubbed. */ |
void |
input_scrub_insert_line (const char *line) |
{ |
sb newline; |
size_t len = strlen (line); |
sb_build (&newline, len); |
sb_add_buffer (&newline, line, len); |
input_scrub_include_sb (&newline, input_line_pointer, 0); |
sb_kill (&newline); |
buffer_limit = input_scrub_next_buffer (&input_line_pointer); |
} |
/* Insert a file into the input stream; the path must resolve to an actual |
file; no include path searching or dependency registering is performed. */ |
void |
input_scrub_insert_file (char *path) |
{ |
input_scrub_include_file (path, input_line_pointer); |
buffer_limit = input_scrub_next_buffer (&input_line_pointer); |
} |
/* Find the end of a line, considering quotation and escaping of quotes. */ |
#if !defined(TC_SINGLE_QUOTE_STRINGS) && defined(SINGLE_QUOTE_STRINGS) |
# define TC_SINGLE_QUOTE_STRINGS 1 |
#endif |
static char * |
_find_end_of_line (char *s, int mri_string, int insn ATTRIBUTE_UNUSED, |
int in_macro) |
{ |
char inquote = '\0'; |
int inescape = 0; |
while (!is_end_of_line[(unsigned char) *s] |
|| (inquote && !ISCNTRL (*s)) |
|| (inquote == '\'' && flag_mri) |
#ifdef TC_EOL_IN_INSN |
|| (insn && TC_EOL_IN_INSN (s)) |
#endif |
/* PR 6926: When we are parsing the body of a macro the sequence |
\@ is special - it refers to the invocation count. If the @ |
character happens to be registered as a line-separator character |
by the target, then the is_end_of_line[] test above will have |
returned true, but we need to ignore the line separating |
semantics in this particular case. */ |
|| (in_macro && inescape && *s == '@') |
) |
{ |
if (mri_string && *s == '\'') |
inquote ^= *s; |
else if (inescape) |
inescape = 0; |
else if (*s == '\\') |
inescape = 1; |
else if (!inquote |
? *s == '"' |
#ifdef TC_SINGLE_QUOTE_STRINGS |
|| (TC_SINGLE_QUOTE_STRINGS && *s == '\'') |
#endif |
: *s == inquote) |
inquote ^= *s; |
++s; |
} |
if (inquote) |
as_warn (_("missing closing `%c'"), inquote); |
if (inescape) |
as_warn (_("stray `\\'")); |
return s; |
} |
char * |
find_end_of_line (char *s, int mri_string) |
{ |
return _find_end_of_line (s, mri_string, 0, 0); |
} |
/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 (¬es, string); |
} |
/* At least for now, stabs in a special stab section are always |
output as 12 byte blocks of information. */ |
p = frag_more (8); |
md_number_to_chars (p, (valueT) stroff, 4); |
md_number_to_chars (p + 4, (valueT) type, 1); |
md_number_to_chars (p + 5, (valueT) other, 1); |
md_number_to_chars (p + 6, (valueT) desc, 2); |
if (what == 's' || what == 'n') |
{ |
/* Pick up the value from the input line. */ |
cons (4); |
input_line_pointer--; |
} |
else |
{ |
symbolS *symbol; |
expressionS exp; |
/* Arrange for a value representing the current location. */ |
symbol = symbol_temp_new (saved_seg, dot, saved_frag); |
exp.X_op = O_symbol; |
exp.X_add_symbol = symbol; |
exp.X_add_number = 0; |
emit_expr (&exp, 4); |
} |
#ifdef OBJ_PROCESS_STAB |
OBJ_PROCESS_STAB (seg, what, string, type, other, desc); |
#endif |
subseg_set (saved_seg, saved_subseg); |
} |
else |
{ |
#ifdef OBJ_PROCESS_STAB |
OBJ_PROCESS_STAB (0, what, string, type, other, desc); |
#else |
abort (); |
#endif |
} |
demand_empty_rest_of_line (); |
} |
/* Regular stab directive. */ |
void |
s_stab (int what) |
{ |
s_stab_generic (what, STAB_SECTION_NAME, STAB_STRING_SECTION_NAME); |
} |
/* "Extended stabs", used in Solaris only now. */ |
void |
s_xstab (int what) |
{ |
int length; |
char *stab_secname, *stabstr_secname; |
static char *saved_secname, *saved_strsecname; |
/* @@ MEMORY LEAK: This allocates a copy of the string, but in most |
cases it will be the same string, so we could release the storage |
back to the obstack it came from. */ |
stab_secname = demand_copy_C_string (&length); |
SKIP_WHITESPACE (); |
if (*input_line_pointer == ',') |
input_line_pointer++; |
else |
{ |
as_bad (_("comma missing in .xstabs")); |
ignore_rest_of_line (); |
return; |
} |
/* To get the name of the stab string section, simply add "str" to |
the stab section name. */ |
if (saved_secname == 0 || strcmp (saved_secname, stab_secname)) |
{ |
stabstr_secname = (char *) xmalloc (strlen (stab_secname) + 4); |
strcpy (stabstr_secname, stab_secname); |
strcat (stabstr_secname, "str"); |
if (saved_secname) |
{ |
free (saved_secname); |
free (saved_strsecname); |
} |
saved_secname = stab_secname; |
saved_strsecname = stabstr_secname; |
} |
s_stab_generic (what, saved_secname, saved_strsecname); |
} |
#ifdef S_SET_DESC |
/* Frob invented at RMS' request. Set the n_desc of a symbol. */ |
void |
s_desc (ignore) |
int ignore ATTRIBUTE_UNUSED; |
{ |
char *name; |
char c; |
char *p; |
symbolS *symbolP; |
int temp; |
name = input_line_pointer; |
c = get_symbol_end (); |
p = input_line_pointer; |
*p = c; |
SKIP_WHITESPACE (); |
if (*input_line_pointer != ',') |
{ |
*p = 0; |
as_bad (_("expected comma after \"%s\""), name); |
*p = c; |
ignore_rest_of_line (); |
} |
else |
{ |
input_line_pointer++; |
temp = get_absolute_expression (); |
*p = 0; |
symbolP = symbol_find_or_make (name); |
*p = c; |
S_SET_DESC (symbolP, temp); |
} |
demand_empty_rest_of_line (); |
} /* s_desc() */ |
#endif /* defined (S_SET_DESC) */ |
/* Generate stabs debugging information to denote the main source file. */ |
void |
stabs_generate_asm_file (void) |
{ |
char *file; |
unsigned int lineno; |
as_where (&file, &lineno); |
if (use_gnu_debug_info_extensions) |
{ |
const char *dir; |
char *dir2; |
dir = remap_debug_filename (getpwd ()); |
dir2 = (char *) alloca (strlen (dir) + 2); |
sprintf (dir2, "%s%s", dir, "/"); |
generate_asm_file (N_SO, dir2); |
xfree ((char *) dir); |
} |
generate_asm_file (N_SO, file); |
} |
/* Generate stabs debugging information to denote the source file. |
TYPE is one of N_SO, N_SOL. */ |
static void |
generate_asm_file (int type, char *file) |
{ |
static char *last_file; |
static int label_count; |
char *hold; |
char sym[30]; |
char *buf; |
char *tmp = file; |
char *file_endp = file + strlen (file); |
char *bufp; |
if (last_file != NULL |
&& filename_cmp (last_file, file) == 0) |
return; |
/* Rather than try to do this in some efficient fashion, we just |
generate a string and then parse it again. That lets us use the |
existing stabs hook, which expect to see a string, rather than |
inventing new ones. */ |
hold = input_line_pointer; |
sprintf (sym, "%sF%d", FAKE_LABEL_NAME, label_count); |
++label_count; |
/* Allocate enough space for the file name (possibly extended with |
doubled up backslashes), the symbol name, and the other characters |
that make up a stabs file directive. */ |
bufp = buf = (char *) xmalloc (2 * strlen (file) + strlen (sym) + 12); |
*bufp++ = '"'; |
while (tmp < file_endp) |
{ |
char *bslash = strchr (tmp, '\\'); |
size_t len = (bslash) ? (size_t) (bslash - tmp + 1) : strlen (tmp); |
/* Double all backslashes, since demand_copy_C_string (used by |
s_stab to extract the part in quotes) will try to replace them as |
escape sequences. backslash may appear in a filespec. */ |
strncpy (bufp, tmp, len); |
tmp += len; |
bufp += len; |
if (bslash != NULL) |
*bufp++ = '\\'; |
} |
sprintf (bufp, "\",%d,0,0,%s\n", type, sym); |
input_line_pointer = buf; |
s_stab ('s'); |
colon (sym); |
if (last_file != NULL) |
free (last_file); |
last_file = xstrdup (file); |
free (buf); |
input_line_pointer = hold; |
} |
/* Generate stabs debugging information for the current line. This is |
used to produce debugging information for an assembler file. */ |
void |
stabs_generate_asm_lineno (void) |
{ |
static int label_count; |
char *hold; |
char *file; |
unsigned int lineno; |
char *buf; |
char sym[30]; |
/* Remember the last file/line and avoid duplicates. */ |
static unsigned int prev_lineno = -1; |
static char *prev_file = NULL; |
/* Rather than try to do this in some efficient fashion, we just |
generate a string and then parse it again. That lets us use the |
existing stabs hook, which expect to see a string, rather than |
inventing new ones. */ |
hold = input_line_pointer; |
as_where (&file, &lineno); |
/* Don't emit sequences of stabs for the same line. */ |
if (prev_file == NULL) |
{ |
/* First time thru. */ |
prev_file = xstrdup (file); |
prev_lineno = lineno; |
} |
else if (lineno == prev_lineno |
&& filename_cmp (file, prev_file) == 0) |
{ |
/* Same file/line as last time. */ |
return; |
} |
else |
{ |
/* Remember file/line for next time. */ |
prev_lineno = lineno; |
if (filename_cmp (file, prev_file) != 0) |
{ |
free (prev_file); |
prev_file = xstrdup (file); |
} |
} |
/* Let the world know that we are in the middle of generating a |
piece of stabs line debugging information. */ |
outputting_stabs_line_debug = 1; |
generate_asm_file (N_SOL, file); |
sprintf (sym, "%sL%d", FAKE_LABEL_NAME, label_count); |
++label_count; |
if (in_dot_func_p) |
{ |
buf = (char *) alloca (100 + strlen (current_function_label)); |
sprintf (buf, "%d,0,%d,%s-%s\n", N_SLINE, lineno, |
sym, current_function_label); |
} |
else |
{ |
buf = (char *) alloca (100); |
sprintf (buf, "%d,0,%d,%s\n", N_SLINE, lineno, sym); |
} |
input_line_pointer = buf; |
s_stab ('n'); |
colon (sym); |
input_line_pointer = hold; |
outputting_stabs_line_debug = 0; |
} |
/* Emit a function stab. |
All assembler functions are assumed to have return type `void'. */ |
void |
stabs_generate_asm_func (const char *funcname, const char *startlabname) |
{ |
static int void_emitted_p; |
char *hold = input_line_pointer; |
char *buf; |
char *file; |
unsigned int lineno; |
if (! void_emitted_p) |
{ |
input_line_pointer = "\"void:t1=1\",128,0,0,0"; |
s_stab ('s'); |
void_emitted_p = 1; |
} |
as_where (&file, &lineno); |
if (asprintf (&buf, "\"%s:F1\",%d,0,%d,%s", |
funcname, N_FUN, lineno + 1, startlabname) == -1) |
as_fatal ("%s", xstrerror (errno)); |
input_line_pointer = buf; |
s_stab ('s'); |
free (buf); |
input_line_pointer = hold; |
current_function_label = xstrdup (startlabname); |
in_dot_func_p = 1; |
} |
/* Emit a stab to record the end of a function. */ |
void |
stabs_generate_asm_endfunc (const char *funcname ATTRIBUTE_UNUSED, |
const char *startlabname) |
{ |
static int label_count; |
char *hold = input_line_pointer; |
char *buf; |
char sym[30]; |
sprintf (sym, "%sendfunc%d", FAKE_LABEL_NAME, label_count); |
++label_count; |
colon (sym); |
if (asprintf (&buf, "\"\",%d,0,0,%s-%s", N_FUN, sym, startlabname) == -1) |
as_fatal ("%s", xstrerror (errno)); |
input_line_pointer = buf; |
s_stab ('s'); |
free (buf); |
input_line_pointer = hold; |
in_dot_func_p = 0; |
current_function_label = NULL; |
} |
/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 (¬es, name, name_length); |
ret = (char *) obstack_finish (¬es); |
#ifdef tc_canonicalize_symbol_name |
ret = tc_canonicalize_symbol_name (ret); |
#endif |
if (! symbols_case_sensitive) |
{ |
char *s; |
for (s = ret; *s != '\0'; s++) |
*s = TOUPPER (*s); |
} |
return ret; |
} |
symbolS * |
symbol_create (const char *name, /* It is copied, the caller can destroy/modify. */ |
segT segment, /* Segment identifier (SEG_<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 (¬es, sizeof (symbolS)); |
/* symbol must be born in some fixed state. This seems as good as any. */ |
memset (symbolP, 0, sizeof (symbolS)); |
symbolP->bsym = bfd_make_empty_symbol (stdoutput); |
if (symbolP->bsym == NULL) |
as_fatal ("bfd_make_empty_symbol: %s", bfd_errmsg (bfd_get_error ())); |
S_SET_NAME (symbolP, preserved_copy_of_name); |
S_SET_SEGMENT (symbolP, segment); |
S_SET_VALUE (symbolP, valu); |
symbol_clear_list_pointers (symbolP); |
symbolP->sy_frag = frag; |
obj_symbol_new_hook (symbolP); |
#ifdef tc_symbol_new_hook |
tc_symbol_new_hook (symbolP); |
#endif |
return symbolP; |
} |
/* Local symbol support. If we can get away with it, we keep only a |
small amount of information for local symbols. */ |
static symbolS *local_symbol_convert (struct local_symbol *); |
/* Used for statistics. */ |
static unsigned long local_symbol_count; |
static unsigned long local_symbol_conversion_count; |
/* This macro is called with a symbol argument passed by reference. |
It returns whether this is a local symbol. If necessary, it |
changes its argument to the real symbol. */ |
#define LOCAL_SYMBOL_CHECK(s) \ |
(s->sy_flags.sy_local_symbol \ |
? (local_symbol_converted_p ((struct local_symbol *) s) \ |
? (s = local_symbol_get_real_symbol ((struct local_symbol *) s), \ |
0) \ |
: 1) \ |
: 0) |
/* Create a local symbol and insert it into the local hash table. */ |
struct local_symbol * |
local_symbol_make (const char *name, segT section, valueT val, fragS *frag) |
{ |
char *name_copy; |
struct local_symbol *ret; |
++local_symbol_count; |
name_copy = save_symbol_name (name); |
ret = (struct local_symbol *) obstack_alloc (¬es, sizeof *ret); |
ret->lsy_flags.sy_local_symbol = 1; |
ret->lsy_flags.sy_resolved = 0; |
ret->lsy_name = name_copy; |
ret->lsy_section = section; |
local_symbol_set_frag (ret, frag); |
ret->lsy_value = val; |
hash_jam (local_hash, name_copy, (void *) ret); |
return ret; |
} |
/* Convert a local symbol into a real symbol. Note that we do not |
reclaim the space used by the local symbol. */ |
static symbolS * |
local_symbol_convert (struct local_symbol *locsym) |
{ |
symbolS *ret; |
gas_assert (locsym->lsy_flags.sy_local_symbol); |
if (local_symbol_converted_p (locsym)) |
return local_symbol_get_real_symbol (locsym); |
++local_symbol_conversion_count; |
ret = symbol_new (locsym->lsy_name, locsym->lsy_section, locsym->lsy_value, |
local_symbol_get_frag (locsym)); |
if (local_symbol_resolved_p (locsym)) |
ret->sy_flags.sy_resolved = 1; |
/* Local symbols are always either defined or used. */ |
ret->sy_flags.sy_used = 1; |
#ifdef TC_LOCAL_SYMFIELD_CONVERT |
TC_LOCAL_SYMFIELD_CONVERT (locsym, ret); |
#endif |
symbol_table_insert (ret); |
local_symbol_mark_converted (locsym); |
local_symbol_set_real_symbol (locsym, ret); |
hash_jam (local_hash, locsym->lsy_name, NULL); |
return ret; |
} |
static void |
define_sym_at_dot (symbolS *symbolP) |
{ |
symbolP->sy_frag = frag_now; |
S_SET_VALUE (symbolP, (valueT) frag_now_fix ()); |
S_SET_SEGMENT (symbolP, now_seg); |
} |
/* We have just seen "<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 (¬es, sizeof (*newsymP)); |
*newsymP = *orgsymP; |
bsymnew = bfd_make_empty_symbol (bfd_asymbol_bfd (bsymorg)); |
if (bsymnew == NULL) |
as_fatal ("bfd_make_empty_symbol: %s", bfd_errmsg (bfd_get_error ())); |
newsymP->bsym = bsymnew; |
bsymnew->name = bsymorg->name; |
bsymnew->flags = bsymorg->flags & ~BSF_SECTION_SYM; |
bsymnew->section = bsymorg->section; |
bfd_copy_private_symbol_data (bfd_asymbol_bfd (bsymorg), bsymorg, |
bfd_asymbol_bfd (bsymnew), bsymnew); |
#ifdef obj_symbol_clone_hook |
obj_symbol_clone_hook (newsymP, orgsymP); |
#endif |
#ifdef tc_symbol_clone_hook |
tc_symbol_clone_hook (newsymP, orgsymP); |
#endif |
if (replace) |
{ |
if (symbol_rootP == orgsymP) |
symbol_rootP = newsymP; |
else if (orgsymP->sy_previous) |
{ |
orgsymP->sy_previous->sy_next = newsymP; |
orgsymP->sy_previous = NULL; |
} |
if (symbol_lastP == orgsymP) |
symbol_lastP = newsymP; |
else if (orgsymP->sy_next) |
orgsymP->sy_next->sy_previous = newsymP; |
/* Symbols that won't be output can't be external. */ |
S_CLEAR_EXTERNAL (orgsymP); |
orgsymP->sy_previous = orgsymP->sy_next = orgsymP; |
debug_verify_symchain (symbol_rootP, symbol_lastP); |
symbol_table_insert (newsymP); |
} |
else |
{ |
/* Symbols that won't be output can't be external. */ |
S_CLEAR_EXTERNAL (newsymP); |
newsymP->sy_previous = newsymP->sy_next = newsymP; |
} |
return newsymP; |
} |
/* Referenced symbols, if they are forward references, need to be cloned |
(without replacing the original) so that the value of the referenced |
symbols at the point of use . */ |
#undef symbol_clone_if_forward_ref |
symbolS * |
symbol_clone_if_forward_ref (symbolS *symbolP, int is_forward) |
{ |
if (symbolP && !LOCAL_SYMBOL_CHECK (symbolP)) |
{ |
symbolS *add_symbol = symbolP->sy_value.X_add_symbol; |
symbolS *op_symbol = symbolP->sy_value.X_op_symbol; |
if (symbolP->sy_flags.sy_forward_ref) |
is_forward = 1; |
if (is_forward) |
{ |
/* assign_symbol() clones volatile symbols; pre-existing expressions |
hold references to the original instance, but want the current |
value. Just repeat the lookup. */ |
if (add_symbol && S_IS_VOLATILE (add_symbol)) |
add_symbol = symbol_find_exact (S_GET_NAME (add_symbol)); |
if (op_symbol && S_IS_VOLATILE (op_symbol)) |
op_symbol = symbol_find_exact (S_GET_NAME (op_symbol)); |
} |
/* Re-using sy_resolving here, as this routine cannot get called from |
symbol resolution code. */ |
if ((symbolP->bsym->section == expr_section |
|| symbolP->sy_flags.sy_forward_ref) |
&& !symbolP->sy_flags.sy_resolving) |
{ |
symbolP->sy_flags.sy_resolving = 1; |
add_symbol = symbol_clone_if_forward_ref (add_symbol, is_forward); |
op_symbol = symbol_clone_if_forward_ref (op_symbol, is_forward); |
symbolP->sy_flags.sy_resolving = 0; |
} |
if (symbolP->sy_flags.sy_forward_ref |
|| add_symbol != symbolP->sy_value.X_add_symbol |
|| op_symbol != symbolP->sy_value.X_op_symbol) |
{ |
if (symbolP != &dot_symbol) |
{ |
symbolP = symbol_clone (symbolP, 0); |
symbolP->sy_flags.sy_resolving = 0; |
} |
else |
{ |
symbolP = symbol_temp_new_now (); |
#ifdef tc_new_dot_label |
tc_new_dot_label (symbolP); |
#endif |
} |
} |
symbolP->sy_value.X_add_symbol = add_symbol; |
symbolP->sy_value.X_op_symbol = op_symbol; |
} |
return symbolP; |
} |
symbolS * |
symbol_temp_new (segT seg, valueT ofs, fragS *frag) |
{ |
return symbol_new (FAKE_LABEL_NAME, seg, ofs, frag); |
} |
symbolS * |
symbol_temp_new_now (void) |
{ |
return symbol_temp_new (now_seg, frag_now_fix (), frag_now); |
} |
symbolS * |
symbol_temp_make (void) |
{ |
return symbol_make (FAKE_LABEL_NAME); |
} |
/* Implement symbol table lookup. |
In: A symbol's name as a string: '\0' can't be part of a symbol name. |
Out: NULL if the name was not in the symbol table, else the address |
of a struct symbol associated with that name. */ |
symbolS * |
symbol_find_exact (const char *name) |
{ |
return symbol_find_exact_noref (name, 0); |
} |
symbolS * |
symbol_find_exact_noref (const char *name, int noref) |
{ |
struct local_symbol *locsym; |
symbolS* sym; |
locsym = (struct local_symbol *) hash_find (local_hash, name); |
if (locsym != NULL) |
return (symbolS *) locsym; |
sym = ((symbolS *) hash_find (sy_hash, name)); |
/* Any references to the symbol, except for the reference in |
.weakref, must clear this flag, such that the symbol does not |
turn into a weak symbol. Note that we don't have to handle the |
local_symbol case, since a weakrefd is always promoted out of the |
local_symbol table when it is turned into a weak symbol. */ |
if (sym && ! noref) |
S_CLEAR_WEAKREFD (sym); |
return sym; |
} |
symbolS * |
symbol_find (const char *name) |
{ |
return symbol_find_noref (name, 0); |
} |
symbolS * |
symbol_find_noref (const char *name, int noref) |
{ |
#ifdef tc_canonicalize_symbol_name |
{ |
char *copy; |
size_t len = strlen (name) + 1; |
copy = (char *) alloca (len); |
memcpy (copy, name, len); |
name = tc_canonicalize_symbol_name (copy); |
} |
#endif |
if (! symbols_case_sensitive) |
{ |
char *copy; |
const char *orig; |
unsigned char c; |
orig = name; |
name = copy = (char *) alloca (strlen (name) + 1); |
while ((c = *orig++) != '\0') |
{ |
*copy++ = TOUPPER (c); |
} |
*copy = '\0'; |
} |
return symbol_find_exact_noref (name, noref); |
} |
/* Once upon a time, symbols were kept in a singly linked list. At |
least coff needs to be able to rearrange them from time to time, for |
which a doubly linked list is much more convenient. Loic did these |
as macros which seemed dangerous to me so they're now functions. |
xoxorich. */ |
/* Link symbol ADDME after symbol TARGET in the chain. */ |
void |
symbol_append (symbolS *addme, symbolS *target, |
symbolS **rootPP, symbolS **lastPP) |
{ |
if (LOCAL_SYMBOL_CHECK (addme)) |
abort (); |
if (target != NULL && LOCAL_SYMBOL_CHECK (target)) |
abort (); |
if (target == NULL) |
{ |
know (*rootPP == NULL); |
know (*lastPP == NULL); |
addme->sy_next = NULL; |
addme->sy_previous = NULL; |
*rootPP = addme; |
*lastPP = addme; |
return; |
} /* if the list is empty */ |
if (target->sy_next != NULL) |
{ |
target->sy_next->sy_previous = addme; |
} |
else |
{ |
know (*lastPP == target); |
*lastPP = addme; |
} /* if we have a next */ |
addme->sy_next = target->sy_next; |
target->sy_next = addme; |
addme->sy_previous = target; |
debug_verify_symchain (symbol_rootP, symbol_lastP); |
} |
/* Set the chain pointers of SYMBOL to null. */ |
void |
symbol_clear_list_pointers (symbolS *symbolP) |
{ |
if (LOCAL_SYMBOL_CHECK (symbolP)) |
abort (); |
symbolP->sy_next = NULL; |
symbolP->sy_previous = NULL; |
} |
/* Remove SYMBOLP from the list. */ |
void |
symbol_remove (symbolS *symbolP, symbolS **rootPP, symbolS **lastPP) |
{ |
if (LOCAL_SYMBOL_CHECK (symbolP)) |
abort (); |
if (symbolP == *rootPP) |
{ |
*rootPP = symbolP->sy_next; |
} /* if it was the root */ |
if (symbolP == *lastPP) |
{ |
*lastPP = symbolP->sy_previous; |
} /* if it was the tail */ |
if (symbolP->sy_next != NULL) |
{ |
symbolP->sy_next->sy_previous = symbolP->sy_previous; |
} /* if not last */ |
if (symbolP->sy_previous != NULL) |
{ |
symbolP->sy_previous->sy_next = symbolP->sy_next; |
} /* if not first */ |
debug_verify_symchain (*rootPP, *lastPP); |
} |
/* Link symbol ADDME before symbol TARGET in the chain. */ |
void |
symbol_insert (symbolS *addme, symbolS *target, |
symbolS **rootPP, symbolS **lastPP ATTRIBUTE_UNUSED) |
{ |
if (LOCAL_SYMBOL_CHECK (addme)) |
abort (); |
if (LOCAL_SYMBOL_CHECK (target)) |
abort (); |
if (target->sy_previous != NULL) |
{ |
target->sy_previous->sy_next = addme; |
} |
else |
{ |
know (*rootPP == target); |
*rootPP = addme; |
} /* if not first */ |
addme->sy_previous = target->sy_previous; |
target->sy_previous = addme; |
addme->sy_next = target; |
debug_verify_symchain (*rootPP, *lastPP); |
} |
void |
verify_symbol_chain (symbolS *rootP, symbolS *lastP) |
{ |
symbolS *symbolP = rootP; |
if (symbolP == NULL) |
return; |
for (; symbol_next (symbolP) != NULL; symbolP = symbol_next (symbolP)) |
{ |
gas_assert (symbolP->bsym != NULL); |
gas_assert (symbolP->sy_flags.sy_local_symbol == 0); |
gas_assert (symbolP->sy_next->sy_previous == symbolP); |
} |
gas_assert (lastP == symbolP); |
} |
#ifdef OBJ_COMPLEX_RELC |
static int |
use_complex_relocs_for (symbolS * symp) |
{ |
switch (symp->sy_value.X_op) |
{ |
case O_constant: |
return 0; |
case O_symbol: |
case O_symbol_rva: |
case O_uminus: |
case O_bit_not: |
case O_logical_not: |
if ( (S_IS_COMMON (symp->sy_value.X_add_symbol) |
|| S_IS_LOCAL (symp->sy_value.X_add_symbol)) |
&& |
(S_IS_DEFINED (symp->sy_value.X_add_symbol) |
&& S_GET_SEGMENT (symp->sy_value.X_add_symbol) != expr_section)) |
return 0; |
break; |
case O_multiply: |
case O_divide: |
case O_modulus: |
case O_left_shift: |
case O_right_shift: |
case O_bit_inclusive_or: |
case O_bit_or_not: |
case O_bit_exclusive_or: |
case O_bit_and: |
case O_add: |
case O_subtract: |
case O_eq: |
case O_ne: |
case O_lt: |
case O_le: |
case O_ge: |
case O_gt: |
case O_logical_and: |
case O_logical_or: |
if ( (S_IS_COMMON (symp->sy_value.X_add_symbol) |
|| S_IS_LOCAL (symp->sy_value.X_add_symbol)) |
&& |
(S_IS_COMMON (symp->sy_value.X_op_symbol) |
|| S_IS_LOCAL (symp->sy_value.X_op_symbol)) |
&& S_IS_DEFINED (symp->sy_value.X_add_symbol) |
&& S_IS_DEFINED (symp->sy_value.X_op_symbol) |
&& S_GET_SEGMENT (symp->sy_value.X_add_symbol) != expr_section |
&& S_GET_SEGMENT (symp->sy_value.X_op_symbol) != expr_section) |
return 0; |
break; |
default: |
break; |
} |
return 1; |
} |
#endif |
static void |
report_op_error (symbolS *symp, symbolS *left, operatorT op, symbolS *right) |
{ |
char *file; |
unsigned int line; |
segT seg_left = left ? S_GET_SEGMENT (left) : 0; |
segT seg_right = S_GET_SEGMENT (right); |
const char *opname; |
switch (op) |
{ |
default: |
abort (); |
return; |
case O_uminus: opname = "-"; break; |
case O_bit_not: opname = "~"; break; |
case O_logical_not: opname = "!"; break; |
case O_multiply: opname = "*"; break; |
case O_divide: opname = "/"; break; |
case O_modulus: opname = "%"; break; |
case O_left_shift: opname = "<<"; break; |
case O_right_shift: opname = ">>"; break; |
case O_bit_inclusive_or: opname = "|"; break; |
case O_bit_or_not: opname = "|~"; break; |
case O_bit_exclusive_or: opname = "^"; break; |
case O_bit_and: opname = "&"; break; |
case O_add: opname = "+"; break; |
case O_subtract: opname = "-"; break; |
case O_eq: opname = "=="; break; |
case O_ne: opname = "!="; break; |
case O_lt: opname = "<"; break; |
case O_le: opname = "<="; break; |
case O_ge: opname = ">="; break; |
case O_gt: opname = ">"; break; |
case O_logical_and: opname = "&&"; break; |
case O_logical_or: opname = "||"; break; |
} |
if (expr_symbol_where (symp, &file, &line)) |
{ |
if (left) |
as_bad_where (file, line, |
_("invalid operands (%s and %s sections) for `%s'"), |
seg_left->name, seg_right->name, opname); |
else |
as_bad_where (file, line, |
_("invalid operand (%s section) for `%s'"), |
seg_right->name, opname); |
} |
else |
{ |
const char *sname = S_GET_NAME (symp); |
if (left) |
as_bad (_("invalid operands (%s and %s sections) for `%s' when setting `%s'"), |
seg_left->name, seg_right->name, opname, sname); |
else |
as_bad (_("invalid operand (%s section) for `%s' when setting `%s'"), |
seg_right->name, opname, sname); |
} |
} |
/* Resolve the value of a symbol. This is called during the final |
pass over the symbol table to resolve any symbols with complex |
values. */ |
valueT |
resolve_symbol_value (symbolS *symp) |
{ |
int resolved; |
valueT final_val = 0; |
segT final_seg; |
if (LOCAL_SYMBOL_CHECK (symp)) |
{ |
struct local_symbol *locsym = (struct local_symbol *) symp; |
final_val = locsym->lsy_value; |
if (local_symbol_resolved_p (locsym)) |
return final_val; |
final_val += local_symbol_get_frag (locsym)->fr_address / OCTETS_PER_BYTE; |
if (finalize_syms) |
{ |
locsym->lsy_value = final_val; |
local_symbol_mark_resolved (locsym); |
} |
return final_val; |
} |
if (symp->sy_flags.sy_resolved) |
{ |
if (symp->sy_value.X_op == O_constant) |
return (valueT) symp->sy_value.X_add_number; |
else |
return 0; |
} |
resolved = 0; |
final_seg = S_GET_SEGMENT (symp); |
if (symp->sy_flags.sy_resolving) |
{ |
if (finalize_syms) |
as_bad (_("symbol definition loop encountered at `%s'"), |
S_GET_NAME (symp)); |
final_val = 0; |
resolved = 1; |
} |
#ifdef OBJ_COMPLEX_RELC |
else if (final_seg == expr_section |
&& use_complex_relocs_for (symp)) |
{ |
symbolS * relc_symbol = NULL; |
char * relc_symbol_name = NULL; |
relc_symbol_name = symbol_relc_make_expr (& symp->sy_value); |
/* For debugging, print out conversion input & output. */ |
#ifdef DEBUG_SYMS |
print_expr (& symp->sy_value); |
if (relc_symbol_name) |
fprintf (stderr, "-> relc symbol: %s\n", relc_symbol_name); |
#endif |
if (relc_symbol_name != NULL) |
relc_symbol = symbol_new (relc_symbol_name, undefined_section, |
0, & zero_address_frag); |
if (relc_symbol == NULL) |
{ |
as_bad (_("cannot convert expression symbol %s to complex relocation"), |
S_GET_NAME (symp)); |
resolved = 0; |
} |
else |
{ |
symbol_table_insert (relc_symbol); |
/* S_CLEAR_EXTERNAL (relc_symbol); */ |
if (symp->bsym->flags & BSF_SRELC) |
relc_symbol->bsym->flags |= BSF_SRELC; |
else |
relc_symbol->bsym->flags |= BSF_RELC; |
/* symp->bsym->flags |= BSF_RELC; */ |
copy_symbol_attributes (symp, relc_symbol); |
symp->sy_value.X_op = O_symbol; |
symp->sy_value.X_add_symbol = relc_symbol; |
symp->sy_value.X_add_number = 0; |
resolved = 1; |
} |
final_seg = undefined_section; |
goto exit_dont_set_value; |
} |
#endif |
else |
{ |
symbolS *add_symbol, *op_symbol; |
offsetT left, right; |
segT seg_left, seg_right; |
operatorT op; |
int move_seg_ok; |
symp->sy_flags.sy_resolving = 1; |
/* Help out with CSE. */ |
add_symbol = symp->sy_value.X_add_symbol; |
op_symbol = symp->sy_value.X_op_symbol; |
final_val = symp->sy_value.X_add_number; |
op = symp->sy_value.X_op; |
switch (op) |
{ |
default: |
BAD_CASE (op); |
break; |
case O_absent: |
final_val = 0; |
/* Fall through. */ |
case O_constant: |
final_val += symp->sy_frag->fr_address / OCTETS_PER_BYTE; |
if (final_seg == expr_section) |
final_seg = absolute_section; |
/* Fall through. */ |
case O_register: |
resolved = 1; |
break; |
case O_symbol: |
case O_symbol_rva: |
left = resolve_symbol_value (add_symbol); |
seg_left = S_GET_SEGMENT (add_symbol); |
if (finalize_syms) |
symp->sy_value.X_op_symbol = NULL; |
do_symbol: |
if (S_IS_WEAKREFR (symp)) |
{ |
gas_assert (final_val == 0); |
if (S_IS_WEAKREFR (add_symbol)) |
{ |
gas_assert (add_symbol->sy_value.X_op == O_symbol |
&& add_symbol->sy_value.X_add_number == 0); |
add_symbol = add_symbol->sy_value.X_add_symbol; |
gas_assert (! S_IS_WEAKREFR (add_symbol)); |
symp->sy_value.X_add_symbol = add_symbol; |
} |
} |
if (symp->sy_flags.sy_mri_common) |
{ |
/* This is a symbol inside an MRI common section. The |
relocation routines are going to handle it specially. |
Don't change the value. */ |
resolved = symbol_resolved_p (add_symbol); |
break; |
} |
if (finalize_syms && final_val == 0) |
{ |
if (LOCAL_SYMBOL_CHECK (add_symbol)) |
add_symbol = local_symbol_convert ((struct local_symbol *) |
add_symbol); |
copy_symbol_attributes (symp, add_symbol); |
} |
/* If we have equated this symbol to an undefined or common |
symbol, keep X_op set to O_symbol, and don't change |
X_add_number. This permits the routine which writes out |
relocation to detect this case, and convert the |
relocation to be against the symbol to which this symbol |
is equated. */ |
if (! S_IS_DEFINED (add_symbol) |
#if defined (OBJ_COFF) && defined (TE_PE) |
|| S_IS_WEAK (add_symbol) |
#endif |
|| S_IS_COMMON (add_symbol)) |
{ |
if (finalize_syms) |
{ |
symp->sy_value.X_op = O_symbol; |
symp->sy_value.X_add_symbol = add_symbol; |
symp->sy_value.X_add_number = final_val; |
/* Use X_op_symbol as a flag. */ |
symp->sy_value.X_op_symbol = add_symbol; |
} |
final_seg = seg_left; |
final_val = 0; |
resolved = symbol_resolved_p (add_symbol); |
symp->sy_flags.sy_resolving = 0; |
goto exit_dont_set_value; |
} |
else if (finalize_syms |
&& ((final_seg == expr_section && seg_left != expr_section) |
|| symbol_shadow_p (symp))) |
{ |
/* If the symbol is an expression symbol, do similarly |
as for undefined and common syms above. Handles |
"sym +/- expr" where "expr" cannot be evaluated |
immediately, and we want relocations to be against |
"sym", eg. because it is weak. */ |
symp->sy_value.X_op = O_symbol; |
symp->sy_value.X_add_symbol = add_symbol; |
symp->sy_value.X_add_number = final_val; |
symp->sy_value.X_op_symbol = add_symbol; |
final_seg = seg_left; |
final_val += symp->sy_frag->fr_address + left; |
resolved = symbol_resolved_p (add_symbol); |
symp->sy_flags.sy_resolving = 0; |
goto exit_dont_set_value; |
} |
else |
{ |
final_val += symp->sy_frag->fr_address + left; |
if (final_seg == expr_section || final_seg == undefined_section) |
final_seg = seg_left; |
} |
resolved = symbol_resolved_p (add_symbol); |
if (S_IS_WEAKREFR (symp)) |
goto exit_dont_set_value; |
break; |
case O_uminus: |
case O_bit_not: |
case O_logical_not: |
left = resolve_symbol_value (add_symbol); |
seg_left = S_GET_SEGMENT (add_symbol); |
/* By reducing these to the relevant dyadic operator, we get |
!S -> S == 0 permitted on anything, |
-S -> 0 - S only permitted on absolute |
~S -> S ^ ~0 only permitted on absolute */ |
if (op != O_logical_not && seg_left != absolute_section |
&& finalize_syms) |
report_op_error (symp, NULL, op, add_symbol); |
if (final_seg == expr_section || final_seg == undefined_section) |
final_seg = absolute_section; |
if (op == O_uminus) |
left = -left; |
else if (op == O_logical_not) |
left = !left; |
else |
left = ~left; |
final_val += left + symp->sy_frag->fr_address; |
resolved = symbol_resolved_p (add_symbol); |
break; |
case O_multiply: |
case O_divide: |
case O_modulus: |
case O_left_shift: |
case O_right_shift: |
case O_bit_inclusive_or: |
case O_bit_or_not: |
case O_bit_exclusive_or: |
case O_bit_and: |
case O_add: |
case O_subtract: |
case O_eq: |
case O_ne: |
case O_lt: |
case O_le: |
case O_ge: |
case O_gt: |
case O_logical_and: |
case O_logical_or: |
left = resolve_symbol_value (add_symbol); |
right = resolve_symbol_value (op_symbol); |
seg_left = S_GET_SEGMENT (add_symbol); |
seg_right = S_GET_SEGMENT (op_symbol); |
/* Simplify addition or subtraction of a constant by folding the |
constant into X_add_number. */ |
if (op == O_add) |
{ |
if (seg_right == absolute_section) |
{ |
final_val += right; |
goto do_symbol; |
} |
else if (seg_left == absolute_section) |
{ |
final_val += left; |
add_symbol = op_symbol; |
left = right; |
seg_left = seg_right; |
goto do_symbol; |
} |
} |
else if (op == O_subtract) |
{ |
if (seg_right == absolute_section) |
{ |
final_val -= right; |
goto do_symbol; |
} |
} |
move_seg_ok = 1; |
/* Equality and non-equality tests are permitted on anything. |
Subtraction, and other comparison operators are permitted if |
both operands are in the same section. Otherwise, both |
operands must be absolute. We already handled the case of |
addition or subtraction of a constant above. This will |
probably need to be changed for an object file format which |
supports arbitrary expressions, such as IEEE-695. */ |
if (!(seg_left == absolute_section |
&& seg_right == absolute_section) |
&& !(op == O_eq || op == O_ne) |
&& !((op == O_subtract |
|| op == O_lt || op == O_le || op == O_ge || op == O_gt) |
&& seg_left == seg_right |
&& (seg_left != undefined_section |
|| add_symbol == op_symbol))) |
{ |
/* Don't emit messages unless we're finalizing the symbol value, |
otherwise we may get the same message multiple times. */ |
if (finalize_syms) |
report_op_error (symp, add_symbol, op, op_symbol); |
/* However do not move the symbol into the absolute section |
if it cannot currently be resolved - this would confuse |
other parts of the assembler into believing that the |
expression had been evaluated to zero. */ |
else |
move_seg_ok = 0; |
} |
if (move_seg_ok |
&& (final_seg == expr_section || final_seg == undefined_section)) |
final_seg = absolute_section; |
/* Check for division by zero. */ |
if ((op == O_divide || op == O_modulus) && right == 0) |
{ |
/* If seg_right is not absolute_section, then we've |
already issued a warning about using a bad symbol. */ |
if (seg_right == absolute_section && finalize_syms) |
{ |
char *file; |
unsigned int line; |
if (expr_symbol_where (symp, &file, &line)) |
as_bad_where (file, line, _("division by zero")); |
else |
as_bad (_("division by zero when setting `%s'"), |
S_GET_NAME (symp)); |
} |
right = 1; |
} |
switch (symp->sy_value.X_op) |
{ |
case O_multiply: left *= right; break; |
case O_divide: left /= right; break; |
case O_modulus: left %= right; break; |
case O_left_shift: left <<= right; break; |
case O_right_shift: left >>= right; break; |
case O_bit_inclusive_or: left |= right; break; |
case O_bit_or_not: left |= ~right; break; |
case O_bit_exclusive_or: left ^= right; break; |
case O_bit_and: left &= right; break; |
case O_add: left += right; break; |
case O_subtract: left -= right; break; |
case O_eq: |
case O_ne: |
left = (left == right && seg_left == seg_right |
&& (seg_left != undefined_section |
|| add_symbol == op_symbol) |
? ~ (offsetT) 0 : 0); |
if (symp->sy_value.X_op == O_ne) |
left = ~left; |
break; |
case O_lt: left = left < right ? ~ (offsetT) 0 : 0; break; |
case O_le: left = left <= right ? ~ (offsetT) 0 : 0; break; |
case O_ge: left = left >= right ? ~ (offsetT) 0 : 0; break; |
case O_gt: left = left > right ? ~ (offsetT) 0 : 0; break; |
case O_logical_and: left = left && right; break; |
case O_logical_or: left = left || right; break; |
default: abort (); |
} |
final_val += symp->sy_frag->fr_address + left; |
if (final_seg == expr_section || final_seg == undefined_section) |
{ |
if (seg_left == undefined_section |
|| seg_right == undefined_section) |
final_seg = undefined_section; |
else if (seg_left == absolute_section) |
final_seg = seg_right; |
else |
final_seg = seg_left; |
} |
resolved = (symbol_resolved_p (add_symbol) |
&& symbol_resolved_p (op_symbol)); |
break; |
case O_big: |
case O_illegal: |
/* Give an error (below) if not in expr_section. We don't |
want to worry about expr_section symbols, because they |
are fictional (they are created as part of expression |
resolution), and any problems may not actually mean |
anything. */ |
break; |
} |
symp->sy_flags.sy_resolving = 0; |
} |
if (finalize_syms) |
S_SET_VALUE (symp, final_val); |
exit_dont_set_value: |
/* Always set the segment, even if not finalizing the value. |
The segment is used to determine whether a symbol is defined. */ |
S_SET_SEGMENT (symp, final_seg); |
/* Don't worry if we can't resolve an expr_section symbol. */ |
if (finalize_syms) |
{ |
if (resolved) |
symp->sy_flags.sy_resolved = 1; |
else if (S_GET_SEGMENT (symp) != expr_section) |
{ |
as_bad (_("can't resolve value for symbol `%s'"), |
S_GET_NAME (symp)); |
symp->sy_flags.sy_resolved = 1; |
} |
} |
return final_val; |
} |
static void resolve_local_symbol (const char *, void *); |
/* A static function passed to hash_traverse. */ |
static void |
resolve_local_symbol (const char *key ATTRIBUTE_UNUSED, void *value) |
{ |
if (value != NULL) |
resolve_symbol_value ((symbolS *) value); |
} |
/* Resolve all local symbols. */ |
void |
resolve_local_symbol_values (void) |
{ |
hash_traverse (local_hash, resolve_local_symbol); |
} |
/* Obtain the current value of a symbol without changing any |
sub-expressions used. */ |
int |
snapshot_symbol (symbolS **symbolPP, valueT *valueP, segT *segP, fragS **fragPP) |
{ |
symbolS *symbolP = *symbolPP; |
if (LOCAL_SYMBOL_CHECK (symbolP)) |
{ |
struct local_symbol *locsym = (struct local_symbol *) symbolP; |
*valueP = locsym->lsy_value; |
*segP = locsym->lsy_section; |
*fragPP = local_symbol_get_frag (locsym); |
} |
else |
{ |
expressionS exp = symbolP->sy_value; |
if (!symbolP->sy_flags.sy_resolved && exp.X_op != O_illegal) |
{ |
int resolved; |
if (symbolP->sy_flags.sy_resolving) |
return 0; |
symbolP->sy_flags.sy_resolving = 1; |
resolved = resolve_expression (&exp); |
symbolP->sy_flags.sy_resolving = 0; |
if (!resolved) |
return 0; |
switch (exp.X_op) |
{ |
case O_constant: |
case O_register: |
if (!symbol_equated_p (symbolP)) |
break; |
/* Fall thru. */ |
case O_symbol: |
case O_symbol_rva: |
symbolP = exp.X_add_symbol; |
break; |
default: |
return 0; |
} |
} |
*symbolPP = symbolP; |
*valueP = exp.X_add_number; |
*segP = symbolP->bsym->section; |
*fragPP = symbolP->sy_frag; |
if (*segP == expr_section) |
switch (exp.X_op) |
{ |
case O_constant: *segP = absolute_section; break; |
case O_register: *segP = reg_section; break; |
default: break; |
} |
} |
return 1; |
} |
/* Dollar labels look like a number followed by a dollar sign. Eg, "42$". |
They are *really* local. That is, they go out of scope whenever we see a |
label that isn't local. Also, like fb labels, there can be multiple |
instances of a dollar label. Therefor, we name encode each instance with |
the instance number, keep a list of defined symbols separate from the real |
symbol table, and we treat these buggers as a sparse array. */ |
static long *dollar_labels; |
static long *dollar_label_instances; |
static char *dollar_label_defines; |
static unsigned long dollar_label_count; |
static unsigned long dollar_label_max; |
int |
dollar_label_defined (long label) |
{ |
long *i; |
know ((dollar_labels != NULL) || (dollar_label_count == 0)); |
for (i = dollar_labels; i < dollar_labels + dollar_label_count; ++i) |
if (*i == label) |
return dollar_label_defines[i - dollar_labels]; |
/* If we get here, label isn't defined. */ |
return 0; |
} |
static long |
dollar_label_instance (long label) |
{ |
long *i; |
know ((dollar_labels != NULL) || (dollar_label_count == 0)); |
for (i = dollar_labels; i < dollar_labels + dollar_label_count; ++i) |
if (*i == label) |
return (dollar_label_instances[i - dollar_labels]); |
/* If we get here, we haven't seen the label before. |
Therefore its instance count is zero. */ |
return 0; |
} |
void |
dollar_label_clear (void) |
{ |
memset (dollar_label_defines, '\0', (unsigned int) dollar_label_count); |
} |
#define DOLLAR_LABEL_BUMP_BY 10 |
void |
define_dollar_label (long label) |
{ |
long *i; |
for (i = dollar_labels; i < dollar_labels + dollar_label_count; ++i) |
if (*i == label) |
{ |
++dollar_label_instances[i - dollar_labels]; |
dollar_label_defines[i - dollar_labels] = 1; |
return; |
} |
/* If we get to here, we don't have label listed yet. */ |
if (dollar_labels == NULL) |
{ |
dollar_labels = (long *) xmalloc (DOLLAR_LABEL_BUMP_BY * sizeof (long)); |
dollar_label_instances = (long *) xmalloc (DOLLAR_LABEL_BUMP_BY * sizeof (long)); |
dollar_label_defines = (char *) xmalloc (DOLLAR_LABEL_BUMP_BY); |
dollar_label_max = DOLLAR_LABEL_BUMP_BY; |
dollar_label_count = 0; |
} |
else if (dollar_label_count == dollar_label_max) |
{ |
dollar_label_max += DOLLAR_LABEL_BUMP_BY; |
dollar_labels = (long *) xrealloc ((char *) dollar_labels, |
dollar_label_max * sizeof (long)); |
dollar_label_instances = (long *) xrealloc ((char *) dollar_label_instances, |
dollar_label_max * sizeof (long)); |
dollar_label_defines = (char *) xrealloc (dollar_label_defines, dollar_label_max); |
} /* if we needed to grow */ |
dollar_labels[dollar_label_count] = label; |
dollar_label_instances[dollar_label_count] = 1; |
dollar_label_defines[dollar_label_count] = 1; |
++dollar_label_count; |
} |
/* Caller must copy returned name: we re-use the area for the next name. |
The mth occurence of label n: is turned into the symbol "Ln^Am" |
where n is the label number and m is the instance number. "L" makes |
it a label discarded unless debugging and "^A"('\1') ensures no |
ordinary symbol SHOULD get the same name as a local label |
symbol. The first "4:" is "L4^A1" - the m numbers begin at 1. |
fb labels get the same treatment, except that ^B is used in place |
of ^A. */ |
char * /* Return local label name. */ |
dollar_label_name (register long n, /* we just saw "n$:" : n a number. */ |
register int augend /* 0 for current instance, 1 for new instance. */) |
{ |
long i; |
/* Returned to caller, then copied. Used for created names ("4f"). */ |
static char symbol_name_build[24]; |
register char *p; |
register char *q; |
char symbol_name_temporary[20]; /* Build up a number, BACKWARDS. */ |
know (n >= 0); |
know (augend == 0 || augend == 1); |
p = symbol_name_build; |
#ifdef LOCAL_LABEL_PREFIX |
*p++ = LOCAL_LABEL_PREFIX; |
#endif |
*p++ = 'L'; |
/* Next code just does sprintf( {}, "%d", n); */ |
/* Label number. */ |
q = symbol_name_temporary; |
for (*q++ = 0, i = n; i; ++q) |
{ |
*q = i % 10 + '0'; |
i /= 10; |
} |
while ((*p = *--q) != '\0') |
++p; |
*p++ = DOLLAR_LABEL_CHAR; /* ^A */ |
/* Instance number. */ |
q = symbol_name_temporary; |
for (*q++ = 0, i = dollar_label_instance (n) + augend; i; ++q) |
{ |
*q = i % 10 + '0'; |
i /= 10; |
} |
while ((*p++ = *--q) != '\0'); |
/* The label, as a '\0' ended string, starts at symbol_name_build. */ |
return symbol_name_build; |
} |
/* Somebody else's idea of local labels. They are made by "n:" where n |
is any decimal digit. Refer to them with |
"nb" for previous (backward) n: |
or "nf" for next (forward) n:. |
We do a little better and let n be any number, not just a single digit, but |
since the other guy's assembler only does ten, we treat the first ten |
specially. |
Like someone else's assembler, we have one set of local label counters for |
entire assembly, not one set per (sub)segment like in most assemblers. This |
implies that one can refer to a label in another segment, and indeed some |
crufty compilers have done just that. |
Since there could be a LOT of these things, treat them as a sparse |
array. */ |
#define FB_LABEL_SPECIAL (10) |
static long fb_low_counter[FB_LABEL_SPECIAL]; |
static long *fb_labels; |
static long *fb_label_instances; |
static long fb_label_count; |
static long fb_label_max; |
/* This must be more than FB_LABEL_SPECIAL. */ |
#define FB_LABEL_BUMP_BY (FB_LABEL_SPECIAL + 6) |
static void |
fb_label_init (void) |
{ |
memset ((void *) fb_low_counter, '\0', sizeof (fb_low_counter)); |
} |
/* Add one to the instance number of this fb label. */ |
void |
fb_label_instance_inc (long label) |
{ |
long *i; |
if ((unsigned long) label < FB_LABEL_SPECIAL) |
{ |
++fb_low_counter[label]; |
return; |
} |
if (fb_labels != NULL) |
{ |
for (i = fb_labels + FB_LABEL_SPECIAL; |
i < fb_labels + fb_label_count; ++i) |
{ |
if (*i == label) |
{ |
++fb_label_instances[i - fb_labels]; |
return; |
} /* if we find it */ |
} /* for each existing label */ |
} |
/* If we get to here, we don't have label listed yet. */ |
if (fb_labels == NULL) |
{ |
fb_labels = (long *) xmalloc (FB_LABEL_BUMP_BY * sizeof (long)); |
fb_label_instances = (long *) xmalloc (FB_LABEL_BUMP_BY * sizeof (long)); |
fb_label_max = FB_LABEL_BUMP_BY; |
fb_label_count = FB_LABEL_SPECIAL; |
} |
else if (fb_label_count == fb_label_max) |
{ |
fb_label_max += FB_LABEL_BUMP_BY; |
fb_labels = (long *) xrealloc ((char *) fb_labels, |
fb_label_max * sizeof (long)); |
fb_label_instances = (long *) xrealloc ((char *) fb_label_instances, |
fb_label_max * sizeof (long)); |
} /* if we needed to grow */ |
fb_labels[fb_label_count] = label; |
fb_label_instances[fb_label_count] = 1; |
++fb_label_count; |
} |
static long |
fb_label_instance (long label) |
{ |
long *i; |
if ((unsigned long) label < FB_LABEL_SPECIAL) |
{ |
return (fb_low_counter[label]); |
} |
if (fb_labels != NULL) |
{ |
for (i = fb_labels + FB_LABEL_SPECIAL; |
i < fb_labels + fb_label_count; ++i) |
{ |
if (*i == label) |
{ |
return (fb_label_instances[i - fb_labels]); |
} /* if we find it */ |
} /* for each existing label */ |
} |
/* We didn't find the label, so this must be a reference to the |
first instance. */ |
return 0; |
} |
/* Caller must copy returned name: we re-use the area for the next name. |
The mth occurence of label n: is turned into the symbol "Ln^Bm" |
where n is the label number and m is the instance number. "L" makes |
it a label discarded unless debugging and "^B"('\2') ensures no |
ordinary symbol SHOULD get the same name as a local label |
symbol. The first "4:" is "L4^B1" - the m numbers begin at 1. |
dollar labels get the same treatment, except that ^A is used in |
place of ^B. */ |
char * /* Return local label name. */ |
fb_label_name (long n, /* We just saw "n:", "nf" or "nb" : n a number. */ |
long augend /* 0 for nb, 1 for n:, nf. */) |
{ |
long i; |
/* Returned to caller, then copied. Used for created names ("4f"). */ |
static char symbol_name_build[24]; |
register char *p; |
register char *q; |
char symbol_name_temporary[20]; /* Build up a number, BACKWARDS. */ |
know (n >= 0); |
#ifdef TC_MMIX |
know ((unsigned long) augend <= 2 /* See mmix_fb_label. */); |
#else |
know ((unsigned long) augend <= 1); |
#endif |
p = symbol_name_build; |
#ifdef LOCAL_LABEL_PREFIX |
*p++ = LOCAL_LABEL_PREFIX; |
#endif |
*p++ = 'L'; |
/* Next code just does sprintf( {}, "%d", n); */ |
/* Label number. */ |
q = symbol_name_temporary; |
for (*q++ = 0, i = n; i; ++q) |
{ |
*q = i % 10 + '0'; |
i /= 10; |
} |
while ((*p = *--q) != '\0') |
++p; |
*p++ = LOCAL_LABEL_CHAR; /* ^B */ |
/* Instance number. */ |
q = symbol_name_temporary; |
for (*q++ = 0, i = fb_label_instance (n) + augend; i; ++q) |
{ |
*q = i % 10 + '0'; |
i /= 10; |
} |
while ((*p++ = *--q) != '\0'); |
/* The label, as a '\0' ended string, starts at symbol_name_build. */ |
return (symbol_name_build); |
} |
/* Decode name that may have been generated by foo_label_name() above. |
If the name wasn't generated by foo_label_name(), then return it |
unaltered. This is used for error messages. */ |
char * |
decode_local_label_name (char *s) |
{ |
char *p; |
char *symbol_decode; |
int label_number; |
int instance_number; |
char *type; |
const char *message_format; |
int lindex = 0; |
#ifdef LOCAL_LABEL_PREFIX |
if (s[lindex] == LOCAL_LABEL_PREFIX) |
++lindex; |
#endif |
if (s[lindex] != 'L') |
return s; |
for (label_number = 0, p = s + lindex + 1; ISDIGIT (*p); ++p) |
label_number = (10 * label_number) + *p - '0'; |
if (*p == DOLLAR_LABEL_CHAR) |
type = "dollar"; |
else if (*p == LOCAL_LABEL_CHAR) |
type = "fb"; |
else |
return s; |
for (instance_number = 0, p++; ISDIGIT (*p); ++p) |
instance_number = (10 * instance_number) + *p - '0'; |
message_format = _("\"%d\" (instance number %d of a %s label)"); |
symbol_decode = (char *) obstack_alloc (¬es, strlen (message_format) + 30); |
sprintf (symbol_decode, message_format, label_number, instance_number, type); |
return symbol_decode; |
} |
/* Get the value of a symbol. */ |
valueT |
S_GET_VALUE (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return resolve_symbol_value (s); |
if (!s->sy_flags.sy_resolved) |
{ |
valueT val = resolve_symbol_value (s); |
if (!finalize_syms) |
return val; |
} |
if (S_IS_WEAKREFR (s)) |
return S_GET_VALUE (s->sy_value.X_add_symbol); |
if (s->sy_value.X_op != O_constant) |
{ |
if (! s->sy_flags.sy_resolved |
|| s->sy_value.X_op != O_symbol |
|| (S_IS_DEFINED (s) && ! S_IS_COMMON (s))) |
as_bad (_("attempt to get value of unresolved symbol `%s'"), |
S_GET_NAME (s)); |
} |
return (valueT) s->sy_value.X_add_number; |
} |
/* Set the value of a symbol. */ |
void |
S_SET_VALUE (symbolS *s, valueT val) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
{ |
((struct local_symbol *) s)->lsy_value = val; |
return; |
} |
s->sy_value.X_op = O_constant; |
s->sy_value.X_add_number = (offsetT) val; |
s->sy_value.X_unsigned = 0; |
S_CLEAR_WEAKREFR (s); |
} |
void |
copy_symbol_attributes (symbolS *dest, symbolS *src) |
{ |
if (LOCAL_SYMBOL_CHECK (dest)) |
dest = local_symbol_convert ((struct local_symbol *) dest); |
if (LOCAL_SYMBOL_CHECK (src)) |
src = local_symbol_convert ((struct local_symbol *) src); |
/* In an expression, transfer the settings of these flags. |
The user can override later, of course. */ |
#define COPIED_SYMFLAGS (BSF_FUNCTION | BSF_OBJECT \ |
| BSF_GNU_INDIRECT_FUNCTION) |
dest->bsym->flags |= src->bsym->flags & COPIED_SYMFLAGS; |
#ifdef OBJ_COPY_SYMBOL_ATTRIBUTES |
OBJ_COPY_SYMBOL_ATTRIBUTES (dest, src); |
#endif |
#ifdef TC_COPY_SYMBOL_ATTRIBUTES |
TC_COPY_SYMBOL_ATTRIBUTES (dest, src); |
#endif |
} |
int |
S_IS_FUNCTION (symbolS *s) |
{ |
flagword flags; |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
flags = s->bsym->flags; |
return (flags & BSF_FUNCTION) != 0; |
} |
int |
S_IS_EXTERNAL (symbolS *s) |
{ |
flagword flags; |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
flags = s->bsym->flags; |
/* Sanity check. */ |
if ((flags & BSF_LOCAL) && (flags & BSF_GLOBAL)) |
abort (); |
return (flags & BSF_GLOBAL) != 0; |
} |
int |
S_IS_WEAK (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
/* Conceptually, a weakrefr is weak if the referenced symbol is. We |
could probably handle a WEAKREFR as always weak though. E.g., if |
the referenced symbol has lost its weak status, there's no reason |
to keep handling the weakrefr as if it was weak. */ |
if (S_IS_WEAKREFR (s)) |
return S_IS_WEAK (s->sy_value.X_add_symbol); |
return (s->bsym->flags & BSF_WEAK) != 0; |
} |
int |
S_IS_WEAKREFR (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
return s->sy_flags.sy_weakrefr != 0; |
} |
int |
S_IS_WEAKREFD (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
return s->sy_flags.sy_weakrefd != 0; |
} |
int |
S_IS_COMMON (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
return bfd_is_com_section (s->bsym->section); |
} |
int |
S_IS_DEFINED (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return ((struct local_symbol *) s)->lsy_section != undefined_section; |
return s->bsym->section != undefined_section; |
} |
#ifndef EXTERN_FORCE_RELOC |
#define EXTERN_FORCE_RELOC IS_ELF |
#endif |
/* Return true for symbols that should not be reduced to section |
symbols or eliminated from expressions, because they may be |
overridden by the linker. */ |
int |
S_FORCE_RELOC (symbolS *s, int strict) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return ((struct local_symbol *) s)->lsy_section == undefined_section; |
return ((strict |
&& ((s->bsym->flags & BSF_WEAK) != 0 |
|| (EXTERN_FORCE_RELOC |
&& (s->bsym->flags & BSF_GLOBAL) != 0))) |
|| (s->bsym->flags & BSF_GNU_INDIRECT_FUNCTION) != 0 |
|| s->bsym->section == undefined_section |
|| bfd_is_com_section (s->bsym->section)); |
} |
int |
S_IS_DEBUG (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
if (s->bsym->flags & BSF_DEBUGGING) |
return 1; |
return 0; |
} |
int |
S_IS_LOCAL (symbolS *s) |
{ |
flagword flags; |
const char *name; |
if (LOCAL_SYMBOL_CHECK (s)) |
return 1; |
flags = s->bsym->flags; |
/* Sanity check. */ |
if ((flags & BSF_LOCAL) && (flags & BSF_GLOBAL)) |
abort (); |
if (bfd_get_section (s->bsym) == reg_section) |
return 1; |
if (flag_strip_local_absolute |
/* Keep BSF_FILE symbols in order to allow debuggers to identify |
the source file even when the object file is stripped. */ |
&& (flags & (BSF_GLOBAL | BSF_FILE)) == 0 |
&& bfd_get_section (s->bsym) == absolute_section) |
return 1; |
name = S_GET_NAME (s); |
return (name != NULL |
&& ! S_IS_DEBUG (s) |
&& (strchr (name, DOLLAR_LABEL_CHAR) |
|| strchr (name, LOCAL_LABEL_CHAR) |
|| TC_LABEL_IS_LOCAL (name) |
|| (! flag_keep_locals |
&& (bfd_is_local_label (stdoutput, s->bsym) |
|| (flag_mri |
&& name[0] == '?' |
&& name[1] == '?'))))); |
} |
int |
S_IS_STABD (symbolS *s) |
{ |
return S_GET_NAME (s) == 0; |
} |
int |
S_CAN_BE_REDEFINED (const symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return (local_symbol_get_frag ((struct local_symbol *) s) |
== &predefined_address_frag); |
/* Permit register names to be redefined. */ |
return s->bsym->section == reg_section; |
} |
int |
S_IS_VOLATILE (const symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
return s->sy_flags.sy_volatile; |
} |
int |
S_IS_FORWARD_REF (const symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
return s->sy_flags.sy_forward_ref; |
} |
const char * |
S_GET_NAME (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return ((struct local_symbol *) s)->lsy_name; |
return s->bsym->name; |
} |
segT |
S_GET_SEGMENT (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return ((struct local_symbol *) s)->lsy_section; |
return s->bsym->section; |
} |
void |
S_SET_SEGMENT (symbolS *s, segT seg) |
{ |
/* Don't reassign section symbols. The direct reason is to prevent seg |
faults assigning back to const global symbols such as *ABS*, but it |
shouldn't happen anyway. */ |
if (LOCAL_SYMBOL_CHECK (s)) |
{ |
if (seg == reg_section) |
s = local_symbol_convert ((struct local_symbol *) s); |
else |
{ |
((struct local_symbol *) s)->lsy_section = seg; |
return; |
} |
} |
if (s->bsym->flags & BSF_SECTION_SYM) |
{ |
if (s->bsym->section != seg) |
abort (); |
} |
else |
s->bsym->section = seg; |
} |
void |
S_SET_EXTERNAL (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
if ((s->bsym->flags & BSF_WEAK) != 0) |
{ |
/* Let .weak override .global. */ |
return; |
} |
if (s->bsym->flags & BSF_SECTION_SYM) |
{ |
char * file; |
unsigned int line; |
/* Do not reassign section symbols. */ |
as_where (& file, & line); |
as_warn_where (file, line, |
_("section symbols are already global")); |
return; |
} |
#ifndef TC_GLOBAL_REGISTER_SYMBOL_OK |
if (S_GET_SEGMENT (s) == reg_section) |
{ |
as_bad ("can't make register symbol `%s' global", |
S_GET_NAME (s)); |
return; |
} |
#endif |
s->bsym->flags |= BSF_GLOBAL; |
s->bsym->flags &= ~(BSF_LOCAL | BSF_WEAK); |
#ifdef TE_PE |
if (! an_external_name && S_GET_NAME(s)[0] != '.') |
an_external_name = S_GET_NAME (s); |
#endif |
} |
void |
S_CLEAR_EXTERNAL (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return; |
if ((s->bsym->flags & BSF_WEAK) != 0) |
{ |
/* Let .weak override. */ |
return; |
} |
s->bsym->flags |= BSF_LOCAL; |
s->bsym->flags &= ~(BSF_GLOBAL | BSF_WEAK); |
} |
void |
S_SET_WEAK (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
#ifdef obj_set_weak_hook |
obj_set_weak_hook (s); |
#endif |
s->bsym->flags |= BSF_WEAK; |
s->bsym->flags &= ~(BSF_GLOBAL | BSF_LOCAL); |
} |
void |
S_SET_WEAKREFR (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
s->sy_flags.sy_weakrefr = 1; |
/* If the alias was already used, make sure we mark the target as |
used as well, otherwise it might be dropped from the symbol |
table. This may have unintended side effects if the alias is |
later redirected to another symbol, such as keeping the unused |
previous target in the symbol table. Since it will be weak, it's |
not a big deal. */ |
if (s->sy_flags.sy_used) |
symbol_mark_used (s->sy_value.X_add_symbol); |
} |
void |
S_CLEAR_WEAKREFR (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return; |
s->sy_flags.sy_weakrefr = 0; |
} |
void |
S_SET_WEAKREFD (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
s->sy_flags.sy_weakrefd = 1; |
S_SET_WEAK (s); |
} |
void |
S_CLEAR_WEAKREFD (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return; |
if (s->sy_flags.sy_weakrefd) |
{ |
s->sy_flags.sy_weakrefd = 0; |
/* If a weakref target symbol is weak, then it was never |
referenced directly before, not even in a .global directive, |
so decay it to local. If it remains undefined, it will be |
later turned into a global, like any other undefined |
symbol. */ |
if (s->bsym->flags & BSF_WEAK) |
{ |
#ifdef obj_clear_weak_hook |
obj_clear_weak_hook (s); |
#endif |
s->bsym->flags &= ~BSF_WEAK; |
s->bsym->flags |= BSF_LOCAL; |
} |
} |
} |
void |
S_SET_THREAD_LOCAL (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
if (bfd_is_com_section (s->bsym->section) |
&& (s->bsym->flags & BSF_THREAD_LOCAL) != 0) |
return; |
s->bsym->flags |= BSF_THREAD_LOCAL; |
if ((s->bsym->flags & BSF_FUNCTION) != 0) |
as_bad (_("Accessing function `%s' as thread-local object"), |
S_GET_NAME (s)); |
else if (! bfd_is_und_section (s->bsym->section) |
&& (s->bsym->section->flags & SEC_THREAD_LOCAL) == 0) |
as_bad (_("Accessing `%s' as thread-local object"), |
S_GET_NAME (s)); |
} |
void |
S_SET_NAME (symbolS *s, const char *name) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
{ |
((struct local_symbol *) s)->lsy_name = name; |
return; |
} |
s->bsym->name = name; |
} |
void |
S_SET_VOLATILE (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
s->sy_flags.sy_volatile = 1; |
} |
void |
S_CLEAR_VOLATILE (symbolS *s) |
{ |
if (!LOCAL_SYMBOL_CHECK (s)) |
s->sy_flags.sy_volatile = 0; |
} |
void |
S_SET_FORWARD_REF (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
s->sy_flags.sy_forward_ref = 1; |
} |
/* Return the previous symbol in a chain. */ |
symbolS * |
symbol_previous (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
abort (); |
return s->sy_previous; |
} |
/* Return the next symbol in a chain. */ |
symbolS * |
symbol_next (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
abort (); |
return s->sy_next; |
} |
/* Return a pointer to the value of a symbol as an expression. */ |
expressionS * |
symbol_get_value_expression (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
return &s->sy_value; |
} |
/* Set the value of a symbol to an expression. */ |
void |
symbol_set_value_expression (symbolS *s, const expressionS *exp) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
s->sy_value = *exp; |
S_CLEAR_WEAKREFR (s); |
} |
/* Return whether 2 symbols are the same. */ |
int |
symbol_same_p (symbolS *s1, symbolS *s2) |
{ |
if (s1->sy_flags.sy_local_symbol |
&& local_symbol_converted_p ((struct local_symbol *) s1)) |
s1 = local_symbol_get_real_symbol ((struct local_symbol *) s1); |
if (s2->sy_flags.sy_local_symbol |
&& local_symbol_converted_p ((struct local_symbol *) s2)) |
s2 = local_symbol_get_real_symbol ((struct local_symbol *) s2); |
return s1 == s2; |
} |
/* Return a pointer to the X_add_number component of a symbol. */ |
offsetT * |
symbol_X_add_number (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return (offsetT *) &((struct local_symbol *) s)->lsy_value; |
return &s->sy_value.X_add_number; |
} |
/* Set the value of SYM to the current position in the current segment. */ |
void |
symbol_set_value_now (symbolS *sym) |
{ |
S_SET_SEGMENT (sym, now_seg); |
S_SET_VALUE (sym, frag_now_fix ()); |
symbol_set_frag (sym, frag_now); |
} |
/* Set the frag of a symbol. */ |
void |
symbol_set_frag (symbolS *s, fragS *f) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
{ |
local_symbol_set_frag ((struct local_symbol *) s, f); |
return; |
} |
s->sy_frag = f; |
S_CLEAR_WEAKREFR (s); |
} |
/* Return the frag of a symbol. */ |
fragS * |
symbol_get_frag (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return local_symbol_get_frag ((struct local_symbol *) s); |
return s->sy_frag; |
} |
/* Mark a symbol as having been used. */ |
void |
symbol_mark_used (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return; |
s->sy_flags.sy_used = 1; |
if (S_IS_WEAKREFR (s)) |
symbol_mark_used (s->sy_value.X_add_symbol); |
} |
/* Clear the mark of whether a symbol has been used. */ |
void |
symbol_clear_used (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
s->sy_flags.sy_used = 0; |
} |
/* Return whether a symbol has been used. */ |
int |
symbol_used_p (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 1; |
return s->sy_flags.sy_used; |
} |
/* Mark a symbol as having been used in a reloc. */ |
void |
symbol_mark_used_in_reloc (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
s->sy_flags.sy_used_in_reloc = 1; |
} |
/* Clear the mark of whether a symbol has been used in a reloc. */ |
void |
symbol_clear_used_in_reloc (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return; |
s->sy_flags.sy_used_in_reloc = 0; |
} |
/* Return whether a symbol has been used in a reloc. */ |
int |
symbol_used_in_reloc_p (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
return s->sy_flags.sy_used_in_reloc; |
} |
/* Mark a symbol as an MRI common symbol. */ |
void |
symbol_mark_mri_common (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
s->sy_flags.sy_mri_common = 1; |
} |
/* Clear the mark of whether a symbol is an MRI common symbol. */ |
void |
symbol_clear_mri_common (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return; |
s->sy_flags.sy_mri_common = 0; |
} |
/* Return whether a symbol is an MRI common symbol. */ |
int |
symbol_mri_common_p (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
return s->sy_flags.sy_mri_common; |
} |
/* Mark a symbol as having been written. */ |
void |
symbol_mark_written (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return; |
s->sy_flags.sy_written = 1; |
} |
/* Clear the mark of whether a symbol has been written. */ |
void |
symbol_clear_written (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return; |
s->sy_flags.sy_written = 0; |
} |
/* Return whether a symbol has been written. */ |
int |
symbol_written_p (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
return s->sy_flags.sy_written; |
} |
/* Mark a symbol has having been resolved. */ |
void |
symbol_mark_resolved (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
{ |
local_symbol_mark_resolved ((struct local_symbol *) s); |
return; |
} |
s->sy_flags.sy_resolved = 1; |
} |
/* Return whether a symbol has been resolved. */ |
int |
symbol_resolved_p (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return local_symbol_resolved_p ((struct local_symbol *) s); |
return s->sy_flags.sy_resolved; |
} |
/* Return whether a symbol is a section symbol. */ |
int |
symbol_section_p (symbolS *s ATTRIBUTE_UNUSED) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
return (s->bsym->flags & BSF_SECTION_SYM) != 0; |
} |
/* Return whether a symbol is equated to another symbol. */ |
int |
symbol_equated_p (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
return s->sy_value.X_op == O_symbol; |
} |
/* Return whether a symbol is equated to another symbol, and should be |
treated specially when writing out relocs. */ |
int |
symbol_equated_reloc_p (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
/* X_op_symbol, normally not used for O_symbol, is set by |
resolve_symbol_value to flag expression syms that have been |
equated. */ |
return (s->sy_value.X_op == O_symbol |
#if defined (OBJ_COFF) && defined (TE_PE) |
&& ! S_IS_WEAK (s) |
#endif |
&& ((s->sy_flags.sy_resolved && s->sy_value.X_op_symbol != NULL) |
|| ! S_IS_DEFINED (s) |
|| S_IS_COMMON (s))); |
} |
/* Return whether a symbol has a constant value. */ |
int |
symbol_constant_p (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 1; |
return s->sy_value.X_op == O_constant; |
} |
/* Return whether a symbol was cloned and thus removed from the global |
symbol list. */ |
int |
symbol_shadow_p (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
return 0; |
return s->sy_next == s; |
} |
/* Return the BFD symbol for a symbol. */ |
asymbol * |
symbol_get_bfdsym (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
return s->bsym; |
} |
/* Set the BFD symbol for a symbol. */ |
void |
symbol_set_bfdsym (symbolS *s, asymbol *bsym) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
/* Usually, it is harmless to reset a symbol to a BFD section |
symbol. For example, obj_elf_change_section sets the BFD symbol |
of an old symbol with the newly created section symbol. But when |
we have multiple sections with the same name, the newly created |
section may have the same name as an old section. We check if the |
old symbol has been already marked as a section symbol before |
resetting it. */ |
if ((s->bsym->flags & BSF_SECTION_SYM) == 0) |
s->bsym = bsym; |
/* else XXX - What do we do now ? */ |
} |
#ifdef OBJ_SYMFIELD_TYPE |
/* Get a pointer to the object format information for a symbol. */ |
OBJ_SYMFIELD_TYPE * |
symbol_get_obj (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
return &s->sy_obj; |
} |
/* Set the object format information for a symbol. */ |
void |
symbol_set_obj (symbolS *s, OBJ_SYMFIELD_TYPE *o) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
s->sy_obj = *o; |
} |
#endif /* OBJ_SYMFIELD_TYPE */ |
#ifdef TC_SYMFIELD_TYPE |
/* Get a pointer to the processor information for a symbol. */ |
TC_SYMFIELD_TYPE * |
symbol_get_tc (symbolS *s) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
return &s->sy_tc; |
} |
/* Set the processor information for a symbol. */ |
void |
symbol_set_tc (symbolS *s, TC_SYMFIELD_TYPE *o) |
{ |
if (LOCAL_SYMBOL_CHECK (s)) |
s = local_symbol_convert ((struct local_symbol *) s); |
s->sy_tc = *o; |
} |
#endif /* TC_SYMFIELD_TYPE */ |
void |
symbol_begin (void) |
{ |
symbol_lastP = NULL; |
symbol_rootP = NULL; /* In case we have 0 symbols (!!) */ |
sy_hash = hash_new (); |
local_hash = hash_new (); |
memset ((char *) (&abs_symbol), '\0', sizeof (abs_symbol)); |
#if defined (EMIT_SECTION_SYMBOLS) || !defined (RELOC_REQUIRES_SYMBOL) |
abs_symbol.bsym = bfd_abs_section_ptr->symbol; |
#endif |
abs_symbol.sy_value.X_op = O_constant; |
abs_symbol.sy_frag = &zero_address_frag; |
if (LOCAL_LABELS_FB) |
fb_label_init (); |
} |
void |
dot_symbol_init (void) |
{ |
dot_symbol.bsym = bfd_make_empty_symbol (stdoutput); |
if (dot_symbol.bsym == NULL) |
as_fatal ("bfd_make_empty_symbol: %s", bfd_errmsg (bfd_get_error ())); |
dot_symbol.bsym->name = "."; |
dot_symbol.sy_flags.sy_forward_ref = 1; |
dot_symbol.sy_value.X_op = O_constant; |
} |
int indent_level; |
/* Maximum indent level. |
Available for modification inside a gdb session. */ |
static int max_indent_level = 8; |
void |
print_symbol_value_1 (FILE *file, symbolS *sym) |
{ |
const char *name = S_GET_NAME (sym); |
if (!name || !name[0]) |
name = "(unnamed)"; |
fprintf (file, "sym "); |
fprintf_vma (file, (bfd_vma) ((bfd_hostptr_t) sym)); |
fprintf (file, " %s", name); |
if (LOCAL_SYMBOL_CHECK (sym)) |
{ |
struct local_symbol *locsym = (struct local_symbol *) sym; |
if (local_symbol_get_frag (locsym) != & zero_address_frag |
&& local_symbol_get_frag (locsym) != NULL) |
{ |
fprintf (file, " frag "); |
fprintf_vma (file, (bfd_vma) ((bfd_hostptr_t) local_symbol_get_frag (locsym))); |
} |
if (local_symbol_resolved_p (locsym)) |
fprintf (file, " resolved"); |
fprintf (file, " local"); |
} |
else |
{ |
if (sym->sy_frag != &zero_address_frag) |
{ |
fprintf (file, " frag "); |
fprintf_vma (file, (bfd_vma) ((bfd_hostptr_t) sym->sy_frag)); |
} |
if (sym->sy_flags.sy_written) |
fprintf (file, " written"); |
if (sym->sy_flags.sy_resolved) |
fprintf (file, " resolved"); |
else if (sym->sy_flags.sy_resolving) |
fprintf (file, " resolving"); |
if (sym->sy_flags.sy_used_in_reloc) |
fprintf (file, " used-in-reloc"); |
if (sym->sy_flags.sy_used) |
fprintf (file, " used"); |
if (S_IS_LOCAL (sym)) |
fprintf (file, " local"); |
if (S_IS_EXTERNAL (sym)) |
fprintf (file, " extern"); |
if (S_IS_WEAK (sym)) |
fprintf (file, " weak"); |
if (S_IS_DEBUG (sym)) |
fprintf (file, " debug"); |
if (S_IS_DEFINED (sym)) |
fprintf (file, " defined"); |
} |
if (S_IS_WEAKREFR (sym)) |
fprintf (file, " weakrefr"); |
if (S_IS_WEAKREFD (sym)) |
fprintf (file, " weakrefd"); |
fprintf (file, " %s", segment_name (S_GET_SEGMENT (sym))); |
if (symbol_resolved_p (sym)) |
{ |
segT s = S_GET_SEGMENT (sym); |
if (s != undefined_section |
&& s != expr_section) |
fprintf (file, " %lx", (unsigned long) S_GET_VALUE (sym)); |
} |
else if (indent_level < max_indent_level |
&& S_GET_SEGMENT (sym) != undefined_section) |
{ |
indent_level++; |
fprintf (file, "\n%*s<", indent_level * 4, ""); |
if (LOCAL_SYMBOL_CHECK (sym)) |
fprintf (file, "constant %lx", |
(unsigned long) ((struct local_symbol *) sym)->lsy_value); |
else |
print_expr_1 (file, &sym->sy_value); |
fprintf (file, ">"); |
indent_level--; |
} |
fflush (file); |
} |
void |
print_symbol_value (symbolS *sym) |
{ |
indent_level = 0; |
print_symbol_value_1 (stderr, sym); |
fprintf (stderr, "\n"); |
} |
static void |
print_binary (FILE *file, const char *name, expressionS *exp) |
{ |
indent_level++; |
fprintf (file, "%s\n%*s<", name, indent_level * 4, ""); |
print_symbol_value_1 (file, exp->X_add_symbol); |
fprintf (file, ">\n%*s<", indent_level * 4, ""); |
print_symbol_value_1 (file, exp->X_op_symbol); |
fprintf (file, ">"); |
indent_level--; |
} |
void |
print_expr_1 (FILE *file, expressionS *exp) |
{ |
fprintf (file, "expr "); |
fprintf_vma (file, (bfd_vma) ((bfd_hostptr_t) exp)); |
fprintf (file, " "); |
switch (exp->X_op) |
{ |
case O_illegal: |
fprintf (file, "illegal"); |
break; |
case O_absent: |
fprintf (file, "absent"); |
break; |
case O_constant: |
fprintf (file, "constant %lx", (unsigned long) exp->X_add_number); |
break; |
case O_symbol: |
indent_level++; |
fprintf (file, "symbol\n%*s<", indent_level * 4, ""); |
print_symbol_value_1 (file, exp->X_add_symbol); |
fprintf (file, ">"); |
maybe_print_addnum: |
if (exp->X_add_number) |
fprintf (file, "\n%*s%lx", indent_level * 4, "", |
(unsigned long) exp->X_add_number); |
indent_level--; |
break; |
case O_register: |
fprintf (file, "register #%d", (int) exp->X_add_number); |
break; |
case O_big: |
fprintf (file, "big"); |
break; |
case O_uminus: |
fprintf (file, "uminus -<"); |
indent_level++; |
print_symbol_value_1 (file, exp->X_add_symbol); |
fprintf (file, ">"); |
goto maybe_print_addnum; |
case O_bit_not: |
fprintf (file, "bit_not"); |
break; |
case O_multiply: |
print_binary (file, "multiply", exp); |
break; |
case O_divide: |
print_binary (file, "divide", exp); |
break; |
case O_modulus: |
print_binary (file, "modulus", exp); |
break; |
case O_left_shift: |
print_binary (file, "lshift", exp); |
break; |
case O_right_shift: |
print_binary (file, "rshift", exp); |
break; |
case O_bit_inclusive_or: |
print_binary (file, "bit_ior", exp); |
break; |
case O_bit_exclusive_or: |
print_binary (file, "bit_xor", exp); |
break; |
case O_bit_and: |
print_binary (file, "bit_and", exp); |
break; |
case O_eq: |
print_binary (file, "eq", exp); |
break; |
case O_ne: |
print_binary (file, "ne", exp); |
break; |
case O_lt: |
print_binary (file, "lt", exp); |
break; |
case O_le: |
print_binary (file, "le", exp); |
break; |
case O_ge: |
print_binary (file, "ge", exp); |
break; |
case O_gt: |
print_binary (file, "gt", exp); |
break; |
case O_logical_and: |
print_binary (file, "logical_and", exp); |
break; |
case O_logical_or: |
print_binary (file, "logical_or", exp); |
break; |
case O_add: |
indent_level++; |
fprintf (file, "add\n%*s<", indent_level * 4, ""); |
print_symbol_value_1 (file, exp->X_add_symbol); |
fprintf (file, ">\n%*s<", indent_level * 4, ""); |
print_symbol_value_1 (file, exp->X_op_symbol); |
fprintf (file, ">"); |
goto maybe_print_addnum; |
case O_subtract: |
indent_level++; |
fprintf (file, "subtract\n%*s<", indent_level * 4, ""); |
print_symbol_value_1 (file, exp->X_add_symbol); |
fprintf (file, ">\n%*s<", indent_level * 4, ""); |
print_symbol_value_1 (file, exp->X_op_symbol); |
fprintf (file, ">"); |
goto maybe_print_addnum; |
default: |
fprintf (file, "{unknown opcode %d}", (int) exp->X_op); |
break; |
} |
fflush (stdout); |
} |
void |
print_expr (expressionS *exp) |
{ |
print_expr_1 (stderr, exp); |
fprintf (stderr, "\n"); |
} |
void |
symbol_print_statistics (FILE *file) |
{ |
hash_print_statistics (file, "symbol table", sy_hash); |
hash_print_statistics (file, "mini local symbol table", local_hash); |
fprintf (file, "%lu mini local symbols created, %lu converted\n", |
local_symbol_count, local_symbol_conversion_count); |
} |
#ifdef OBJ_COMPLEX_RELC |
/* Convert given symbol to a new complex-relocation symbol name. This |
may be a recursive function, since it might be called for non-leaf |
nodes (plain symbols) in the expression tree. The caller owns the |
returning string, so should free it eventually. Errors are |
indicated via as_bad and a NULL return value. The given symbol |
is marked with sy_used_in_reloc. */ |
char * |
symbol_relc_make_sym (symbolS * sym) |
{ |
char * terminal = NULL; |
const char * sname; |
char typetag; |
int sname_len; |
gas_assert (sym != NULL); |
/* Recurse to symbol_relc_make_expr if this symbol |
is defined as an expression or a plain value. */ |
if ( S_GET_SEGMENT (sym) == expr_section |
|| S_GET_SEGMENT (sym) == absolute_section) |
return symbol_relc_make_expr (& sym->sy_value); |
/* This may be a "fake symbol" L0\001, referring to ".". |
Write out a special null symbol to refer to this position. */ |
if (! strcmp (S_GET_NAME (sym), FAKE_LABEL_NAME)) |
return xstrdup ("."); |
/* We hope this is a plain leaf symbol. Construct the encoding |
as {S,s}II...:CCCCCCC.... |
where 'S'/'s' means section symbol / plain symbol |
III is decimal for the symbol name length |
CCC is the symbol name itself. */ |
symbol_mark_used_in_reloc (sym); |
sname = S_GET_NAME (sym); |
sname_len = strlen (sname); |
typetag = symbol_section_p (sym) ? 'S' : 's'; |
terminal = xmalloc (1 /* S or s */ |
+ 8 /* sname_len in decimal */ |
+ 1 /* _ spacer */ |
+ sname_len /* name itself */ |
+ 1 /* \0 */ ); |
sprintf (terminal, "%c%d:%s", typetag, sname_len, sname); |
return terminal; |
} |
/* Convert given value to a new complex-relocation symbol name. This |
is a non-recursive function, since it is be called for leaf nodes |
(plain values) in the expression tree. The caller owns the |
returning string, so should free() it eventually. No errors. */ |
char * |
symbol_relc_make_value (offsetT val) |
{ |
char * terminal = xmalloc (28); /* Enough for long long. */ |
terminal[0] = '#'; |
bfd_sprintf_vma (stdoutput, terminal + 1, val); |
return terminal; |
} |
/* Convert given expression to a new complex-relocation symbol name. |
This is a recursive function, since it traverses the entire given |
expression tree. The caller owns the returning string, so should |
free() it eventually. Errors are indicated via as_bad() and a NULL |
return value. */ |
char * |
symbol_relc_make_expr (expressionS * exp) |
{ |
char * opstr = NULL; /* Operator prefix string. */ |
int arity = 0; /* Arity of this operator. */ |
char * operands[3]; /* Up to three operands. */ |
char * concat_string = NULL; |
operands[0] = operands[1] = operands[2] = NULL; |
gas_assert (exp != NULL); |
/* Match known operators -> fill in opstr, arity, operands[] and fall |
through to construct subexpression fragments; may instead return |
string directly for leaf nodes. */ |
/* See expr.h for the meaning of all these enums. Many operators |
have an unnatural arity (X_add_number implicitly added). The |
conversion logic expands them to explicit "+" subexpressions. */ |
switch (exp->X_op) |
{ |
default: |
as_bad ("Unknown expression operator (enum %d)", exp->X_op); |
break; |
/* Leaf nodes. */ |
case O_constant: |
return symbol_relc_make_value (exp->X_add_number); |
case O_symbol: |
if (exp->X_add_number) |
{ |
arity = 2; |
opstr = "+"; |
operands[0] = symbol_relc_make_sym (exp->X_add_symbol); |
operands[1] = symbol_relc_make_value (exp->X_add_number); |
break; |
} |
else |
return symbol_relc_make_sym (exp->X_add_symbol); |
/* Helper macros for nesting nodes. */ |
#define HANDLE_XADD_OPT1(str_) \ |
if (exp->X_add_number) \ |
{ \ |
arity = 2; \ |
opstr = "+:" str_; \ |
operands[0] = symbol_relc_make_sym (exp->X_add_symbol); \ |
operands[1] = symbol_relc_make_value (exp->X_add_number); \ |
break; \ |
} \ |
else \ |
{ \ |
arity = 1; \ |
opstr = str_; \ |
operands[0] = symbol_relc_make_sym (exp->X_add_symbol); \ |
} \ |
break |
#define HANDLE_XADD_OPT2(str_) \ |
if (exp->X_add_number) \ |
{ \ |
arity = 3; \ |
opstr = "+:" str_; \ |
operands[0] = symbol_relc_make_sym (exp->X_add_symbol); \ |
operands[1] = symbol_relc_make_sym (exp->X_op_symbol); \ |
operands[2] = symbol_relc_make_value (exp->X_add_number); \ |
} \ |
else \ |
{ \ |
arity = 2; \ |
opstr = str_; \ |
operands[0] = symbol_relc_make_sym (exp->X_add_symbol); \ |
operands[1] = symbol_relc_make_sym (exp->X_op_symbol); \ |
} \ |
break |
/* Nesting nodes. */ |
case O_uminus: HANDLE_XADD_OPT1 ("0-"); |
case O_bit_not: HANDLE_XADD_OPT1 ("~"); |
case O_logical_not: HANDLE_XADD_OPT1 ("!"); |
case O_multiply: HANDLE_XADD_OPT2 ("*"); |
case O_divide: HANDLE_XADD_OPT2 ("/"); |
case O_modulus: HANDLE_XADD_OPT2 ("%"); |
case O_left_shift: HANDLE_XADD_OPT2 ("<<"); |
case O_right_shift: HANDLE_XADD_OPT2 (">>"); |
case O_bit_inclusive_or: HANDLE_XADD_OPT2 ("|"); |
case O_bit_exclusive_or: HANDLE_XADD_OPT2 ("^"); |
case O_bit_and: HANDLE_XADD_OPT2 ("&"); |
case O_add: HANDLE_XADD_OPT2 ("+"); |
case O_subtract: HANDLE_XADD_OPT2 ("-"); |
case O_eq: HANDLE_XADD_OPT2 ("=="); |
case O_ne: HANDLE_XADD_OPT2 ("!="); |
case O_lt: HANDLE_XADD_OPT2 ("<"); |
case O_le: HANDLE_XADD_OPT2 ("<="); |
case O_ge: HANDLE_XADD_OPT2 (">="); |
case O_gt: HANDLE_XADD_OPT2 (">"); |
case O_logical_and: HANDLE_XADD_OPT2 ("&&"); |
case O_logical_or: HANDLE_XADD_OPT2 ("||"); |
} |
/* Validate & reject early. */ |
if (arity >= 1 && ((operands[0] == NULL) || (strlen (operands[0]) == 0))) |
opstr = NULL; |
if (arity >= 2 && ((operands[1] == NULL) || (strlen (operands[1]) == 0))) |
opstr = NULL; |
if (arity >= 3 && ((operands[2] == NULL) || (strlen (operands[2]) == 0))) |
opstr = NULL; |
if (opstr == NULL) |
concat_string = NULL; |
else |
{ |
/* Allocate new string; include inter-operand padding gaps etc. */ |
concat_string = xmalloc (strlen (opstr) |
+ 1 |
+ (arity >= 1 ? (strlen (operands[0]) + 1 ) : 0) |
+ (arity >= 2 ? (strlen (operands[1]) + 1 ) : 0) |
+ (arity >= 3 ? (strlen (operands[2]) + 0 ) : 0) |
+ 1); |
gas_assert (concat_string != NULL); |
/* Format the thing. */ |
sprintf (concat_string, |
(arity == 0 ? "%s" : |
arity == 1 ? "%s:%s" : |
arity == 2 ? "%s:%s:%s" : |
/* arity == 3 */ "%s:%s:%s:%s"), |
opstr, operands[0], operands[1], operands[2]); |
} |
/* Free operand strings (not opstr). */ |
if (arity >= 1) xfree (operands[0]); |
if (arity >= 2) xfree (operands[1]); |
if (arity >= 3) xfree (operands[2]); |
return concat_string; |
} |
#endif |
/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 (¬es, sizeof (fixS)); |
fixP->fx_frag = frag; |
fixP->fx_where = where; |
fixP->fx_size = size; |
/* We've made fx_size a narrow field; check that it's wide enough. */ |
if (fixP->fx_size != size) |
{ |
as_bad (_("field fx_size too small to hold %d"), size); |
abort (); |
} |
fixP->fx_addsy = add_symbol; |
fixP->fx_subsy = sub_symbol; |
fixP->fx_offset = offset; |
fixP->fx_dot_value = dot_value; |
fixP->fx_dot_frag = dot_frag; |
fixP->fx_pcrel = pcrel; |
fixP->fx_r_type = r_type; |
fixP->fx_im_disp = 0; |
fixP->fx_pcrel_adjust = 0; |
fixP->fx_bit_fixP = 0; |
fixP->fx_addnumber = 0; |
fixP->fx_tcbit = 0; |
fixP->fx_tcbit2 = 0; |
fixP->fx_done = 0; |
fixP->fx_no_overflow = 0; |
fixP->fx_signed = 0; |
#ifdef USING_CGEN |
fixP->fx_cgen.insn = NULL; |
fixP->fx_cgen.opinfo = 0; |
#endif |
#ifdef TC_FIX_TYPE |
TC_INIT_FIX_DATA (fixP); |
#endif |
as_where (&fixP->fx_file, &fixP->fx_line); |
{ |
fixS **seg_fix_rootP = (frags_chained |
? &seg_info (now_seg)->fix_root |
: &frchain_now->fix_root); |
fixS **seg_fix_tailP = (frags_chained |
? &seg_info (now_seg)->fix_tail |
: &frchain_now->fix_tail); |
if (at_beginning) |
{ |
fixP->fx_next = *seg_fix_rootP; |
*seg_fix_rootP = fixP; |
if (fixP->fx_next == NULL) |
*seg_fix_tailP = fixP; |
} |
else |
{ |
fixP->fx_next = NULL; |
if (*seg_fix_tailP) |
(*seg_fix_tailP)->fx_next = fixP; |
else |
*seg_fix_rootP = fixP; |
*seg_fix_tailP = fixP; |
} |
} |
return fixP; |
} |
/* Create a fixup relative to a symbol (plus a constant). */ |
fixS * |
fix_new (fragS *frag, /* Which frag? */ |
int where, /* Where in that frag? */ |
int size, /* 1, 2, or 4 usually. */ |
symbolS *add_symbol, /* X_add_symbol. */ |
offsetT offset, /* X_add_number. */ |
int pcrel, /* TRUE if PC-relative relocation. */ |
RELOC_ENUM r_type /* Relocation type. */) |
{ |
return fix_new_internal (frag, where, size, add_symbol, |
(symbolS *) NULL, offset, pcrel, r_type, FALSE); |
} |
/* Create a fixup for an expression. Currently we only support fixups |
for difference expressions. That is itself more than most object |
file formats support anyhow. */ |
fixS * |
fix_new_exp (fragS *frag, /* Which frag? */ |
int where, /* Where in that frag? */ |
int size, /* 1, 2, or 4 usually. */ |
expressionS *exp, /* Expression. */ |
int pcrel, /* TRUE if PC-relative relocation. */ |
RELOC_ENUM r_type /* Relocation type. */) |
{ |
symbolS *add = NULL; |
symbolS *sub = NULL; |
offsetT off = 0; |
switch (exp->X_op) |
{ |
case O_absent: |
break; |
case O_register: |
as_bad (_("register value used as expression")); |
break; |
case O_add: |
/* This comes up when _GLOBAL_OFFSET_TABLE_+(.-L0) is read, if |
the difference expression cannot immediately be reduced. */ |
{ |
symbolS *stmp = make_expr_symbol (exp); |
exp->X_op = O_symbol; |
exp->X_op_symbol = 0; |
exp->X_add_symbol = stmp; |
exp->X_add_number = 0; |
return fix_new_exp (frag, where, size, exp, pcrel, r_type); |
} |
case O_symbol_rva: |
add = exp->X_add_symbol; |
off = exp->X_add_number; |
r_type = BFD_RELOC_RVA; |
break; |
case O_uminus: |
sub = exp->X_add_symbol; |
off = exp->X_add_number; |
break; |
case O_subtract: |
sub = exp->X_op_symbol; |
/* Fall through. */ |
case O_symbol: |
add = exp->X_add_symbol; |
/* Fall through. */ |
case O_constant: |
off = exp->X_add_number; |
break; |
default: |
add = make_expr_symbol (exp); |
break; |
} |
return fix_new_internal (frag, where, size, add, sub, off, pcrel, |
r_type, FALSE); |
} |
/* Create a fixup at the beginning of FRAG. The arguments are the same |
as for fix_new, except that WHERE is implicitly 0. */ |
fixS * |
fix_at_start (fragS *frag, int size, symbolS *add_symbol, |
offsetT offset, int pcrel, RELOC_ENUM r_type) |
{ |
return fix_new_internal (frag, 0, size, add_symbol, |
(symbolS *) NULL, offset, pcrel, r_type, TRUE); |
} |
/* Generic function to determine whether a fixup requires a relocation. */ |
int |
generic_force_reloc (fixS *fix) |
{ |
if (fix->fx_r_type == BFD_RELOC_VTABLE_INHERIT |
|| fix->fx_r_type == BFD_RELOC_VTABLE_ENTRY) |
return 1; |
if (fix->fx_addsy == NULL) |
return 0; |
return S_FORCE_RELOC (fix->fx_addsy, fix->fx_subsy == NULL); |
} |
/* Append a string onto another string, bumping the pointer along. */ |
void |
append (char **charPP, char *fromP, unsigned long length) |
{ |
/* Don't trust memcpy() of 0 chars. */ |
if (length == 0) |
return; |
memcpy (*charPP, fromP, length); |
*charPP += length; |
} |
/* This routine records the largest alignment seen for each segment. |
If the beginning of the segment is aligned on the worst-case |
boundary, all of the other alignments within it will work. At |
least one object format really uses this info. */ |
void |
record_alignment (/* Segment to which alignment pertains. */ |
segT seg, |
/* Alignment, as a power of 2 (e.g., 1 => 2-byte |
boundary, 2 => 4-byte boundary, etc.) */ |
int align) |
{ |
if (seg == absolute_section) |
return; |
if ((unsigned int) align > bfd_get_section_alignment (stdoutput, seg)) |
bfd_set_section_alignment (stdoutput, seg, align); |
} |
int |
get_recorded_alignment (segT seg) |
{ |
if (seg == absolute_section) |
return 0; |
return bfd_get_section_alignment (stdoutput, seg); |
} |
/* Reset the section indices after removing the gas created sections. */ |
static void |
renumber_sections (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *countparg) |
{ |
int *countp = (int *) countparg; |
sec->index = *countp; |
++*countp; |
} |
static fragS * |
chain_frchains_together_1 (segT section, struct frchain *frchp) |
{ |
fragS dummy, *prev_frag = &dummy; |
fixS fix_dummy, *prev_fix = &fix_dummy; |
for (; frchp; frchp = frchp->frch_next) |
{ |
prev_frag->fr_next = frchp->frch_root; |
prev_frag = frchp->frch_last; |
gas_assert (prev_frag->fr_type != 0); |
if (frchp->fix_root != (fixS *) NULL) |
{ |
if (seg_info (section)->fix_root == (fixS *) NULL) |
seg_info (section)->fix_root = frchp->fix_root; |
prev_fix->fx_next = frchp->fix_root; |
seg_info (section)->fix_tail = frchp->fix_tail; |
prev_fix = frchp->fix_tail; |
} |
} |
gas_assert (prev_frag != &dummy |
&& prev_frag->fr_type != 0); |
prev_frag->fr_next = 0; |
return prev_frag; |
} |
static void |
chain_frchains_together (bfd *abfd ATTRIBUTE_UNUSED, |
segT section, |
void *xxx ATTRIBUTE_UNUSED) |
{ |
segment_info_type *info; |
/* BFD may have introduced its own sections without using |
subseg_new, so it is possible that seg_info is NULL. */ |
info = seg_info (section); |
if (info != (segment_info_type *) NULL) |
info->frchainP->frch_last |
= chain_frchains_together_1 (section, info->frchainP); |
/* Now that we've chained the frags together, we must add new fixups |
to the segment, not to the frag chain. */ |
frags_chained = 1; |
} |
static void |
cvt_frag_to_fill (segT sec ATTRIBUTE_UNUSED, fragS *fragP) |
{ |
switch (fragP->fr_type) |
{ |
case rs_align: |
case rs_align_code: |
case rs_align_test: |
case rs_org: |
case rs_space: |
#ifdef HANDLE_ALIGN |
HANDLE_ALIGN (fragP); |
#endif |
know (fragP->fr_next != NULL); |
fragP->fr_offset = (fragP->fr_next->fr_address |
- fragP->fr_address |
- fragP->fr_fix) / fragP->fr_var; |
if (fragP->fr_offset < 0) |
{ |
as_bad_where (fragP->fr_file, fragP->fr_line, |
_("attempt to .org/.space backwards? (%ld)"), |
(long) fragP->fr_offset); |
fragP->fr_offset = 0; |
} |
fragP->fr_type = rs_fill; |
break; |
case rs_fill: |
break; |
case rs_leb128: |
{ |
valueT value = S_GET_VALUE (fragP->fr_symbol); |
int size; |
size = output_leb128 (fragP->fr_literal + fragP->fr_fix, value, |
fragP->fr_subtype); |
fragP->fr_fix += size; |
fragP->fr_type = rs_fill; |
fragP->fr_var = 0; |
fragP->fr_offset = 0; |
fragP->fr_symbol = NULL; |
} |
break; |
case rs_cfa: |
eh_frame_convert_frag (fragP); |
break; |
case rs_dwarf2dbg: |
dwarf2dbg_convert_frag (fragP); |
break; |
case rs_machine_dependent: |
md_convert_frag (stdoutput, sec, fragP); |
gas_assert (fragP->fr_next == NULL |
|| ((offsetT) (fragP->fr_next->fr_address - fragP->fr_address) |
== fragP->fr_fix)); |
/* After md_convert_frag, we make the frag into a ".space 0". |
md_convert_frag() should set up any fixSs and constants |
required. */ |
frag_wane (fragP); |
break; |
#ifndef WORKING_DOT_WORD |
case rs_broken_word: |
{ |
struct broken_word *lie; |
if (fragP->fr_subtype) |
{ |
fragP->fr_fix += md_short_jump_size; |
for (lie = (struct broken_word *) (fragP->fr_symbol); |
lie && lie->dispfrag == fragP; |
lie = lie->next_broken_word) |
if (lie->added == 1) |
fragP->fr_fix += md_long_jump_size; |
} |
frag_wane (fragP); |
} |
break; |
#endif |
default: |
BAD_CASE (fragP->fr_type); |
break; |
} |
#ifdef md_frag_check |
md_frag_check (fragP); |
#endif |
} |
struct relax_seg_info |
{ |
int pass; |
int changed; |
}; |
static void |
relax_seg (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, void *xxx) |
{ |
segment_info_type *seginfo = seg_info (sec); |
struct relax_seg_info *info = (struct relax_seg_info *) xxx; |
if (seginfo && seginfo->frchainP |
&& relax_segment (seginfo->frchainP->frch_root, sec, info->pass)) |
info->changed = 1; |
} |
static void |
size_seg (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED) |
{ |
flagword flags; |
fragS *fragp; |
segment_info_type *seginfo; |
int x; |
valueT size, newsize; |
subseg_change (sec, 0); |
seginfo = seg_info (sec); |
if (seginfo && seginfo->frchainP) |
{ |
for (fragp = seginfo->frchainP->frch_root; fragp; fragp = fragp->fr_next) |
cvt_frag_to_fill (sec, fragp); |
for (fragp = seginfo->frchainP->frch_root; |
fragp->fr_next; |
fragp = fragp->fr_next) |
/* Walk to last elt. */ |
; |
size = fragp->fr_address + fragp->fr_fix; |
} |
else |
size = 0; |
flags = bfd_get_section_flags (abfd, sec); |
if (size == 0 && bfd_get_section_size (sec) != 0 && |
(flags & SEC_HAS_CONTENTS) != 0) |
return; |
if (size > 0 && ! seginfo->bss) |
flags |= SEC_HAS_CONTENTS; |
flags &= ~SEC_RELOC; |
x = bfd_set_section_flags (abfd, sec, flags); |
gas_assert (x); |
newsize = md_section_align (sec, size); |
x = bfd_set_section_size (abfd, sec, newsize); |
gas_assert (x); |
/* If the size had to be rounded up, add some padding in the last |
non-empty frag. */ |
gas_assert (newsize >= size); |
if (size != newsize) |
{ |
fragS *last = seginfo->frchainP->frch_last; |
fragp = seginfo->frchainP->frch_root; |
while (fragp->fr_next != last) |
fragp = fragp->fr_next; |
last->fr_address = size; |
if ((newsize - size) % fragp->fr_var == 0) |
fragp->fr_offset += (newsize - size) / fragp->fr_var; |
else |
/* If we hit this abort, it's likely due to subsegs_finish not |
providing sufficient alignment on the last frag, and the |
machine dependent code using alignment frags with fr_var |
greater than 1. */ |
abort (); |
} |
#ifdef tc_frob_section |
tc_frob_section (sec); |
#endif |
#ifdef obj_frob_section |
obj_frob_section (sec); |
#endif |
} |
#ifdef DEBUG2 |
static void |
dump_section_relocs (bfd *abfd ATTRIBUTE_UNUSED, asection *sec, FILE *stream) |
{ |
segment_info_type *seginfo = seg_info (sec); |
fixS *fixp = seginfo->fix_root; |
if (!fixp) |
return; |
fprintf (stream, "sec %s relocs:\n", sec->name); |
while (fixp) |
{ |
symbolS *s = fixp->fx_addsy; |
fprintf (stream, " %08lx: type %d ", (unsigned long) fixp, |
(int) fixp->fx_r_type); |
if (s == NULL) |
fprintf (stream, "no sym\n"); |
else |
{ |
print_symbol_value_1 (stream, s); |
fprintf (stream, "\n"); |
} |
fixp = fixp->fx_next; |
} |
} |
#else |
#define dump_section_relocs(ABFD,SEC,STREAM) ((void) 0) |
#endif |
#ifndef EMIT_SECTION_SYMBOLS |
#define EMIT_SECTION_SYMBOLS 1 |
#endif |
/* Resolve U.A.OFFSET_SYM and U.A.SYM fields of RELOC_LIST entries, |
and check for validity. Convert RELOC_LIST from using U.A fields |
to U.B fields. */ |
static void |
resolve_reloc_expr_symbols (void) |
{ |
bfd_vma addr_mask = 1; |
struct reloc_list *r; |
/* Avoid a shift by the width of type. */ |
addr_mask <<= bfd_arch_bits_per_address (stdoutput) - 1; |
addr_mask <<= 1; |
addr_mask -= 1; |
for (r = reloc_list; r; r = r->next) |
{ |
reloc_howto_type *howto = r->u.a.howto; |
expressionS *symval; |
symbolS *sym; |
bfd_vma offset, addend; |
asection *sec; |
resolve_symbol_value (r->u.a.offset_sym); |
symval = symbol_get_value_expression (r->u.a.offset_sym); |
offset = 0; |
sym = NULL; |
if (symval->X_op == O_constant) |
sym = r->u.a.offset_sym; |
else if (symval->X_op == O_symbol) |
{ |
sym = symval->X_add_symbol; |
offset = symval->X_add_number; |
symval = symbol_get_value_expression (symval->X_add_symbol); |
} |
if (sym == NULL |
|| symval->X_op != O_constant |
|| (sec = S_GET_SEGMENT (sym)) == NULL |
|| !SEG_NORMAL (sec)) |
{ |
as_bad_where (r->file, r->line, _("invalid offset expression")); |
sec = NULL; |
} |
else |
offset += S_GET_VALUE (sym); |
sym = NULL; |
addend = r->u.a.addend; |
if (r->u.a.sym != NULL) |
{ |
resolve_symbol_value (r->u.a.sym); |
symval = symbol_get_value_expression (r->u.a.sym); |
if (symval->X_op == O_constant) |
sym = r->u.a.sym; |
else if (symval->X_op == O_symbol) |
{ |
sym = symval->X_add_symbol; |
addend += symval->X_add_number; |
symval = symbol_get_value_expression (symval->X_add_symbol); |
} |
if (symval->X_op != O_constant) |
{ |
as_bad_where (r->file, r->line, _("invalid reloc expression")); |
sec = NULL; |
} |
else if (sym != NULL) |
{ |
/* Convert relocs against local symbols to refer to the |
corresponding section symbol plus offset instead. Keep |
PC-relative relocs of the REL variety intact though to |
prevent the offset from overflowing the relocated field, |
unless it has enough bits to cover the whole address |
space. */ |
if (S_IS_LOCAL (sym) && !symbol_section_p (sym) |
&& (sec->use_rela_p |
|| (howto->partial_inplace |
&& (!howto->pc_relative |
|| howto->src_mask == addr_mask)))) |
{ |
asection *symsec = S_GET_SEGMENT (sym); |
if (!(((symsec->flags & SEC_MERGE) != 0 |
&& addend != 0) |
|| (symsec->flags & SEC_THREAD_LOCAL) != 0)) |
{ |
addend += S_GET_VALUE (sym); |
sym = section_symbol (symsec); |
} |
} |
symbol_mark_used_in_reloc (sym); |
} |
} |
if (sym == NULL) |
{ |
if (abs_section_sym == NULL) |
abs_section_sym = section_symbol (absolute_section); |
sym = abs_section_sym; |
} |
r->u.b.sec = sec; |
r->u.b.s = symbol_get_bfdsym (sym); |
r->u.b.r.sym_ptr_ptr = &r->u.b.s; |
r->u.b.r.address = offset; |
r->u.b.r.addend = addend; |
r->u.b.r.howto = howto; |
} |
} |
/* This pass over fixups decides whether symbols can be replaced with |
section symbols. */ |
static void |
adjust_reloc_syms (bfd *abfd ATTRIBUTE_UNUSED, |
asection *sec, |
void *xxx ATTRIBUTE_UNUSED) |
{ |
segment_info_type *seginfo = seg_info (sec); |
fixS *fixp; |
if (seginfo == NULL) |
return; |
dump_section_relocs (abfd, sec, stderr); |
for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next) |
if (fixp->fx_done) |
/* Ignore it. */ |
; |
else if (fixp->fx_addsy) |
{ |
symbolS *sym; |
asection *symsec; |
#ifdef DEBUG5 |
fprintf (stderr, "\n\nadjusting fixup:\n"); |
print_fixup (fixp); |
#endif |
sym = fixp->fx_addsy; |
/* All symbols should have already been resolved at this |
point. It is possible to see unresolved expression |
symbols, though, since they are not in the regular symbol |
table. */ |
resolve_symbol_value (sym); |
if (fixp->fx_subsy != NULL) |
resolve_symbol_value (fixp->fx_subsy); |
/* If this symbol is equated to an undefined or common symbol, |
convert the fixup to being against that symbol. */ |
while (symbol_equated_reloc_p (sym) |
|| S_IS_WEAKREFR (sym)) |
{ |
symbolS *newsym = symbol_get_value_expression (sym)->X_add_symbol; |
if (sym == newsym) |
break; |
fixp->fx_offset += symbol_get_value_expression (sym)->X_add_number; |
fixp->fx_addsy = newsym; |
sym = newsym; |
} |
if (symbol_mri_common_p (sym)) |
{ |
fixp->fx_offset += S_GET_VALUE (sym); |
fixp->fx_addsy = symbol_get_value_expression (sym)->X_add_symbol; |
continue; |
} |
/* If the symbol is undefined, common, weak, or global (ELF |
shared libs), we can't replace it with the section symbol. */ |
if (S_FORCE_RELOC (fixp->fx_addsy, 1)) |
continue; |
/* Is there some other (target cpu dependent) reason we can't adjust |
this one? (E.g. relocations involving function addresses on |
the PA. */ |
#ifdef tc_fix_adjustable |
if (! tc_fix_adjustable (fixp)) |
continue; |
#endif |
/* Since we're reducing to section symbols, don't attempt to reduce |
anything that's already using one. */ |
if (symbol_section_p (sym)) |
continue; |
symsec = S_GET_SEGMENT (sym); |
if (symsec == NULL) |
abort (); |
if (bfd_is_abs_section (symsec)) |
{ |
/* The fixup_segment routine normally will not use this |
symbol in a relocation. */ |
continue; |
} |
/* Don't try to reduce relocs which refer to non-local symbols |
in .linkonce sections. It can lead to confusion when a |
debugging section refers to a .linkonce section. I hope |
this will always be correct. */ |
if (symsec != sec && ! S_IS_LOCAL (sym)) |
{ |
if ((symsec->flags & SEC_LINK_ONCE) != 0 |
|| (IS_ELF |
/* The GNU toolchain uses an extension for ELF: a |
section beginning with the magic string |
.gnu.linkonce is a linkonce section. */ |
&& strncmp (segment_name (symsec), ".gnu.linkonce", |
sizeof ".gnu.linkonce" - 1) == 0)) |
continue; |
} |
/* Never adjust a reloc against local symbol in a merge section |
with non-zero addend. */ |
if ((symsec->flags & SEC_MERGE) != 0 |
&& (fixp->fx_offset != 0 || fixp->fx_subsy != NULL)) |
continue; |
/* Never adjust a reloc against TLS local symbol. */ |
if ((symsec->flags & SEC_THREAD_LOCAL) != 0) |
continue; |
/* We refetch the segment when calling section_symbol, rather |
than using symsec, because S_GET_VALUE may wind up changing |
the section when it calls resolve_symbol_value. */ |
fixp->fx_offset += S_GET_VALUE (sym); |
fixp->fx_addsy = section_symbol (S_GET_SEGMENT (sym)); |
#ifdef DEBUG5 |
fprintf (stderr, "\nadjusted fixup:\n"); |
print_fixup (fixp); |
#endif |
} |
dump_section_relocs (abfd, sec, stderr); |
} |
/* fixup_segment() |
Go through all the fixS's in a segment and see which ones can be |
handled now. (These consist of fixS where we have since discovered |
the value of a symbol, or the address of the frag involved.) |
For each one, call md_apply_fix to put the fix into the frag data. |
Ones that we couldn't completely handle here will be output later |
by emit_relocations. */ |
static void |
fixup_segment (fixS *fixP, segT this_segment) |
{ |
valueT add_number; |
fragS *fragP; |
segT add_symbol_segment = absolute_section; |
if (fixP != NULL && abs_section_sym == NULL) |
abs_section_sym = section_symbol (absolute_section); |
/* If the linker is doing the relaxing, we must not do any fixups. |
Well, strictly speaking that's not true -- we could do any that |
are PC-relative and don't cross regions that could change size. |
And for the i960 we might be able to turn callx/callj into bal |
anyways in cases where we know the maximum displacement. */ |
if (linkrelax && TC_LINKRELAX_FIXUP (this_segment)) |
{ |
for (; fixP; fixP = fixP->fx_next) |
if (!fixP->fx_done) |
{ |
if (fixP->fx_addsy == NULL) |
{ |
/* There was no symbol required by this relocation. |
However, BFD doesn't really handle relocations |
without symbols well. So fake up a local symbol in |
the absolute section. */ |
fixP->fx_addsy = abs_section_sym; |
} |
symbol_mark_used_in_reloc (fixP->fx_addsy); |
if (fixP->fx_subsy != NULL) |
symbol_mark_used_in_reloc (fixP->fx_subsy); |
} |
return; |
} |
for (; fixP; fixP = fixP->fx_next) |
{ |
#ifdef DEBUG5 |
fprintf (stderr, "\nprocessing fixup:\n"); |
print_fixup (fixP); |
#endif |
fragP = fixP->fx_frag; |
know (fragP); |
#ifdef TC_VALIDATE_FIX |
TC_VALIDATE_FIX (fixP, this_segment, skip); |
#endif |
add_number = fixP->fx_offset; |
if (fixP->fx_addsy != NULL) |
add_symbol_segment = S_GET_SEGMENT (fixP->fx_addsy); |
if (fixP->fx_subsy != NULL) |
{ |
segT sub_symbol_segment; |
resolve_symbol_value (fixP->fx_subsy); |
sub_symbol_segment = S_GET_SEGMENT (fixP->fx_subsy); |
if (fixP->fx_addsy != NULL |
&& sub_symbol_segment == add_symbol_segment |
&& !S_FORCE_RELOC (fixP->fx_addsy, 0) |
&& !S_FORCE_RELOC (fixP->fx_subsy, 0) |
&& !TC_FORCE_RELOCATION_SUB_SAME (fixP, add_symbol_segment)) |
{ |
add_number += S_GET_VALUE (fixP->fx_addsy); |
add_number -= S_GET_VALUE (fixP->fx_subsy); |
fixP->fx_offset = add_number; |
fixP->fx_addsy = NULL; |
fixP->fx_subsy = NULL; |
#ifdef TC_M68K |
/* See the comment below about 68k weirdness. */ |
fixP->fx_pcrel = 0; |
#endif |
} |
else if (sub_symbol_segment == absolute_section |
&& !S_FORCE_RELOC (fixP->fx_subsy, 0) |
&& !TC_FORCE_RELOCATION_SUB_ABS (fixP, add_symbol_segment)) |
{ |
add_number -= S_GET_VALUE (fixP->fx_subsy); |
fixP->fx_offset = add_number; |
fixP->fx_subsy = NULL; |
} |
else if (sub_symbol_segment == this_segment |
&& !S_FORCE_RELOC (fixP->fx_subsy, 0) |
&& !TC_FORCE_RELOCATION_SUB_LOCAL (fixP, add_symbol_segment)) |
{ |
add_number -= S_GET_VALUE (fixP->fx_subsy); |
fixP->fx_offset = (add_number + fixP->fx_dot_value |
+ fixP->fx_dot_frag->fr_address); |
/* Make it pc-relative. If the back-end code has not |
selected a pc-relative reloc, cancel the adjustment |
we do later on all pc-relative relocs. */ |
if (0 |
#ifdef TC_M68K |
/* Do this for m68k even if it's already described |
as pc-relative. On the m68k, an operand of |
"pc@(foo-.-2)" should address "foo" in a |
pc-relative mode. */ |
|| 1 |
#endif |
|| !fixP->fx_pcrel) |
add_number += MD_PCREL_FROM_SECTION (fixP, this_segment); |
fixP->fx_subsy = NULL; |
fixP->fx_pcrel = 1; |
} |
else if (!TC_VALIDATE_FIX_SUB (fixP, add_symbol_segment)) |
{ |
if (!md_register_arithmetic |
&& (add_symbol_segment == reg_section |
|| sub_symbol_segment == reg_section)) |
as_bad_where (fixP->fx_file, fixP->fx_line, |
_("register value used as expression")); |
else |
as_bad_where (fixP->fx_file, fixP->fx_line, |
_("can't resolve `%s' {%s section} - `%s' {%s section}"), |
fixP->fx_addsy ? S_GET_NAME (fixP->fx_addsy) : "0", |
segment_name (add_symbol_segment), |
S_GET_NAME (fixP->fx_subsy), |
segment_name (sub_symbol_segment)); |
} |
else if (sub_symbol_segment != undefined_section |
&& ! bfd_is_com_section (sub_symbol_segment) |
&& MD_APPLY_SYM_VALUE (fixP)) |
add_number -= S_GET_VALUE (fixP->fx_subsy); |
} |
if (fixP->fx_addsy) |
{ |
if (add_symbol_segment == this_segment |
&& !S_FORCE_RELOC (fixP->fx_addsy, 0) |
&& !TC_FORCE_RELOCATION_LOCAL (fixP)) |
{ |
/* This fixup was made when the symbol's segment was |
SEG_UNKNOWN, but it is now in the local segment. |
So we know how to do the address without relocation. */ |
add_number += S_GET_VALUE (fixP->fx_addsy); |
fixP->fx_offset = add_number; |
if (fixP->fx_pcrel) |
add_number -= MD_PCREL_FROM_SECTION (fixP, this_segment); |
fixP->fx_addsy = NULL; |
fixP->fx_pcrel = 0; |
} |
else if (add_symbol_segment == absolute_section |
&& !S_FORCE_RELOC (fixP->fx_addsy, 0) |
&& !TC_FORCE_RELOCATION_ABS (fixP)) |
{ |
add_number += S_GET_VALUE (fixP->fx_addsy); |
fixP->fx_offset = add_number; |
fixP->fx_addsy = NULL; |
} |
else if (add_symbol_segment != undefined_section |
&& ! bfd_is_com_section (add_symbol_segment) |
&& MD_APPLY_SYM_VALUE (fixP)) |
add_number += S_GET_VALUE (fixP->fx_addsy); |
} |
if (fixP->fx_pcrel) |
{ |
add_number -= MD_PCREL_FROM_SECTION (fixP, this_segment); |
if (!fixP->fx_done && fixP->fx_addsy == NULL) |
{ |
/* There was no symbol required by this relocation. |
However, BFD doesn't really handle relocations |
without symbols well. So fake up a local symbol in |
the absolute section. */ |
fixP->fx_addsy = abs_section_sym; |
} |
} |
if (!fixP->fx_done) |
md_apply_fix (fixP, &add_number, this_segment); |
if (!fixP->fx_done) |
{ |
if (fixP->fx_addsy == NULL) |
fixP->fx_addsy = abs_section_sym; |
symbol_mark_used_in_reloc (fixP->fx_addsy); |
if (fixP->fx_subsy != NULL) |
symbol_mark_used_in_reloc (fixP->fx_subsy); |
} |
if (!fixP->fx_bit_fixP && !fixP->fx_no_overflow && fixP->fx_size != 0) |
{ |
if (fixP->fx_size < sizeof (valueT)) |
{ |
valueT mask; |
mask = 0; |
mask--; /* Set all bits to one. */ |
mask <<= fixP->fx_size * 8 - (fixP->fx_signed ? 1 : 0); |
if ((add_number & mask) != 0 && (add_number & mask) != mask) |
{ |
char buf[50], buf2[50]; |
sprint_value (buf, fragP->fr_address + fixP->fx_where); |
if (add_number > 1000) |
sprint_value (buf2, add_number); |
else |
sprintf (buf2, "%ld", (long) add_number); |
as_bad_where (fixP->fx_file, fixP->fx_line, |
_("value of %s too large for field of %d bytes at %s"), |
buf2, fixP->fx_size, buf); |
} /* Generic error checking. */ |
} |
#ifdef WARN_SIGNED_OVERFLOW_WORD |
/* Warn if a .word value is too large when treated as a signed |
number. We already know it is not too negative. This is to |
catch over-large switches generated by gcc on the 68k. */ |
if (!flag_signed_overflow_ok |
&& fixP->fx_size == 2 |
&& add_number > 0x7fff) |
as_bad_where (fixP->fx_file, fixP->fx_line, |
_("signed .word overflow; switch may be too large; %ld at 0x%lx"), |
(long) add_number, |
(long) (fragP->fr_address + fixP->fx_where)); |
#endif |
} /* Not a bit fix. */ |
#ifdef TC_VALIDATE_FIX |
skip: ATTRIBUTE_UNUSED_LABEL |
; |
#endif |
#ifdef DEBUG5 |
fprintf (stderr, "result:\n"); |
print_fixup (fixP); |
#endif |
} /* For each fixS in this segment. */ |
} |
static void |
fix_segment (bfd *abfd ATTRIBUTE_UNUSED, |
asection *sec, |
void *xxx ATTRIBUTE_UNUSED) |
{ |
segment_info_type *seginfo = seg_info (sec); |
fixup_segment (seginfo->fix_root, sec); |
} |
static void |
install_reloc (asection *sec, arelent *reloc, fragS *fragp, |
char *file, unsigned int line) |
{ |
char *err; |
bfd_reloc_status_type s; |
asymbol *sym; |
if (reloc->sym_ptr_ptr != NULL |
&& (sym = *reloc->sym_ptr_ptr) != NULL |
&& (sym->flags & BSF_KEEP) == 0 |
&& ((sym->flags & BSF_SECTION_SYM) == 0 |
|| (EMIT_SECTION_SYMBOLS |
&& !bfd_is_abs_section (sym->section)))) |
as_bad_where (file, line, _("redefined symbol cannot be used on reloc")); |
s = bfd_install_relocation (stdoutput, reloc, |
fragp->fr_literal, fragp->fr_address, |
sec, &err); |
switch (s) |
{ |
case bfd_reloc_ok: |
break; |
case bfd_reloc_overflow: |
as_bad_where (file, line, _("relocation overflow")); |
break; |
case bfd_reloc_outofrange: |
as_bad_where (file, line, _("relocation out of range")); |
break; |
default: |
as_fatal (_("%s:%u: bad return from bfd_install_relocation: %x"), |
file, line, s); |
} |
} |
static fragS * |
get_frag_for_reloc (fragS *last_frag, |
const segment_info_type *seginfo, |
const struct reloc_list *r) |
{ |
fragS *f; |
for (f = last_frag; f != NULL; f = f->fr_next) |
if (f->fr_address <= r->u.b.r.address |
&& r->u.b.r.address < f->fr_address + f->fr_fix) |
return f; |
for (f = seginfo->frchainP->frch_root; f != NULL; f = f->fr_next) |
if (f->fr_address <= r->u.b.r.address |
&& r->u.b.r.address < f->fr_address + f->fr_fix) |
return f; |
as_bad_where (r->file, r->line, |
_("reloc not within (fixed part of) section")); |
return NULL; |
} |
static void |
write_relocs (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED) |
{ |
segment_info_type *seginfo = seg_info (sec); |
unsigned int n; |
struct reloc_list *my_reloc_list, **rp, *r; |
arelent **relocs; |
fixS *fixp; |
fragS *last_frag; |
/* If seginfo is NULL, we did not create this section; don't do |
anything with it. */ |
if (seginfo == NULL) |
return; |
n = 0; |
for (fixp = seginfo->fix_root; fixp; fixp = fixp->fx_next) |
if (!fixp->fx_done) |
n++; |
#ifdef RELOC_EXPANSION_POSSIBLE |
n *= MAX_RELOC_EXPANSION; |
#endif |
/* Extract relocs for this section from reloc_list. */ |
rp = &reloc_list; |
my_reloc_list = NULL; |
while ((r = *rp) != NULL) |
{ |
if (r->u.b.sec == sec) |
{ |
*rp = r->next; |
r->next = my_reloc_list; |
my_reloc_list = r; |
n++; |
} |
else |
rp = &r->next; |
} |
relocs = (arelent **) xcalloc (n, sizeof (arelent *)); |
n = 0; |
r = my_reloc_list; |
last_frag = NULL; |
for (fixp = seginfo->fix_root; fixp != (fixS *) NULL; fixp = fixp->fx_next) |
{ |
int fx_size, slack; |
offsetT loc; |
arelent **reloc; |
#ifndef RELOC_EXPANSION_POSSIBLE |
arelent *rel; |
reloc = &rel; |
#endif |
if (fixp->fx_done) |
continue; |
fx_size = fixp->fx_size; |
slack = TC_FX_SIZE_SLACK (fixp); |
if (slack > 0) |
fx_size = fx_size > slack ? fx_size - slack : 0; |
loc = fixp->fx_where + fx_size; |
if (slack >= 0 && loc > fixp->fx_frag->fr_fix) |
as_bad_where (fixp->fx_file, fixp->fx_line, |
_("internal error: fixup not contained within frag")); |
#ifndef RELOC_EXPANSION_POSSIBLE |
*reloc = tc_gen_reloc (sec, fixp); |
#else |
reloc = tc_gen_reloc (sec, fixp); |
#endif |
while (*reloc) |
{ |
while (r != NULL && r->u.b.r.address < (*reloc)->address) |
{ |
fragS *f = get_frag_for_reloc (last_frag, seginfo, r); |
if (f != NULL) |
{ |
last_frag = f; |
relocs[n++] = &r->u.b.r; |
install_reloc (sec, &r->u.b.r, f, r->file, r->line); |
} |
r = r->next; |
} |
relocs[n++] = *reloc; |
install_reloc (sec, *reloc, fixp->fx_frag, |
fixp->fx_file, fixp->fx_line); |
#ifndef RELOC_EXPANSION_POSSIBLE |
break; |
#else |
reloc++; |
#endif |
} |
} |
while (r != NULL) |
{ |
fragS *f = get_frag_for_reloc (last_frag, seginfo, r); |
if (f != NULL) |
{ |
last_frag = f; |
relocs[n++] = &r->u.b.r; |
install_reloc (sec, &r->u.b.r, f, r->file, r->line); |
} |
r = r->next; |
} |
#ifdef DEBUG4 |
{ |
unsigned int k, j, nsyms; |
asymbol **sympp; |
sympp = bfd_get_outsymbols (stdoutput); |
nsyms = bfd_get_symcount (stdoutput); |
for (k = 0; k < n; k++) |
if (((*relocs[k]->sym_ptr_ptr)->flags & BSF_SECTION_SYM) == 0) |
{ |
for (j = 0; j < nsyms; j++) |
if (sympp[j] == *relocs[k]->sym_ptr_ptr) |
break; |
if (j == nsyms) |
abort (); |
} |
} |
#endif |
if (n) |
{ |
flagword flags = bfd_get_section_flags (abfd, sec); |
flags |= SEC_RELOC; |
bfd_set_section_flags (abfd, sec, flags); |
bfd_set_reloc (stdoutput, sec, relocs, n); |
} |
#ifdef SET_SECTION_RELOCS |
SET_SECTION_RELOCS (sec, relocs, n); |
#endif |
#ifdef DEBUG3 |
{ |
unsigned int k; |
fprintf (stderr, "relocs for sec %s\n", sec->name); |
for (k = 0; k < n; k++) |
{ |
arelent *rel = relocs[k]; |
asymbol *s = *rel->sym_ptr_ptr; |
fprintf (stderr, " reloc %2d @%p off %4lx : sym %-10s addend %lx\n", |
k, rel, (unsigned long)rel->address, s->name, |
(unsigned long)rel->addend); |
} |
} |
#endif |
} |
static int |
compress_frag (struct z_stream_s *strm, const char *contents, int in_size, |
fragS **last_newf, struct obstack *ob) |
{ |
int out_size; |
int total_out_size = 0; |
fragS *f = *last_newf; |
char *next_out; |
int avail_out; |
/* Call the compression routine repeatedly until it has finished |
processing the frag. */ |
while (in_size > 0) |
{ |
/* Reserve all the space available in the current chunk. |
If none is available, start a new frag. */ |
avail_out = obstack_room (ob); |
if (avail_out <= 0) |
{ |
obstack_finish (ob); |
f = frag_alloc (ob); |
f->fr_type = rs_fill; |
(*last_newf)->fr_next = f; |
*last_newf = f; |
avail_out = obstack_room (ob); |
} |
if (avail_out <= 0) |
as_fatal (_("can't extend frag")); |
next_out = obstack_next_free (ob); |
obstack_blank_fast (ob, avail_out); |
out_size = compress_data (strm, &contents, &in_size, |
&next_out, &avail_out); |
if (out_size < 0) |
return -1; |
f->fr_fix += out_size; |
total_out_size += out_size; |
/* Return unused space. */ |
if (avail_out > 0) |
obstack_blank_fast (ob, -avail_out); |
} |
return total_out_size; |
} |
static void |
compress_debug (bfd *abfd, asection *sec, void *xxx ATTRIBUTE_UNUSED) |
{ |
segment_info_type *seginfo = seg_info (sec); |
fragS *f; |
fragS *first_newf; |
fragS *last_newf; |
struct obstack *ob = &seginfo->frchainP->frch_obstack; |
bfd_size_type uncompressed_size = (bfd_size_type) sec->size; |
bfd_size_type compressed_size; |
const char *section_name; |
char *compressed_name; |
char *header; |
struct z_stream_s *strm; |
int x; |
flagword flags = bfd_get_section_flags (abfd, sec); |
if (seginfo == NULL |
|| sec->size < 32 |
|| (flags & (SEC_ALLOC | SEC_HAS_CONTENTS)) == SEC_ALLOC) |
return; |
section_name = bfd_get_section_name (stdoutput, sec); |
if (strncmp (section_name, ".debug_", 7) != 0) |
return; |
strm = compress_init (); |
if (strm == NULL) |
return; |
/* Create a new frag to contain the "ZLIB" header. */ |
first_newf = frag_alloc (ob); |
if (obstack_room (ob) < 12) |
first_newf = frag_alloc (ob); |
if (obstack_room (ob) < 12) |
as_fatal (_("can't extend frag %u chars"), 12); |
last_newf = first_newf; |
obstack_blank_fast (ob, 12); |
last_newf->fr_type = rs_fill; |
last_newf->fr_fix = 12; |
header = last_newf->fr_literal; |
memcpy (header, "ZLIB", 4); |
header[11] = uncompressed_size; uncompressed_size >>= 8; |
header[10] = uncompressed_size; uncompressed_size >>= 8; |
header[9] = uncompressed_size; uncompressed_size >>= 8; |
header[8] = uncompressed_size; uncompressed_size >>= 8; |
header[7] = uncompressed_size; uncompressed_size >>= 8; |
header[6] = uncompressed_size; uncompressed_size >>= 8; |
header[5] = uncompressed_size; uncompressed_size >>= 8; |
header[4] = uncompressed_size; |
compressed_size = 12; |
/* Stream the frags through the compression engine, adding new frags |
as necessary to accomodate the compressed output. */ |
for (f = seginfo->frchainP->frch_root; |
f; |
f = f->fr_next) |
{ |
offsetT fill_size; |
char *fill_literal; |
offsetT count; |
int out_size; |
gas_assert (f->fr_type == rs_fill); |
if (f->fr_fix) |
{ |
out_size = compress_frag (strm, f->fr_literal, f->fr_fix, |
&last_newf, ob); |
if (out_size < 0) |
return; |
compressed_size += out_size; |
} |
fill_literal = f->fr_literal + f->fr_fix; |
fill_size = f->fr_var; |
count = f->fr_offset; |
gas_assert (count >= 0); |
if (fill_size && count) |
{ |
while (count--) |
{ |
out_size = compress_frag (strm, fill_literal, (int) fill_size, |
&last_newf, ob); |
if (out_size < 0) |
return; |
compressed_size += out_size; |
} |
} |
} |
/* Flush the compression state. */ |
for (;;) |
{ |
int avail_out; |
char *next_out; |
int out_size; |
/* Reserve all the space available in the current chunk. |
If none is available, start a new frag. */ |
avail_out = obstack_room (ob); |
if (avail_out <= 0) |
{ |
fragS *newf; |
obstack_finish (ob); |
newf = frag_alloc (ob); |
newf->fr_type = rs_fill; |
last_newf->fr_next = newf; |
last_newf = newf; |
avail_out = obstack_room (ob); |
} |
if (avail_out <= 0) |
as_fatal (_("can't extend frag")); |
next_out = obstack_next_free (ob); |
obstack_blank_fast (ob, avail_out); |
x = compress_finish (strm, &next_out, &avail_out, &out_size); |
if (x < 0) |
return; |
last_newf->fr_fix += out_size; |
compressed_size += out_size; |
/* Return unused space. */ |
if (avail_out > 0) |
obstack_blank_fast (ob, -avail_out); |
if (x == 0) |
break; |
} |
/* Replace the uncompressed frag list with the compressed frag list. */ |
seginfo->frchainP->frch_root = first_newf; |
seginfo->frchainP->frch_last = last_newf; |
/* Update the section size and its name. */ |
x = bfd_set_section_size (abfd, sec, compressed_size); |
gas_assert (x); |
compressed_name = (char *) xmalloc (strlen (section_name) + 2); |
compressed_name[0] = '.'; |
compressed_name[1] = 'z'; |
strcpy (compressed_name + 2, section_name + 1); |
bfd_section_name (stdoutput, sec) = compressed_name; |
} |
static void |
write_contents (bfd *abfd ATTRIBUTE_UNUSED, |
asection *sec, |
void *xxx ATTRIBUTE_UNUSED) |
{ |
segment_info_type *seginfo = seg_info (sec); |
addressT offset = 0; |
fragS *f; |
/* Write out the frags. */ |
if (seginfo == NULL |
|| !(bfd_get_section_flags (abfd, sec) & SEC_HAS_CONTENTS)) |
return; |
for (f = seginfo->frchainP->frch_root; |
f; |
f = f->fr_next) |
{ |
int x; |
addressT fill_size; |
char *fill_literal; |
offsetT count; |
gas_assert (f->fr_type == rs_fill); |
if (f->fr_fix) |
{ |
x = bfd_set_section_contents (stdoutput, sec, |
f->fr_literal, (file_ptr) offset, |
(bfd_size_type) f->fr_fix); |
if (!x) |
as_fatal (_("can't write %s: %s"), stdoutput->filename, |
bfd_errmsg (bfd_get_error ())); |
offset += f->fr_fix; |
} |
fill_literal = f->fr_literal + f->fr_fix; |
fill_size = f->fr_var; |
count = f->fr_offset; |
gas_assert (count >= 0); |
if (fill_size && count) |
{ |
char buf[256]; |
if (fill_size > sizeof (buf)) |
{ |
/* Do it the old way. Can this ever happen? */ |
while (count--) |
{ |
x = bfd_set_section_contents (stdoutput, sec, |
fill_literal, |
(file_ptr) offset, |
(bfd_size_type) fill_size); |
if (!x) |
as_fatal (_("can't write %s: %s"), stdoutput->filename, |
bfd_errmsg (bfd_get_error ())); |
offset += fill_size; |
} |
} |
else |
{ |
/* Build a buffer full of fill objects and output it as |
often as necessary. This saves on the overhead of |
potentially lots of bfd_set_section_contents calls. */ |
int n_per_buf, i; |
if (fill_size == 1) |
{ |
n_per_buf = sizeof (buf); |
memset (buf, *fill_literal, n_per_buf); |
} |
else |
{ |
char *bufp; |
n_per_buf = sizeof (buf) / fill_size; |
for (i = n_per_buf, bufp = buf; i; i--, bufp += fill_size) |
memcpy (bufp, fill_literal, fill_size); |
} |
for (; count > 0; count -= n_per_buf) |
{ |
n_per_buf = n_per_buf > count ? count : n_per_buf; |
x = bfd_set_section_contents |
(stdoutput, sec, buf, (file_ptr) offset, |
(bfd_size_type) n_per_buf * fill_size); |
if (!x) |
as_fatal (_("cannot write to output file '%s': %s"), |
stdoutput->filename, |
bfd_errmsg (bfd_get_error ())); |
offset += n_per_buf * fill_size; |
} |
} |
} |
} |
} |
static void |
merge_data_into_text (void) |
{ |
seg_info (text_section)->frchainP->frch_last->fr_next = |
seg_info (data_section)->frchainP->frch_root; |
seg_info (text_section)->frchainP->frch_last = |
seg_info (data_section)->frchainP->frch_last; |
seg_info (data_section)->frchainP = 0; |
} |
static void |
set_symtab (void) |
{ |
int nsyms; |
asymbol **asympp; |
symbolS *symp; |
bfd_boolean result; |
/* Count symbols. We can't rely on a count made by the loop in |
write_object_file, because *_frob_file may add a new symbol or |
two. */ |
nsyms = 0; |
for (symp = symbol_rootP; symp; symp = symbol_next (symp)) |
nsyms++; |
if (nsyms) |
{ |
int i; |
bfd_size_type amt = (bfd_size_type) nsyms * sizeof (asymbol *); |
asympp = (asymbol **) bfd_alloc (stdoutput, amt); |
symp = symbol_rootP; |
for (i = 0; i < nsyms; i++, symp = symbol_next (symp)) |
{ |
asympp[i] = symbol_get_bfdsym (symp); |
if (asympp[i]->flags != BSF_SECTION_SYM |
|| !(bfd_is_const_section (asympp[i]->section) |
&& asympp[i]->section->symbol == asympp[i])) |
asympp[i]->flags |= BSF_KEEP; |
symbol_mark_written (symp); |
} |
} |
else |
asympp = 0; |
result = bfd_set_symtab (stdoutput, asympp, nsyms); |
gas_assert (result); |
symbol_table_frozen = 1; |
} |
/* Finish the subsegments. After every sub-segment, we fake an |
".align ...". This conforms to BSD4.2 brane-damage. We then fake |
".fill 0" because that is the kind of frag that requires least |
thought. ".align" frags like to have a following frag since that |
makes calculating their intended length trivial. */ |
#ifndef SUB_SEGMENT_ALIGN |
#ifdef HANDLE_ALIGN |
/* The last subsegment gets an alignment corresponding to the alignment |
of the section. This allows proper nop-filling at the end of |
code-bearing sections. */ |
#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) \ |
(!(FRCHAIN)->frch_next ? get_recorded_alignment (SEG) : 0) |
#else |
#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 0 |
#endif |
#endif |
void |
subsegs_finish (void) |
{ |
struct frchain *frchainP; |
asection *s; |
for (s = stdoutput->sections; s; s = s->next) |
{ |
segment_info_type *seginfo = seg_info (s); |
if (!seginfo) |
continue; |
for (frchainP = seginfo->frchainP; |
frchainP != NULL; |
frchainP = frchainP->frch_next) |
{ |
int alignment = 0; |
subseg_set (s, frchainP->frch_subseg); |
/* This now gets called even if we had errors. In that case, |
any alignment is meaningless, and, moreover, will look weird |
if we are generating a listing. */ |
if (!had_errors ()) |
{ |
alignment = SUB_SEGMENT_ALIGN (now_seg, frchainP); |
if ((bfd_get_section_flags (now_seg->owner, now_seg) & SEC_MERGE) |
&& now_seg->entsize) |
{ |
unsigned int entsize = now_seg->entsize; |
int entalign = 0; |
while ((entsize & 1) == 0) |
{ |
++entalign; |
entsize >>= 1; |
} |
if (entalign > alignment) |
alignment = entalign; |
} |
} |
if (subseg_text_p (now_seg)) |
frag_align_code (alignment, 0); |
else |
frag_align (alignment, 0, 0); |
/* frag_align will have left a new frag. |
Use this last frag for an empty ".fill". |
For this segment ... |
Create a last frag. Do not leave a "being filled in frag". */ |
frag_wane (frag_now); |
frag_now->fr_fix = 0; |
know (frag_now->fr_next == NULL); |
} |
} |
} |
/* Write the object file. */ |
void |
write_object_file (void) |
{ |
struct relax_seg_info rsi; |
#ifndef WORKING_DOT_WORD |
fragS *fragP; /* Track along all frags. */ |
#endif |
#ifdef md_pre_output_hook |
md_pre_output_hook; |
#endif |
/* Do we really want to write it? */ |
{ |
int n_warns, n_errs; |
n_warns = had_warnings (); |
n_errs = had_errors (); |
/* The -Z flag indicates that an object file should be generated, |
regardless of warnings and errors. */ |
if (flag_always_generate_output) |
{ |
if (n_warns || n_errs) |
as_warn (_("%d error%s, %d warning%s, generating bad object file"), |
n_errs, n_errs == 1 ? "" : "s", |
n_warns, n_warns == 1 ? "" : "s"); |
} |
else |
{ |
if (n_errs) |
as_fatal (_("%d error%s, %d warning%s, no object file generated"), |
n_errs, n_errs == 1 ? "" : "s", |
n_warns, n_warns == 1 ? "" : "s"); |
} |
} |
#ifdef md_pre_relax_hook |
md_pre_relax_hook; |
#endif |
/* From now on, we don't care about sub-segments. Build one frag chain |
for each segment. Linked thru fr_next. */ |
/* Remove the sections created by gas for its own purposes. */ |
{ |
int i; |
bfd_section_list_remove (stdoutput, reg_section); |
bfd_section_list_remove (stdoutput, expr_section); |
stdoutput->section_count -= 2; |
i = 0; |
bfd_map_over_sections (stdoutput, renumber_sections, &i); |
} |
bfd_map_over_sections (stdoutput, chain_frchains_together, (char *) 0); |
/* We have two segments. If user gave -R flag, then we must put the |
data frags into the text segment. Do this before relaxing so |
we know to take advantage of -R and make shorter addresses. */ |
if (flag_readonly_data_in_text) |
{ |
merge_data_into_text (); |
} |
rsi.pass = 0; |
while (1) |
{ |
#ifndef WORKING_DOT_WORD |
/* We need to reset the markers in the broken word list and |
associated frags between calls to relax_segment (via |
relax_seg). Since the broken word list is global, we do it |
once per round, rather than locally in relax_segment for each |
segment. */ |
struct broken_word *brokp; |
for (brokp = broken_words; |
brokp != (struct broken_word *) NULL; |
brokp = brokp->next_broken_word) |
{ |
brokp->added = 0; |
if (brokp->dispfrag != (fragS *) NULL |
&& brokp->dispfrag->fr_type == rs_broken_word) |
brokp->dispfrag->fr_subtype = 0; |
} |
#endif |
rsi.changed = 0; |
bfd_map_over_sections (stdoutput, relax_seg, &rsi); |
rsi.pass++; |
if (!rsi.changed) |
break; |
} |
/* Note - Most ports will use the default value of |
TC_FINALIZE_SYMS_BEFORE_SIZE_SEG, which 1. This will force |
local symbols to be resolved, removing their frag information. |
Some ports however, will not have finished relaxing all of |
their frags and will still need the local symbol frag |
information. These ports can set |
TC_FINALIZE_SYMS_BEFORE_SIZE_SEG to 0. */ |
finalize_syms = TC_FINALIZE_SYMS_BEFORE_SIZE_SEG; |
bfd_map_over_sections (stdoutput, size_seg, (char *) 0); |
/* Relaxation has completed. Freeze all syms. */ |
finalize_syms = 1; |
#ifdef md_post_relax_hook |
md_post_relax_hook; |
#endif |
#ifndef WORKING_DOT_WORD |
{ |
struct broken_word *lie; |
struct broken_word **prevP; |
prevP = &broken_words; |
for (lie = broken_words; lie; lie = lie->next_broken_word) |
if (!lie->added) |
{ |
expressionS exp; |
subseg_change (lie->seg, lie->subseg); |
exp.X_op = O_subtract; |
exp.X_add_symbol = lie->add; |
exp.X_op_symbol = lie->sub; |
exp.X_add_number = lie->addnum; |
#ifdef TC_CONS_FIX_NEW |
TC_CONS_FIX_NEW (lie->frag, |
lie->word_goes_here - lie->frag->fr_literal, |
2, &exp); |
#else |
fix_new_exp (lie->frag, |
lie->word_goes_here - lie->frag->fr_literal, |
2, &exp, 0, BFD_RELOC_16); |
#endif |
*prevP = lie->next_broken_word; |
} |
else |
prevP = &(lie->next_broken_word); |
for (lie = broken_words; lie;) |
{ |
struct broken_word *untruth; |
char *table_ptr; |
addressT table_addr; |
addressT from_addr, to_addr; |
int n, m; |
subseg_change (lie->seg, lie->subseg); |
fragP = lie->dispfrag; |
/* Find out how many broken_words go here. */ |
n = 0; |
for (untruth = lie; |
untruth && untruth->dispfrag == fragP; |
untruth = untruth->next_broken_word) |
if (untruth->added == 1) |
n++; |
table_ptr = lie->dispfrag->fr_opcode; |
table_addr = (lie->dispfrag->fr_address |
+ (table_ptr - lie->dispfrag->fr_literal)); |
/* Create the jump around the long jumps. This is a short |
jump from table_ptr+0 to table_ptr+n*long_jump_size. */ |
from_addr = table_addr; |
to_addr = table_addr + md_short_jump_size + n * md_long_jump_size; |
md_create_short_jump (table_ptr, from_addr, to_addr, lie->dispfrag, |
lie->add); |
table_ptr += md_short_jump_size; |
table_addr += md_short_jump_size; |
for (m = 0; |
lie && lie->dispfrag == fragP; |
m++, lie = lie->next_broken_word) |
{ |
if (lie->added == 2) |
continue; |
/* Patch the jump table. */ |
for (untruth = (struct broken_word *) (fragP->fr_symbol); |
untruth && untruth->dispfrag == fragP; |
untruth = untruth->next_broken_word) |
{ |
if (untruth->use_jump == lie) |
{ |
/* This is the offset from ??? to table_ptr+0. |
The target is the same for all users of this |
md_long_jump, but the "sub" bases (and hence the |
offsets) may be different. */ |
addressT to_word = table_addr - S_GET_VALUE (untruth->sub); |
#ifdef TC_CHECK_ADJUSTED_BROKEN_DOT_WORD |
TC_CHECK_ADJUSTED_BROKEN_DOT_WORD (to_word, untruth); |
#endif |
md_number_to_chars (untruth->word_goes_here, to_word, 2); |
} |
} |
/* Install the long jump. */ |
/* This is a long jump from table_ptr+0 to the final target. */ |
from_addr = table_addr; |
to_addr = S_GET_VALUE (lie->add) + lie->addnum; |
md_create_long_jump (table_ptr, from_addr, to_addr, lie->dispfrag, |
lie->add); |
table_ptr += md_long_jump_size; |
table_addr += md_long_jump_size; |
} |
} |
} |
#endif /* not WORKING_DOT_WORD */ |
/* Resolve symbol values. This needs to be done before processing |
the relocations. */ |
if (symbol_rootP) |
{ |
symbolS *symp; |
for (symp = symbol_rootP; symp; symp = symbol_next (symp)) |
resolve_symbol_value (symp); |
} |
resolve_local_symbol_values (); |
resolve_reloc_expr_symbols (); |
PROGRESS (1); |
#ifdef tc_frob_file_before_adjust |
tc_frob_file_before_adjust (); |
#endif |
#ifdef obj_frob_file_before_adjust |
obj_frob_file_before_adjust (); |
#endif |
bfd_map_over_sections (stdoutput, adjust_reloc_syms, (char *) 0); |
#ifdef tc_frob_file_before_fix |
tc_frob_file_before_fix (); |
#endif |
#ifdef obj_frob_file_before_fix |
obj_frob_file_before_fix (); |
#endif |
bfd_map_over_sections (stdoutput, fix_segment, (char *) 0); |
/* Set up symbol table, and write it out. */ |
if (symbol_rootP) |
{ |
symbolS *symp; |
bfd_boolean skip_next_symbol = FALSE; |
for (symp = symbol_rootP; symp; symp = symbol_next (symp)) |
{ |
int punt = 0; |
const char *name; |
if (skip_next_symbol) |
{ |
/* Don't do anything besides moving the value of the |
symbol from the GAS value-field to the BFD value-field. */ |
symbol_get_bfdsym (symp)->value = S_GET_VALUE (symp); |
skip_next_symbol = FALSE; |
continue; |
} |
if (symbol_mri_common_p (symp)) |
{ |
if (S_IS_EXTERNAL (symp)) |
as_bad (_("%s: global symbols not supported in common sections"), |
S_GET_NAME (symp)); |
symbol_remove (symp, &symbol_rootP, &symbol_lastP); |
continue; |
} |
name = S_GET_NAME (symp); |
if (name) |
{ |
const char *name2 = |
decode_local_label_name ((char *) S_GET_NAME (symp)); |
/* They only differ if `name' is a fb or dollar local |
label name. */ |
if (name2 != name && ! S_IS_DEFINED (symp)) |
as_bad (_("local label `%s' is not defined"), name2); |
} |
/* Do it again, because adjust_reloc_syms might introduce |
more symbols. They'll probably only be section symbols, |
but they'll still need to have the values computed. */ |
resolve_symbol_value (symp); |
/* Skip symbols which were equated to undefined or common |
symbols. */ |
if (symbol_equated_reloc_p (symp) |
|| S_IS_WEAKREFR (symp)) |
{ |
const char *sname = S_GET_NAME (symp); |
if (S_IS_COMMON (symp) |
&& !TC_FAKE_LABEL (sname) |
&& !S_IS_WEAKREFR (symp) |
&& (!S_IS_EXTERNAL (symp) || S_IS_LOCAL (symp))) |
{ |
expressionS *e = symbol_get_value_expression (symp); |
as_bad (_("Local symbol `%s' can't be equated to common symbol `%s'"), |
sname, S_GET_NAME (e->X_add_symbol)); |
} |
if (S_GET_SEGMENT (symp) == reg_section) |
{ |
/* Report error only if we know the symbol name. */ |
if (S_GET_NAME (symp) != reg_section->name) |
as_bad (_("can't make global register symbol `%s'"), |
sname); |
} |
symbol_remove (symp, &symbol_rootP, &symbol_lastP); |
continue; |
} |
#ifdef obj_frob_symbol |
obj_frob_symbol (symp, punt); |
#endif |
#ifdef tc_frob_symbol |
if (! punt || symbol_used_in_reloc_p (symp)) |
tc_frob_symbol (symp, punt); |
#endif |
/* If we don't want to keep this symbol, splice it out of |
the chain now. If EMIT_SECTION_SYMBOLS is 0, we never |
want section symbols. Otherwise, we skip local symbols |
and symbols that the frob_symbol macros told us to punt, |
but we keep such symbols if they are used in relocs. */ |
if (symp == abs_section_sym |
|| (! EMIT_SECTION_SYMBOLS |
&& symbol_section_p (symp)) |
/* Note that S_IS_EXTERNAL and S_IS_LOCAL are not always |
opposites. Sometimes the former checks flags and the |
latter examines the name... */ |
|| (!S_IS_EXTERNAL (symp) |
&& (punt || S_IS_LOCAL (symp) || |
(S_IS_WEAKREFD (symp) && ! symbol_used_p (symp))) |
&& ! symbol_used_in_reloc_p (symp))) |
{ |
symbol_remove (symp, &symbol_rootP, &symbol_lastP); |
/* After symbol_remove, symbol_next(symp) still returns |
the one that came after it in the chain. So we don't |
need to do any extra cleanup work here. */ |
continue; |
} |
/* Make sure we really got a value for the symbol. */ |
if (! symbol_resolved_p (symp)) |
{ |
as_bad (_("can't resolve value for symbol `%s'"), |
S_GET_NAME (symp)); |
symbol_mark_resolved (symp); |
} |
/* Set the value into the BFD symbol. Up til now the value |
has only been kept in the gas symbolS struct. */ |
symbol_get_bfdsym (symp)->value = S_GET_VALUE (symp); |
/* A warning construct is a warning symbol followed by the |
symbol warned about. Don't let anything object-format or |
target-specific muck with it; it's ready for output. */ |
if (symbol_get_bfdsym (symp)->flags & BSF_WARNING) |
skip_next_symbol = TRUE; |
} |
} |
PROGRESS (1); |
/* Now do any format-specific adjustments to the symbol table, such |
as adding file symbols. */ |
#ifdef tc_adjust_symtab |
tc_adjust_symtab (); |
#endif |
#ifdef obj_adjust_symtab |
obj_adjust_symtab (); |
#endif |
/* Stop if there is an error. */ |
if (had_errors ()) |
return; |
/* Now that all the sizes are known, and contents correct, we can |
start writing to the file. */ |
set_symtab (); |
/* If *_frob_file changes the symbol value at this point, it is |
responsible for moving the changed value into symp->bsym->value |
as well. Hopefully all symbol value changing can be done in |
*_frob_symbol. */ |
#ifdef tc_frob_file |
tc_frob_file (); |
#endif |
#ifdef obj_frob_file |
obj_frob_file (); |
#endif |
#ifdef obj_coff_generate_pdata |
obj_coff_generate_pdata (); |
#endif |
bfd_map_over_sections (stdoutput, write_relocs, (char *) 0); |
#ifdef tc_frob_file_after_relocs |
tc_frob_file_after_relocs (); |
#endif |
#ifdef obj_frob_file_after_relocs |
obj_frob_file_after_relocs (); |
#endif |
/* Once all relocations have been written, we can compress the |
contents of the debug sections. This needs to be done before |
we start writing any sections, because it will affect the file |
layout, which is fixed once we start writing contents. */ |
if (flag_compress_debug) |
bfd_map_over_sections (stdoutput, compress_debug, (char *) 0); |
bfd_map_over_sections (stdoutput, write_contents, (char *) 0); |
} |
#ifdef TC_GENERIC_RELAX_TABLE |
/* Relax a fragment by scanning TC_GENERIC_RELAX_TABLE. */ |
long |
relax_frag (segT segment, fragS *fragP, long stretch) |
{ |
const relax_typeS *this_type; |
const relax_typeS *start_type; |
relax_substateT next_state; |
relax_substateT this_state; |
offsetT growth; |
offsetT aim; |
addressT target; |
addressT address; |
symbolS *symbolP; |
const relax_typeS *table; |
target = fragP->fr_offset; |
address = fragP->fr_address; |
table = TC_GENERIC_RELAX_TABLE; |
this_state = fragP->fr_subtype; |
start_type = this_type = table + this_state; |
symbolP = fragP->fr_symbol; |
if (symbolP) |
{ |
fragS *sym_frag; |
sym_frag = symbol_get_frag (symbolP); |
#ifndef DIFF_EXPR_OK |
know (sym_frag != NULL); |
#endif |
know (S_GET_SEGMENT (symbolP) != absolute_section |
|| sym_frag == &zero_address_frag); |
target += S_GET_VALUE (symbolP); |
/* If SYM_FRAG has yet to be reached on this pass, assume it |
will move by STRETCH just as we did, unless there is an |
alignment frag between here and SYM_FRAG. An alignment may |
well absorb any STRETCH, and we don't want to choose a larger |
branch insn by overestimating the needed reach of this |
branch. It isn't critical to calculate TARGET exactly; We |
know we'll be doing another pass if STRETCH is non-zero. */ |
if (stretch != 0 |
&& sym_frag->relax_marker != fragP->relax_marker |
&& S_GET_SEGMENT (symbolP) == segment) |
{ |
if (stretch < 0 |
|| sym_frag->region == fragP->region) |
target += stretch; |
/* If we get here we know we have a forward branch. This |
relax pass may have stretched previous instructions so |
far that omitting STRETCH would make the branch |
negative. Don't allow this in case the negative reach is |
large enough to require a larger branch instruction. */ |
else if (target < address) |
target = fragP->fr_next->fr_address + stretch; |
} |
} |
aim = target - address - fragP->fr_fix; |
#ifdef TC_PCREL_ADJUST |
/* Currently only the ns32k family needs this. */ |
aim += TC_PCREL_ADJUST (fragP); |
#endif |
#ifdef md_prepare_relax_scan |
/* Formerly called M68K_AIM_KLUDGE. */ |
md_prepare_relax_scan (fragP, address, aim, this_state, this_type); |
#endif |
if (aim < 0) |
{ |
/* Look backwards. */ |
for (next_state = this_type->rlx_more; next_state;) |
if (aim >= this_type->rlx_backward) |
next_state = 0; |
else |
{ |
/* Grow to next state. */ |
this_state = next_state; |
this_type = table + this_state; |
next_state = this_type->rlx_more; |
} |
} |
else |
{ |
/* Look forwards. */ |
for (next_state = this_type->rlx_more; next_state;) |
if (aim <= this_type->rlx_forward) |
next_state = 0; |
else |
{ |
/* Grow to next state. */ |
this_state = next_state; |
this_type = table + this_state; |
next_state = this_type->rlx_more; |
} |
} |
growth = this_type->rlx_length - start_type->rlx_length; |
if (growth != 0) |
fragP->fr_subtype = this_state; |
return growth; |
} |
#endif /* defined (TC_GENERIC_RELAX_TABLE) */ |
/* Relax_align. Advance location counter to next address that has 'alignment' |
lowest order bits all 0s, return size of adjustment made. */ |
static relax_addressT |
relax_align (register relax_addressT address, /* Address now. */ |
register int alignment /* Alignment (binary). */) |
{ |
relax_addressT mask; |
relax_addressT new_address; |
mask = ~((~0) << alignment); |
new_address = (address + mask) & (~mask); |
#ifdef LINKER_RELAXING_SHRINKS_ONLY |
if (linkrelax) |
/* We must provide lots of padding, so the linker can discard it |
when needed. The linker will not add extra space, ever. */ |
new_address += (1 << alignment); |
#endif |
return (new_address - address); |
} |
/* Now we have a segment, not a crowd of sub-segments, we can make |
fr_address values. |
Relax the frags. |
After this, all frags in this segment have addresses that are correct |
within the segment. Since segments live in different file addresses, |
these frag addresses may not be the same as final object-file |
addresses. */ |
int |
relax_segment (struct frag *segment_frag_root, segT segment, int pass) |
{ |
unsigned long frag_count; |
struct frag *fragP; |
relax_addressT address; |
int region; |
int ret; |
/* In case md_estimate_size_before_relax() wants to make fixSs. */ |
subseg_change (segment, 0); |
/* For each frag in segment: count and store (a 1st guess of) |
fr_address. */ |
address = 0; |
region = 0; |
for (frag_count = 0, fragP = segment_frag_root; |
fragP; |
fragP = fragP->fr_next, frag_count ++) |
{ |
fragP->region = region; |
fragP->relax_marker = 0; |
fragP->fr_address = address; |
address += fragP->fr_fix; |
switch (fragP->fr_type) |
{ |
case rs_fill: |
address += fragP->fr_offset * fragP->fr_var; |
break; |
case rs_align: |
case rs_align_code: |
case rs_align_test: |
{ |
addressT offset = relax_align (address, (int) fragP->fr_offset); |
if (fragP->fr_subtype != 0 && offset > fragP->fr_subtype) |
offset = 0; |
if (offset % fragP->fr_var != 0) |
{ |
as_bad_where (fragP->fr_file, fragP->fr_line, |
_("alignment padding (%lu bytes) not a multiple of %ld"), |
(unsigned long) offset, (long) fragP->fr_var); |
offset -= (offset % fragP->fr_var); |
} |
address += offset; |
region += 1; |
} |
break; |
case rs_org: |
/* Assume .org is nugatory. It will grow with 1st relax. */ |
region += 1; |
break; |
case rs_space: |
break; |
case rs_machine_dependent: |
/* If fr_symbol is an expression, this call to |
resolve_symbol_value sets up the correct segment, which will |
likely be needed in md_estimate_size_before_relax. */ |
if (fragP->fr_symbol) |
resolve_symbol_value (fragP->fr_symbol); |
address += md_estimate_size_before_relax (fragP, segment); |
break; |
#ifndef WORKING_DOT_WORD |
/* Broken words don't concern us yet. */ |
case rs_broken_word: |
break; |
#endif |
case rs_leb128: |
/* Initial guess is always 1; doing otherwise can result in |
stable solutions that are larger than the minimum. */ |
address += fragP->fr_offset = 1; |
break; |
case rs_cfa: |
address += eh_frame_estimate_size_before_relax (fragP); |
break; |
case rs_dwarf2dbg: |
address += dwarf2dbg_estimate_size_before_relax (fragP); |
break; |
default: |
BAD_CASE (fragP->fr_type); |
break; |
} |
} |
/* Do relax(). */ |
{ |
unsigned long max_iterations; |
/* Cumulative address adjustment. */ |
offsetT stretch; |
/* Have we made any adjustment this pass? We can't just test |
stretch because one piece of code may have grown and another |
shrank. */ |
int stretched; |
/* Most horrible, but gcc may give us some exception data that |
is impossible to assemble, of the form |
.align 4 |
.byte 0, 0 |
.uleb128 end - start |
start: |
.space 128*128 - 1 |
.align 4 |
end: |
If the leb128 is two bytes in size, then end-start is 128*128, |
which requires a three byte leb128. If the leb128 is three |
bytes in size, then end-start is 128*128-1, which requires a |
two byte leb128. We work around this dilemma by inserting |
an extra 4 bytes of alignment just after the .align. This |
works because the data after the align is accessed relative to |
the end label. |
This counter is used in a tiny state machine to detect |
whether a leb128 followed by an align is impossible to |
relax. */ |
int rs_leb128_fudge = 0; |
/* We want to prevent going into an infinite loop where one frag grows |
depending upon the location of a symbol which is in turn moved by |
the growing frag. eg: |
foo = . |
.org foo+16 |
foo = . |
So we dictate that this algorithm can be at most O2. */ |
max_iterations = frag_count * frag_count; |
/* Check for overflow. */ |
if (max_iterations < frag_count) |
max_iterations = frag_count; |
ret = 0; |
do |
{ |
stretch = 0; |
stretched = 0; |
for (fragP = segment_frag_root; fragP; fragP = fragP->fr_next) |
{ |
offsetT growth = 0; |
addressT was_address; |
offsetT offset; |
symbolS *symbolP; |
fragP->relax_marker ^= 1; |
was_address = fragP->fr_address; |
address = fragP->fr_address += stretch; |
symbolP = fragP->fr_symbol; |
offset = fragP->fr_offset; |
switch (fragP->fr_type) |
{ |
case rs_fill: /* .fill never relaxes. */ |
growth = 0; |
break; |
#ifndef WORKING_DOT_WORD |
/* JF: This is RMS's idea. I do *NOT* want to be blamed |
for it I do not want to write it. I do not want to have |
anything to do with it. This is not the proper way to |
implement this misfeature. */ |
case rs_broken_word: |
{ |
struct broken_word *lie; |
struct broken_word *untruth; |
/* Yes this is ugly (storing the broken_word pointer |
in the symbol slot). Still, this whole chunk of |
code is ugly, and I don't feel like doing anything |
about it. Think of it as stubbornness in action. */ |
growth = 0; |
for (lie = (struct broken_word *) (fragP->fr_symbol); |
lie && lie->dispfrag == fragP; |
lie = lie->next_broken_word) |
{ |
if (lie->added) |
continue; |
offset = (S_GET_VALUE (lie->add) |
+ lie->addnum |
- S_GET_VALUE (lie->sub)); |
if (offset <= -32768 || offset >= 32767) |
{ |
if (flag_warn_displacement) |
{ |
char buf[50]; |
sprint_value (buf, (addressT) lie->addnum); |
as_warn_where (fragP->fr_file, fragP->fr_line, |
_(".word %s-%s+%s didn't fit"), |
S_GET_NAME (lie->add), |
S_GET_NAME (lie->sub), |
buf); |
} |
if (fragP->fr_subtype == 0) |
{ |
fragP->fr_subtype++; |
growth += md_short_jump_size; |
} |
/* Redirect *all* words of this table with the same |
target, lest we have to handle the case where the |
same target but with a offset that fits on this |
round overflows at the next relaxation round. */ |
for (untruth = (struct broken_word *) (fragP->fr_symbol); |
untruth && untruth->dispfrag == lie->dispfrag; |
untruth = untruth->next_broken_word) |
if ((symbol_get_frag (untruth->add) |
== symbol_get_frag (lie->add)) |
&& (S_GET_VALUE (untruth->add) |
== S_GET_VALUE (lie->add))) |
{ |
untruth->added = 2; |
untruth->use_jump = lie; |
} |
lie->added = 1; |
growth += md_long_jump_size; |
} |
} |
break; |
} /* case rs_broken_word */ |
#endif |
case rs_align: |
case rs_align_code: |
case rs_align_test: |
{ |
addressT oldoff, newoff; |
oldoff = relax_align (was_address + fragP->fr_fix, |
(int) offset); |
newoff = relax_align (address + fragP->fr_fix, |
(int) offset); |
if (fragP->fr_subtype != 0) |
{ |
if (oldoff > fragP->fr_subtype) |
oldoff = 0; |
if (newoff > fragP->fr_subtype) |
newoff = 0; |
} |
growth = newoff - oldoff; |
/* If this align happens to follow a leb128 and |
we have determined that the leb128 is bouncing |
in size, then break the cycle by inserting an |
extra alignment. */ |
if (growth < 0 |
&& (rs_leb128_fudge & 16) != 0 |
&& (rs_leb128_fudge & 15) >= 2) |
{ |
segment_info_type *seginfo = seg_info (segment); |
struct obstack *ob = &seginfo->frchainP->frch_obstack; |
struct frag *newf; |
newf = frag_alloc (ob); |
obstack_blank_fast (ob, fragP->fr_var); |
obstack_finish (ob); |
memcpy (newf, fragP, SIZEOF_STRUCT_FRAG); |
memcpy (newf->fr_literal, |
fragP->fr_literal + fragP->fr_fix, |
fragP->fr_var); |
newf->fr_type = rs_fill; |
newf->fr_address = address + fragP->fr_fix + newoff; |
newf->fr_fix = 0; |
newf->fr_offset = (((offsetT) 1 << fragP->fr_offset) |
/ fragP->fr_var); |
if (newf->fr_offset * newf->fr_var |
!= (offsetT) 1 << fragP->fr_offset) |
{ |
newf->fr_offset = (offsetT) 1 << fragP->fr_offset; |
newf->fr_var = 1; |
} |
/* Include size of new frag in GROWTH. */ |
growth += newf->fr_offset * newf->fr_var; |
/* Adjust the new frag address for the amount |
we'll add when we process the new frag. */ |
newf->fr_address -= stretch + growth; |
newf->relax_marker ^= 1; |
fragP->fr_next = newf; |
#ifdef DEBUG |
as_warn (_("padding added")); |
#endif |
} |
} |
break; |
case rs_org: |
{ |
addressT target = offset; |
addressT after; |
if (symbolP) |
{ |
/* Convert from an actual address to an octet offset |
into the section. Here it is assumed that the |
section's VMA is zero, and can omit subtracting it |
from the symbol's value to get the address offset. */ |
know (S_GET_SEGMENT (symbolP)->vma == 0); |
target += S_GET_VALUE (symbolP) * OCTETS_PER_BYTE; |
} |
know (fragP->fr_next); |
after = fragP->fr_next->fr_address + stretch; |
growth = target - after; |
if (growth < 0) |
{ |
growth = 0; |
/* Don't error on first few frag relax passes. |
The symbol might be an expression involving |
symbol values from other sections. If those |
sections have not yet been processed their |
frags will all have zero addresses, so we |
will calculate incorrect values for them. The |
number of passes we allow before giving an |
error is somewhat arbitrary. It should be at |
least one, with larger values requiring |
increasingly contrived dependencies between |
frags to trigger a false error. */ |
if (pass < 2) |
{ |
/* Force another pass. */ |
ret = 1; |
break; |
} |
/* Growth may be negative, but variable part of frag |
cannot have fewer than 0 chars. That is, we can't |
.org backwards. */ |
as_bad_where (fragP->fr_file, fragP->fr_line, |
_("attempt to move .org backwards")); |
/* We've issued an error message. Change the |
frag to avoid cascading errors. */ |
fragP->fr_type = rs_align; |
fragP->fr_subtype = 0; |
fragP->fr_offset = 0; |
fragP->fr_fix = after - address; |
} |
} |
break; |
case rs_space: |
growth = 0; |
if (symbolP) |
{ |
offsetT amount; |
amount = S_GET_VALUE (symbolP); |
if (S_GET_SEGMENT (symbolP) != absolute_section |
|| S_IS_COMMON (symbolP) |
|| ! S_IS_DEFINED (symbolP)) |
{ |
as_bad_where (fragP->fr_file, fragP->fr_line, |
_(".space specifies non-absolute value")); |
/* Prevent repeat of this error message. */ |
fragP->fr_symbol = 0; |
} |
else if (amount < 0) |
{ |
/* Don't error on first few frag relax passes. |
See rs_org comment for a longer explanation. */ |
if (pass < 2) |
{ |
ret = 1; |
break; |
} |
as_warn_where (fragP->fr_file, fragP->fr_line, |
_(".space or .fill with negative value, ignored")); |
fragP->fr_symbol = 0; |
} |
else |
growth = (was_address + fragP->fr_fix + amount |
- fragP->fr_next->fr_address); |
} |
break; |
case rs_machine_dependent: |
#ifdef md_relax_frag |
growth = md_relax_frag (segment, fragP, stretch); |
#else |
#ifdef TC_GENERIC_RELAX_TABLE |
/* The default way to relax a frag is to look through |
TC_GENERIC_RELAX_TABLE. */ |
growth = relax_frag (segment, fragP, stretch); |
#endif /* TC_GENERIC_RELAX_TABLE */ |
#endif |
break; |
case rs_leb128: |
{ |
valueT value; |
offsetT size; |
value = resolve_symbol_value (fragP->fr_symbol); |
size = sizeof_leb128 (value, fragP->fr_subtype); |
growth = size - fragP->fr_offset; |
fragP->fr_offset = size; |
} |
break; |
case rs_cfa: |
growth = eh_frame_relax_frag (fragP); |
break; |
case rs_dwarf2dbg: |
growth = dwarf2dbg_relax_frag (fragP); |
break; |
default: |
BAD_CASE (fragP->fr_type); |
break; |
} |
if (growth) |
{ |
stretch += growth; |
stretched = 1; |
if (fragP->fr_type == rs_leb128) |
rs_leb128_fudge += 16; |
else if (fragP->fr_type == rs_align |
&& (rs_leb128_fudge & 16) != 0 |
&& stretch == 0) |
rs_leb128_fudge += 16; |
else |
rs_leb128_fudge = 0; |
} |
} |
if (stretch == 0 |
&& (rs_leb128_fudge & 16) == 0 |
&& (rs_leb128_fudge & -16) != 0) |
rs_leb128_fudge += 1; |
else |
rs_leb128_fudge = 0; |
} |
/* Until nothing further to relax. */ |
while (stretched && -- max_iterations); |
if (stretched) |
as_fatal (_("Infinite loop encountered whilst attempting to compute the addresses of symbols in section %s"), |
segment_name (segment)); |
} |
for (fragP = segment_frag_root; fragP; fragP = fragP->fr_next) |
if (fragP->last_fr_address != fragP->fr_address) |
{ |
fragP->last_fr_address = fragP->fr_address; |
ret = 1; |
} |
return ret; |
} |
void |
number_to_chars_bigendian (char *buf, valueT val, int n) |
{ |
if (n <= 0) |
abort (); |
while (n--) |
{ |
buf[n] = val & 0xff; |
val >>= 8; |
} |
} |
void |
number_to_chars_littleendian (char *buf, valueT val, int n) |
{ |
if (n <= 0) |
abort (); |
while (n--) |
{ |
*buf++ = val & 0xff; |
val >>= 8; |
} |
} |
void |
write_print_statistics (FILE *file) |
{ |
fprintf (file, "fixups: %d\n", n_fixups); |
} |
/* For debugging. */ |
extern int indent_level; |
void |
print_fixup (fixS *fixp) |
{ |
indent_level = 1; |
fprintf (stderr, "fix "); |
fprintf_vma (stderr, (bfd_vma)((bfd_hostptr_t) fixp)); |
fprintf (stderr, " %s:%d",fixp->fx_file, fixp->fx_line); |
if (fixp->fx_pcrel) |
fprintf (stderr, " pcrel"); |
if (fixp->fx_pcrel_adjust) |
fprintf (stderr, " pcrel_adjust=%d", fixp->fx_pcrel_adjust); |
if (fixp->fx_im_disp) |
{ |
#ifdef TC_NS32K |
fprintf (stderr, " im_disp=%d", fixp->fx_im_disp); |
#else |
fprintf (stderr, " im_disp"); |
#endif |
} |
if (fixp->fx_tcbit) |
fprintf (stderr, " tcbit"); |
if (fixp->fx_done) |
fprintf (stderr, " done"); |
fprintf (stderr, "\n size=%d frag=", fixp->fx_size); |
fprintf_vma (stderr, (bfd_vma) ((bfd_hostptr_t) fixp->fx_frag)); |
fprintf (stderr, " where=%ld offset=%lx addnumber=%lx", |
(long) fixp->fx_where, |
(unsigned long) fixp->fx_offset, |
(unsigned long) fixp->fx_addnumber); |
fprintf (stderr, "\n %s (%d)", bfd_get_reloc_code_name (fixp->fx_r_type), |
fixp->fx_r_type); |
if (fixp->fx_addsy) |
{ |
fprintf (stderr, "\n +<"); |
print_symbol_value_1 (stderr, fixp->fx_addsy); |
fprintf (stderr, ">"); |
} |
if (fixp->fx_subsy) |
{ |
fprintf (stderr, "\n -<"); |
print_symbol_value_1 (stderr, fixp->fx_subsy); |
fprintf (stderr, ">"); |
} |
fprintf (stderr, "\n"); |
#ifdef TC_FIX_DATA_PRINT |
TC_FIX_DATA_PRINT (stderr, fixp); |
#endif |
} |
/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__ */ |