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 |
}; |