2000-07-26 04:22:40 +04:00
|
|
|
/* This module handles expression trees.
|
2001-08-14 06:57:43 +04:00
|
|
|
Copyright 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
|
|
|
|
2001
|
2000-07-26 04:22:40 +04:00
|
|
|
Free Software Foundation, Inc.
|
2001-04-23 16:27:01 +04:00
|
|
|
Written by Steve Chamberlain of Cygnus Support <sac@cygnus.com>.
|
2000-07-26 04:22:40 +04:00
|
|
|
|
|
|
|
This file is part of GLD, the Gnu Linker.
|
|
|
|
|
|
|
|
GLD 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 2, or (at your option)
|
|
|
|
any later version.
|
|
|
|
|
|
|
|
GLD 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 GLD; see the file COPYING. If not, write to the Free
|
|
|
|
Software Foundation, 59 Temple Place - Suite 330, Boston, MA
|
|
|
|
02111-1307, USA. */
|
|
|
|
|
2001-04-23 16:27:01 +04:00
|
|
|
/* This module is in charge of working out the contents of expressions.
|
2000-07-26 04:22:40 +04:00
|
|
|
|
2001-04-23 16:27:01 +04:00
|
|
|
It has to keep track of the relative/absness of a symbol etc. This
|
|
|
|
is done by keeping all values in a struct (an etree_value_type)
|
|
|
|
which contains a value, a section to which it is relative and a
|
|
|
|
valid bit. */
|
2000-07-26 04:22:40 +04:00
|
|
|
|
|
|
|
#include "bfd.h"
|
|
|
|
#include "sysdep.h"
|
|
|
|
#include "bfdlink.h"
|
|
|
|
|
|
|
|
#include "ld.h"
|
|
|
|
#include "ldmain.h"
|
|
|
|
#include "ldmisc.h"
|
|
|
|
#include "ldexp.h"
|
|
|
|
#include "ldgram.h"
|
|
|
|
#include "ldlang.h"
|
|
|
|
|
|
|
|
static void exp_print_token PARAMS ((token_code_type code));
|
|
|
|
static void make_abs PARAMS ((etree_value_type *ptr));
|
|
|
|
static etree_value_type new_abs PARAMS ((bfd_vma value));
|
|
|
|
static void check PARAMS ((lang_output_section_statement_type *os,
|
|
|
|
const char *name, const char *op));
|
|
|
|
static etree_value_type new_rel
|
|
|
|
PARAMS ((bfd_vma value, lang_output_section_statement_type *section));
|
|
|
|
static etree_value_type new_rel_from_section
|
|
|
|
PARAMS ((bfd_vma value, lang_output_section_statement_type *section));
|
|
|
|
static etree_value_type fold_binary
|
|
|
|
PARAMS ((etree_type *tree,
|
|
|
|
lang_output_section_statement_type *current_section,
|
|
|
|
lang_phase_type allocation_done,
|
|
|
|
bfd_vma dot, bfd_vma *dotp));
|
|
|
|
static etree_value_type fold_name
|
|
|
|
PARAMS ((etree_type *tree,
|
|
|
|
lang_output_section_statement_type *current_section,
|
|
|
|
lang_phase_type allocation_done,
|
|
|
|
bfd_vma dot));
|
|
|
|
static etree_value_type exp_fold_tree_no_dot
|
|
|
|
PARAMS ((etree_type *tree,
|
|
|
|
lang_output_section_statement_type *current_section,
|
|
|
|
lang_phase_type allocation_done));
|
|
|
|
|
|
|
|
static void
|
|
|
|
exp_print_token (code)
|
|
|
|
token_code_type code;
|
|
|
|
{
|
2001-04-23 16:27:01 +04:00
|
|
|
static CONST struct {
|
|
|
|
token_code_type code;
|
|
|
|
char *name;
|
|
|
|
} table[] = {
|
|
|
|
{ INT, "int" },
|
|
|
|
{ REL, "relocateable" },
|
|
|
|
{ NAME, "NAME" },
|
|
|
|
{ PLUSEQ, "+=" },
|
|
|
|
{ MINUSEQ, "-=" },
|
|
|
|
{ MULTEQ, "*=" },
|
|
|
|
{ DIVEQ, "/=" },
|
|
|
|
{ LSHIFTEQ, "<<=" },
|
|
|
|
{ RSHIFTEQ, ">>=" },
|
|
|
|
{ ANDEQ, "&=" },
|
|
|
|
{ OREQ, "|=" },
|
|
|
|
{ OROR, "||" },
|
|
|
|
{ ANDAND, "&&" },
|
|
|
|
{ EQ, "==" },
|
|
|
|
{ NE, "!=" },
|
|
|
|
{ LE, "<=" },
|
|
|
|
{ GE, ">=" },
|
|
|
|
{ LSHIFT, "<<" },
|
|
|
|
{ RSHIFT, ">>=" },
|
|
|
|
{ ALIGN_K, "ALIGN" },
|
|
|
|
{ BLOCK, "BLOCK" },
|
|
|
|
{ SECTIONS, "SECTIONS" },
|
|
|
|
{ SIZEOF_HEADERS, "SIZEOF_HEADERS" },
|
|
|
|
{ NEXT, "NEXT" },
|
|
|
|
{ SIZEOF, "SIZEOF" },
|
|
|
|
{ ADDR, "ADDR" },
|
|
|
|
{ LOADADDR, "LOADADDR" },
|
|
|
|
{ MEMORY, "MEMORY" },
|
|
|
|
{ DEFINED, "DEFINED" },
|
|
|
|
{ TARGET_K, "TARGET" },
|
|
|
|
{ SEARCH_DIR, "SEARCH_DIR" },
|
|
|
|
{ MAP, "MAP" },
|
|
|
|
{ QUAD, "QUAD" },
|
|
|
|
{ SQUAD, "SQUAD" },
|
|
|
|
{ LONG, "LONG" },
|
|
|
|
{ SHORT, "SHORT" },
|
|
|
|
{ BYTE, "BYTE" },
|
|
|
|
{ ENTRY, "ENTRY" },
|
|
|
|
{ 0, (char *) NULL }
|
|
|
|
};
|
2000-07-26 04:22:40 +04:00
|
|
|
unsigned int idx;
|
|
|
|
|
2001-04-23 16:27:01 +04:00
|
|
|
for (idx = 0; table[idx].name != (char *) NULL; idx++)
|
|
|
|
{
|
|
|
|
if (table[idx].code == code)
|
|
|
|
{
|
|
|
|
fprintf (config.map_file, "%s", table[idx].name);
|
|
|
|
return;
|
|
|
|
}
|
2000-07-26 04:22:40 +04:00
|
|
|
}
|
|
|
|
/* Not in table, just print it alone */
|
2001-04-23 16:27:01 +04:00
|
|
|
fprintf (config.map_file, "%c", code);
|
2000-07-26 04:22:40 +04:00
|
|
|
}
|
|
|
|
|
2001-04-23 16:27:01 +04:00
|
|
|
static void
|
2000-07-26 04:22:40 +04:00
|
|
|
make_abs (ptr)
|
|
|
|
etree_value_type *ptr;
|
|
|
|
{
|
2001-04-23 16:27:01 +04:00
|
|
|
asection *s = ptr->section->bfd_section;
|
|
|
|
ptr->value += s->vma;
|
|
|
|
ptr->section = abs_output_section;
|
2000-07-26 04:22:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
static etree_value_type
|
|
|
|
new_abs (value)
|
|
|
|
bfd_vma value;
|
|
|
|
{
|
|
|
|
etree_value_type new;
|
|
|
|
new.valid_p = true;
|
|
|
|
new.section = abs_output_section;
|
|
|
|
new.value = value;
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
2001-04-23 16:27:01 +04:00
|
|
|
static void
|
2000-07-26 04:22:40 +04:00
|
|
|
check (os, name, op)
|
|
|
|
lang_output_section_statement_type *os;
|
|
|
|
const char *name;
|
|
|
|
const char *op;
|
|
|
|
{
|
|
|
|
if (os == NULL)
|
|
|
|
einfo (_("%F%P: %s uses undefined section %s\n"), op, name);
|
|
|
|
if (! os->processed)
|
|
|
|
einfo (_("%F%P: %s forward reference of section %s\n"), op, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
etree_type *
|
|
|
|
exp_intop (value)
|
|
|
|
bfd_vma value;
|
|
|
|
{
|
2001-04-23 16:27:01 +04:00
|
|
|
etree_type *new = (etree_type *) stat_alloc (sizeof (new->value));
|
2000-07-26 04:22:40 +04:00
|
|
|
new->type.node_code = INT;
|
|
|
|
new->value.value = value;
|
|
|
|
new->type.node_class = etree_value;
|
|
|
|
return new;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Build an expression representing an unnamed relocateable value. */
|
|
|
|
|
|
|
|
etree_type *
|
|
|
|
exp_relop (section, value)
|
|
|
|
asection *section;
|
|
|
|
bfd_vma value;
|
|
|
|
{
|
|
|
|
etree_type *new = (etree_type *) stat_alloc (sizeof (new->rel));
|
|
|
|
new->type.node_code = REL;
|
|
|
|
new->type.node_class = etree_rel;
|
|
|
|
new->rel.section = section;
|
|
|
|
new->rel.value = value;
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
static etree_value_type
|
|
|
|
new_rel (value, section)
|
|
|
|
bfd_vma value;
|
|
|
|
lang_output_section_statement_type *section;
|
|
|
|
{
|
|
|
|
etree_value_type new;
|
|
|
|
new.valid_p = true;
|
|
|
|
new.value = value;
|
|
|
|
new.section = section;
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
static etree_value_type
|
|
|
|
new_rel_from_section (value, section)
|
|
|
|
bfd_vma value;
|
|
|
|
lang_output_section_statement_type *section;
|
|
|
|
{
|
|
|
|
etree_value_type new;
|
|
|
|
new.valid_p = true;
|
|
|
|
new.value = value;
|
|
|
|
new.section = section;
|
|
|
|
|
2001-04-23 16:27:01 +04:00
|
|
|
new.value -= section->bfd_section->vma;
|
2000-07-26 04:22:40 +04:00
|
|
|
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
2001-04-23 16:27:01 +04:00
|
|
|
static etree_value_type
|
2000-07-26 04:22:40 +04:00
|
|
|
fold_binary (tree, current_section, allocation_done, dot, dotp)
|
|
|
|
etree_type *tree;
|
|
|
|
lang_output_section_statement_type *current_section;
|
|
|
|
lang_phase_type allocation_done;
|
|
|
|
bfd_vma dot;
|
|
|
|
bfd_vma *dotp;
|
|
|
|
{
|
|
|
|
etree_value_type result;
|
|
|
|
|
|
|
|
result = exp_fold_tree (tree->binary.lhs, current_section,
|
|
|
|
allocation_done, dot, dotp);
|
|
|
|
if (result.valid_p)
|
|
|
|
{
|
|
|
|
etree_value_type other;
|
|
|
|
|
|
|
|
other = exp_fold_tree (tree->binary.rhs,
|
|
|
|
current_section,
|
2001-04-23 16:27:01 +04:00
|
|
|
allocation_done, dot, dotp);
|
2000-07-26 04:22:40 +04:00
|
|
|
if (other.valid_p)
|
|
|
|
{
|
|
|
|
/* If the values are from different sections, or this is an
|
|
|
|
absolute expression, make both the source arguments
|
|
|
|
absolute. However, adding or subtracting an absolute
|
|
|
|
value from a relative value is meaningful, and is an
|
|
|
|
exception. */
|
|
|
|
if (current_section != abs_output_section
|
|
|
|
&& (other.section == abs_output_section
|
|
|
|
|| (result.section == abs_output_section
|
|
|
|
&& tree->type.node_code == '+'))
|
|
|
|
&& (tree->type.node_code == '+'
|
|
|
|
|| tree->type.node_code == '-'))
|
|
|
|
{
|
|
|
|
etree_value_type hold;
|
|
|
|
|
|
|
|
/* If there is only one absolute term, make sure it is the
|
|
|
|
second one. */
|
|
|
|
if (other.section != abs_output_section)
|
|
|
|
{
|
|
|
|
hold = result;
|
|
|
|
result = other;
|
|
|
|
other = hold;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (result.section != other.section
|
|
|
|
|| current_section == abs_output_section)
|
|
|
|
{
|
2001-04-23 16:27:01 +04:00
|
|
|
make_abs (&result);
|
|
|
|
make_abs (&other);
|
2000-07-26 04:22:40 +04:00
|
|
|
}
|
|
|
|
|
2001-04-23 16:27:01 +04:00
|
|
|
switch (tree->type.node_code)
|
2000-07-26 04:22:40 +04:00
|
|
|
{
|
|
|
|
case '%':
|
|
|
|
if (other.value == 0)
|
|
|
|
einfo (_("%F%S %% by zero\n"));
|
|
|
|
result.value = ((bfd_signed_vma) result.value
|
|
|
|
% (bfd_signed_vma) other.value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '/':
|
|
|
|
if (other.value == 0)
|
|
|
|
einfo (_("%F%S / by zero\n"));
|
|
|
|
result.value = ((bfd_signed_vma) result.value
|
|
|
|
/ (bfd_signed_vma) other.value);
|
|
|
|
break;
|
|
|
|
|
|
|
|
#define BOP(x,y) case x : result.value = result.value y other.value; break;
|
2001-04-23 16:27:01 +04:00
|
|
|
BOP ('+', +);
|
|
|
|
BOP ('*', *);
|
|
|
|
BOP ('-', -);
|
|
|
|
BOP (LSHIFT, <<);
|
|
|
|
BOP (RSHIFT, >>);
|
|
|
|
BOP (EQ, ==);
|
|
|
|
BOP (NE, !=);
|
|
|
|
BOP ('<', <);
|
|
|
|
BOP ('>', >);
|
|
|
|
BOP (LE, <=);
|
|
|
|
BOP (GE, >=);
|
|
|
|
BOP ('&', &);
|
|
|
|
BOP ('^', ^);
|
|
|
|
BOP ('|', |);
|
|
|
|
BOP (ANDAND, &&);
|
|
|
|
BOP (OROR, ||);
|
2000-07-26 04:22:40 +04:00
|
|
|
|
|
|
|
case MAX_K:
|
|
|
|
if (result.value < other.value)
|
|
|
|
result = other;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case MIN_K:
|
|
|
|
if (result.value > other.value)
|
|
|
|
result = other;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2001-04-23 16:27:01 +04:00
|
|
|
FAIL ();
|
2000-07-26 04:22:40 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result.valid_p = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2001-04-23 16:27:01 +04:00
|
|
|
etree_value_type
|
2000-07-26 04:22:40 +04:00
|
|
|
invalid ()
|
|
|
|
{
|
|
|
|
etree_value_type new;
|
|
|
|
new.valid_p = false;
|
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
2001-04-23 16:27:01 +04:00
|
|
|
static etree_value_type
|
2000-07-26 04:22:40 +04:00
|
|
|
fold_name (tree, current_section, allocation_done, dot)
|
|
|
|
etree_type *tree;
|
|
|
|
lang_output_section_statement_type *current_section;
|
2001-04-23 16:27:01 +04:00
|
|
|
lang_phase_type allocation_done;
|
2000-07-26 04:22:40 +04:00
|
|
|
bfd_vma dot;
|
|
|
|
{
|
|
|
|
etree_value_type result;
|
2001-04-23 16:27:01 +04:00
|
|
|
switch (tree->type.node_code)
|
|
|
|
{
|
|
|
|
case SIZEOF_HEADERS:
|
|
|
|
if (allocation_done != lang_first_phase_enum)
|
|
|
|
{
|
|
|
|
result = new_abs ((bfd_vma)
|
|
|
|
bfd_sizeof_headers (output_bfd,
|
|
|
|
link_info.relocateable));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-07-26 04:22:40 +04:00
|
|
|
result.valid_p = false;
|
2001-04-23 16:27:01 +04:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DEFINED:
|
|
|
|
if (allocation_done == lang_first_phase_enum)
|
2000-07-26 04:22:40 +04:00
|
|
|
result.valid_p = false;
|
2001-04-23 16:27:01 +04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
struct bfd_link_hash_entry *h;
|
|
|
|
|
|
|
|
h = bfd_wrapped_link_hash_lookup (output_bfd, &link_info,
|
|
|
|
tree->name.name,
|
|
|
|
false, false, true);
|
|
|
|
result.value = (h != (struct bfd_link_hash_entry *) NULL
|
|
|
|
&& (h->type == bfd_link_hash_defined
|
|
|
|
|| h->type == bfd_link_hash_defweak
|
|
|
|
|| h->type == bfd_link_hash_common));
|
|
|
|
result.section = 0;
|
|
|
|
result.valid_p = true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case NAME:
|
|
|
|
result.valid_p = false;
|
|
|
|
if (tree->name.name[0] == '.' && tree->name.name[1] == 0)
|
|
|
|
{
|
|
|
|
if (allocation_done != lang_first_phase_enum)
|
|
|
|
result = new_rel_from_section (dot, current_section);
|
|
|
|
else
|
|
|
|
result = invalid ();
|
|
|
|
}
|
|
|
|
else if (allocation_done != lang_first_phase_enum)
|
|
|
|
{
|
|
|
|
struct bfd_link_hash_entry *h;
|
|
|
|
|
|
|
|
h = bfd_wrapped_link_hash_lookup (output_bfd, &link_info,
|
|
|
|
tree->name.name,
|
|
|
|
false, false, true);
|
|
|
|
if (h != NULL
|
|
|
|
&& (h->type == bfd_link_hash_defined
|
|
|
|
|| h->type == bfd_link_hash_defweak))
|
|
|
|
{
|
|
|
|
if (bfd_is_abs_section (h->u.def.section))
|
|
|
|
result = new_abs (h->u.def.value);
|
|
|
|
else if (allocation_done == lang_final_phase_enum
|
|
|
|
|| allocation_done == lang_allocating_phase_enum)
|
|
|
|
{
|
|
|
|
asection *output_section;
|
|
|
|
|
|
|
|
output_section = h->u.def.section->output_section;
|
|
|
|
if (output_section == NULL)
|
|
|
|
einfo (_("%X%S: unresolvable symbol `%s' referenced in expression\n"),
|
|
|
|
tree->name.name);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
lang_output_section_statement_type *os;
|
|
|
|
|
|
|
|
os = (lang_output_section_statement_lookup
|
|
|
|
(bfd_get_section_name (output_bfd,
|
|
|
|
output_section)));
|
|
|
|
|
|
|
|
/* FIXME: Is this correct if this section is
|
|
|
|
being linked with -R? */
|
|
|
|
result = new_rel ((h->u.def.value
|
|
|
|
+ h->u.def.section->output_offset),
|
|
|
|
os);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (allocation_done == lang_final_phase_enum)
|
|
|
|
einfo (_("%F%S: undefined symbol `%s' referenced in expression\n"),
|
|
|
|
tree->name.name);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ADDR:
|
|
|
|
if (allocation_done != lang_first_phase_enum)
|
|
|
|
{
|
|
|
|
lang_output_section_statement_type *os;
|
|
|
|
|
|
|
|
os = lang_output_section_find (tree->name.name);
|
|
|
|
check (os, tree->name.name, "ADDR");
|
|
|
|
result = new_rel (0, os);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
result = invalid ();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case LOADADDR:
|
|
|
|
if (allocation_done != lang_first_phase_enum)
|
|
|
|
{
|
|
|
|
lang_output_section_statement_type *os;
|
|
|
|
|
|
|
|
os = lang_output_section_find (tree->name.name);
|
|
|
|
check (os, tree->name.name, "LOADADDR");
|
|
|
|
if (os->load_base == NULL)
|
2000-07-26 04:22:40 +04:00
|
|
|
result = new_rel (0, os);
|
2001-04-23 16:27:01 +04:00
|
|
|
else
|
|
|
|
result = exp_fold_tree_no_dot (os->load_base,
|
|
|
|
abs_output_section,
|
|
|
|
allocation_done);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
result = invalid ();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case SIZEOF:
|
Fix a problem with placing orphaned sections (like the ones with
C-referenceable names, i.e. "link sets") that happens with explicit
LMAs in the linker script:
* As orphans are sorted after sections, the effective
size of the section is changed. Record a size ajustment
for each orphan that is sorted after the section.
...and for sections which have an explicit load address expression:
* The first time an orphan is sorted after a section,
copy the load_base expression to the orphan, adding
the _unadjusted_ size of the parent section. We need
to use the unadjusted size because by the time the
expression can be folded, all of the orphan size
adjustments will have accumulated, resulting in misplacing
the orphan.
* For each subsequent orphan sorted after a section,
set the load_base of the orphan to the load address
of the previous orphan plus the size of the previous
orphan (actually, the unadjusted size, but for orphans,
size and unadjusted size are always equal).
2002-11-20 09:51:07 +03:00
|
|
|
case SIZEOF_UNADJ: /* not actually allowed in grammar */
|
2001-04-23 16:27:01 +04:00
|
|
|
if (allocation_done != lang_first_phase_enum)
|
|
|
|
{
|
|
|
|
int opb = bfd_octets_per_byte (output_bfd);
|
|
|
|
lang_output_section_statement_type *os;
|
|
|
|
|
|
|
|
os = lang_output_section_find (tree->name.name);
|
|
|
|
check (os, tree->name.name, "SIZEOF");
|
|
|
|
result = new_abs (os->bfd_section->_raw_size / opb);
|
Fix a problem with placing orphaned sections (like the ones with
C-referenceable names, i.e. "link sets") that happens with explicit
LMAs in the linker script:
* As orphans are sorted after sections, the effective
size of the section is changed. Record a size ajustment
for each orphan that is sorted after the section.
...and for sections which have an explicit load address expression:
* The first time an orphan is sorted after a section,
copy the load_base expression to the orphan, adding
the _unadjusted_ size of the parent section. We need
to use the unadjusted size because by the time the
expression can be folded, all of the orphan size
adjustments will have accumulated, resulting in misplacing
the orphan.
* For each subsequent orphan sorted after a section,
set the load_base of the orphan to the load address
of the previous orphan plus the size of the previous
orphan (actually, the unadjusted size, but for orphans,
size and unadjusted size are always equal).
2002-11-20 09:51:07 +03:00
|
|
|
|
|
|
|
/* If the output section has a size adjustment, and we've
|
|
|
|
not been asked for the unadjusted size, apply it. */
|
|
|
|
if (os->size_adj && tree->type.node_code != SIZEOF_UNADJ)
|
|
|
|
result = exp_fold_tree_no_dot
|
|
|
|
(exp_binop ('+', exp_intop (result.value), os->size_adj),
|
|
|
|
abs_output_section, allocation_done);
|
2001-04-23 16:27:01 +04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
result = invalid ();
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
FAIL ();
|
|
|
|
break;
|
|
|
|
}
|
2000-07-26 04:22:40 +04:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
2001-04-23 16:27:01 +04:00
|
|
|
|
|
|
|
etree_value_type
|
2000-07-26 04:22:40 +04:00
|
|
|
exp_fold_tree (tree, current_section, allocation_done, dot, dotp)
|
|
|
|
etree_type *tree;
|
|
|
|
lang_output_section_statement_type *current_section;
|
2001-04-23 16:27:01 +04:00
|
|
|
lang_phase_type allocation_done;
|
2000-07-26 04:22:40 +04:00
|
|
|
bfd_vma dot;
|
|
|
|
bfd_vma *dotp;
|
|
|
|
{
|
|
|
|
etree_value_type result;
|
|
|
|
|
|
|
|
if (tree == NULL)
|
|
|
|
{
|
|
|
|
result.valid_p = false;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2001-04-23 16:27:01 +04:00
|
|
|
switch (tree->type.node_class)
|
2000-07-26 04:22:40 +04:00
|
|
|
{
|
|
|
|
case etree_value:
|
|
|
|
result = new_rel (tree->value.value, current_section);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case etree_rel:
|
|
|
|
if (allocation_done != lang_final_phase_enum)
|
|
|
|
result.valid_p = false;
|
|
|
|
else
|
|
|
|
result = new_rel ((tree->rel.value
|
|
|
|
+ tree->rel.section->output_section->vma
|
|
|
|
+ tree->rel.section->output_offset),
|
|
|
|
current_section);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case etree_assert:
|
|
|
|
result = exp_fold_tree (tree->assert_s.child,
|
2001-04-23 16:27:01 +04:00
|
|
|
current_section,
|
|
|
|
allocation_done, dot, dotp);
|
2000-07-26 04:22:40 +04:00
|
|
|
if (result.valid_p)
|
|
|
|
{
|
|
|
|
if (! result.value)
|
|
|
|
einfo ("%F%P: %s\n", tree->assert_s.message);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case etree_unary:
|
|
|
|
result = exp_fold_tree (tree->unary.child,
|
|
|
|
current_section,
|
|
|
|
allocation_done, dot, dotp);
|
|
|
|
if (result.valid_p)
|
|
|
|
{
|
2001-04-23 16:27:01 +04:00
|
|
|
switch (tree->type.node_code)
|
2000-07-26 04:22:40 +04:00
|
|
|
{
|
|
|
|
case ALIGN_K:
|
|
|
|
if (allocation_done != lang_first_phase_enum)
|
|
|
|
result = new_rel_from_section (ALIGN_N (dot, result.value),
|
|
|
|
current_section);
|
|
|
|
else
|
|
|
|
result.valid_p = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ABSOLUTE:
|
|
|
|
if (allocation_done != lang_first_phase_enum && result.valid_p)
|
|
|
|
{
|
|
|
|
result.value += result.section->bfd_section->vma;
|
|
|
|
result.section = abs_output_section;
|
|
|
|
}
|
2001-04-23 16:27:01 +04:00
|
|
|
else
|
2000-07-26 04:22:40 +04:00
|
|
|
result.valid_p = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '~':
|
|
|
|
make_abs (&result);
|
|
|
|
result.value = ~result.value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '!':
|
|
|
|
make_abs (&result);
|
|
|
|
result.value = !result.value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '-':
|
|
|
|
make_abs (&result);
|
|
|
|
result.value = -result.value;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NEXT:
|
|
|
|
/* Return next place aligned to value. */
|
|
|
|
if (allocation_done == lang_allocating_phase_enum)
|
|
|
|
{
|
|
|
|
make_abs (&result);
|
|
|
|
result.value = ALIGN_N (dot, result.value);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
result.valid_p = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
FAIL ();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case etree_trinary:
|
|
|
|
result = exp_fold_tree (tree->trinary.cond, current_section,
|
|
|
|
allocation_done, dot, dotp);
|
|
|
|
if (result.valid_p)
|
|
|
|
result = exp_fold_tree ((result.value
|
|
|
|
? tree->trinary.lhs
|
|
|
|
: tree->trinary.rhs),
|
|
|
|
current_section,
|
|
|
|
allocation_done, dot, dotp);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case etree_binary:
|
|
|
|
result = fold_binary (tree, current_section, allocation_done,
|
|
|
|
dot, dotp);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case etree_assign:
|
|
|
|
case etree_provide:
|
2001-08-14 06:57:43 +04:00
|
|
|
case etree_provided:
|
2000-07-26 04:22:40 +04:00
|
|
|
if (tree->assign.dst[0] == '.' && tree->assign.dst[1] == 0)
|
|
|
|
{
|
|
|
|
/* Assignment to dot can only be done during allocation */
|
2001-08-14 06:57:43 +04:00
|
|
|
if (tree->type.node_class != etree_assign)
|
2000-07-26 04:22:40 +04:00
|
|
|
einfo (_("%F%S can not PROVIDE assignment to location counter\n"));
|
|
|
|
if (allocation_done == lang_allocating_phase_enum
|
|
|
|
|| (allocation_done == lang_final_phase_enum
|
|
|
|
&& current_section == abs_output_section))
|
|
|
|
{
|
|
|
|
result = exp_fold_tree (tree->assign.src,
|
|
|
|
current_section,
|
|
|
|
lang_allocating_phase_enum, dot,
|
|
|
|
dotp);
|
|
|
|
if (! result.valid_p)
|
|
|
|
einfo (_("%F%S invalid assignment to location counter\n"));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (current_section == NULL)
|
|
|
|
einfo (_("%F%S assignment to location counter invalid outside of SECTION\n"));
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bfd_vma nextdot;
|
|
|
|
|
|
|
|
nextdot = (result.value
|
|
|
|
+ current_section->bfd_section->vma);
|
|
|
|
if (nextdot < dot
|
|
|
|
&& current_section != abs_output_section)
|
|
|
|
{
|
|
|
|
einfo (_("%F%S cannot move location counter backwards (from %V to %V)\n"),
|
|
|
|
dot, nextdot);
|
|
|
|
}
|
|
|
|
else
|
2001-04-23 16:27:01 +04:00
|
|
|
*dotp = nextdot;
|
2000-07-26 04:22:40 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
result = exp_fold_tree (tree->assign.src,
|
|
|
|
current_section, allocation_done,
|
|
|
|
dot, dotp);
|
|
|
|
if (result.valid_p)
|
|
|
|
{
|
|
|
|
boolean create;
|
|
|
|
struct bfd_link_hash_entry *h;
|
|
|
|
|
|
|
|
if (tree->type.node_class == etree_assign)
|
|
|
|
create = true;
|
|
|
|
else
|
|
|
|
create = false;
|
|
|
|
h = bfd_link_hash_lookup (link_info.hash, tree->assign.dst,
|
|
|
|
create, false, false);
|
|
|
|
if (h == (struct bfd_link_hash_entry *) NULL)
|
|
|
|
{
|
|
|
|
if (tree->type.node_class == etree_assign)
|
|
|
|
einfo (_("%P%F:%s: hash creation failed\n"),
|
|
|
|
tree->assign.dst);
|
|
|
|
}
|
|
|
|
else if (tree->type.node_class == etree_provide
|
|
|
|
&& h->type != bfd_link_hash_undefined
|
|
|
|
&& h->type != bfd_link_hash_common)
|
|
|
|
{
|
|
|
|
/* Do nothing. The symbol was defined by some
|
|
|
|
object. */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* FIXME: Should we worry if the symbol is already
|
|
|
|
defined? */
|
|
|
|
h->type = bfd_link_hash_defined;
|
|
|
|
h->u.def.value = result.value;
|
|
|
|
h->u.def.section = result.section->bfd_section;
|
2001-08-14 06:57:43 +04:00
|
|
|
if (tree->type.node_class == etree_provide)
|
|
|
|
tree->type.node_class = etree_provided;
|
2000-07-26 04:22:40 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case etree_name:
|
|
|
|
result = fold_name (tree, current_section, allocation_done, dot);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
FAIL ();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2001-04-23 16:27:01 +04:00
|
|
|
static etree_value_type
|
2000-07-26 04:22:40 +04:00
|
|
|
exp_fold_tree_no_dot (tree, current_section, allocation_done)
|
|
|
|
etree_type *tree;
|
|
|
|
lang_output_section_statement_type *current_section;
|
|
|
|
lang_phase_type allocation_done;
|
|
|
|
{
|
2001-04-23 16:27:01 +04:00
|
|
|
return exp_fold_tree (tree, current_section, allocation_done,
|
|
|
|
(bfd_vma) 0, (bfd_vma *) NULL);
|
2000-07-26 04:22:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
etree_type *
|
|
|
|
exp_binop (code, lhs, rhs)
|
|
|
|
int code;
|
|
|
|
etree_type *lhs;
|
|
|
|
etree_type *rhs;
|
|
|
|
{
|
|
|
|
etree_type value, *new;
|
|
|
|
etree_value_type r;
|
|
|
|
|
|
|
|
value.type.node_code = code;
|
|
|
|
value.binary.lhs = lhs;
|
|
|
|
value.binary.rhs = rhs;
|
|
|
|
value.type.node_class = etree_binary;
|
2001-04-23 16:27:01 +04:00
|
|
|
r = exp_fold_tree_no_dot (&value,
|
|
|
|
abs_output_section,
|
|
|
|
lang_first_phase_enum);
|
2000-07-26 04:22:40 +04:00
|
|
|
if (r.valid_p)
|
|
|
|
{
|
2001-04-23 16:27:01 +04:00
|
|
|
return exp_intop (r.value);
|
2000-07-26 04:22:40 +04:00
|
|
|
}
|
|
|
|
new = (etree_type *) stat_alloc (sizeof (new->binary));
|
2001-04-23 16:27:01 +04:00
|
|
|
memcpy ((char *) new, (char *) &value, sizeof (new->binary));
|
2000-07-26 04:22:40 +04:00
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
etree_type *
|
|
|
|
exp_trinop (code, cond, lhs, rhs)
|
|
|
|
int code;
|
|
|
|
etree_type *cond;
|
|
|
|
etree_type *lhs;
|
|
|
|
etree_type *rhs;
|
|
|
|
{
|
|
|
|
etree_type value, *new;
|
|
|
|
etree_value_type r;
|
|
|
|
value.type.node_code = code;
|
|
|
|
value.trinary.lhs = lhs;
|
|
|
|
value.trinary.cond = cond;
|
|
|
|
value.trinary.rhs = rhs;
|
|
|
|
value.type.node_class = etree_trinary;
|
2001-04-23 16:27:01 +04:00
|
|
|
r = exp_fold_tree_no_dot (&value,
|
|
|
|
(lang_output_section_statement_type *) NULL,
|
|
|
|
lang_first_phase_enum);
|
|
|
|
if (r.valid_p)
|
|
|
|
{
|
|
|
|
return exp_intop (r.value);
|
|
|
|
}
|
2000-07-26 04:22:40 +04:00
|
|
|
new = (etree_type *) stat_alloc (sizeof (new->trinary));
|
2001-04-23 16:27:01 +04:00
|
|
|
memcpy ((char *) new, (char *) &value, sizeof (new->trinary));
|
2000-07-26 04:22:40 +04:00
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
etree_type *
|
|
|
|
exp_unop (code, child)
|
|
|
|
int code;
|
|
|
|
etree_type *child;
|
|
|
|
{
|
|
|
|
etree_type value, *new;
|
|
|
|
|
|
|
|
etree_value_type r;
|
|
|
|
value.unary.type.node_code = code;
|
|
|
|
value.unary.child = child;
|
|
|
|
value.unary.type.node_class = etree_unary;
|
2001-04-23 16:27:01 +04:00
|
|
|
r = exp_fold_tree_no_dot (&value, abs_output_section,
|
|
|
|
lang_first_phase_enum);
|
|
|
|
if (r.valid_p)
|
|
|
|
{
|
|
|
|
return exp_intop (r.value);
|
|
|
|
}
|
2000-07-26 04:22:40 +04:00
|
|
|
new = (etree_type *) stat_alloc (sizeof (new->unary));
|
2001-04-23 16:27:01 +04:00
|
|
|
memcpy ((char *) new, (char *) &value, sizeof (new->unary));
|
2000-07-26 04:22:40 +04:00
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
etree_type *
|
|
|
|
exp_nameop (code, name)
|
|
|
|
int code;
|
|
|
|
CONST char *name;
|
|
|
|
{
|
|
|
|
etree_type value, *new;
|
|
|
|
etree_value_type r;
|
|
|
|
value.name.type.node_code = code;
|
|
|
|
value.name.name = name;
|
|
|
|
value.name.type.node_class = etree_name;
|
|
|
|
|
2001-04-23 16:27:01 +04:00
|
|
|
r = exp_fold_tree_no_dot (&value,
|
|
|
|
(lang_output_section_statement_type *) NULL,
|
|
|
|
lang_first_phase_enum);
|
|
|
|
if (r.valid_p)
|
|
|
|
{
|
|
|
|
return exp_intop (r.value);
|
|
|
|
}
|
2000-07-26 04:22:40 +04:00
|
|
|
new = (etree_type *) stat_alloc (sizeof (new->name));
|
2001-04-23 16:27:01 +04:00
|
|
|
memcpy ((char *) new, (char *) &value, sizeof (new->name));
|
2000-07-26 04:22:40 +04:00
|
|
|
return new;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
etree_type *
|
|
|
|
exp_assop (code, dst, src)
|
|
|
|
int code;
|
|
|
|
CONST char *dst;
|
|
|
|
etree_type *src;
|
|
|
|
{
|
|
|
|
etree_type value, *new;
|
|
|
|
|
|
|
|
value.assign.type.node_code = code;
|
|
|
|
|
|
|
|
value.assign.src = src;
|
|
|
|
value.assign.dst = dst;
|
|
|
|
value.assign.type.node_class = etree_assign;
|
|
|
|
|
|
|
|
#if 0
|
2001-04-23 16:27:01 +04:00
|
|
|
if (exp_fold_tree_no_dot (&value, &result))
|
|
|
|
{
|
|
|
|
return exp_intop (result);
|
|
|
|
}
|
2000-07-26 04:22:40 +04:00
|
|
|
#endif
|
2001-04-23 16:27:01 +04:00
|
|
|
new = (etree_type *) stat_alloc (sizeof (new->assign));
|
|
|
|
memcpy ((char *) new, (char *) &value, sizeof (new->assign));
|
2000-07-26 04:22:40 +04:00
|
|
|
return new;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle PROVIDE. */
|
|
|
|
|
|
|
|
etree_type *
|
|
|
|
exp_provide (dst, src)
|
|
|
|
const char *dst;
|
|
|
|
etree_type *src;
|
|
|
|
{
|
|
|
|
etree_type *n;
|
|
|
|
|
|
|
|
n = (etree_type *) stat_alloc (sizeof (n->assign));
|
|
|
|
n->assign.type.node_code = '=';
|
|
|
|
n->assign.type.node_class = etree_provide;
|
|
|
|
n->assign.src = src;
|
|
|
|
n->assign.dst = dst;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle ASSERT. */
|
|
|
|
|
|
|
|
etree_type *
|
|
|
|
exp_assert (exp, message)
|
|
|
|
etree_type *exp;
|
|
|
|
const char *message;
|
|
|
|
{
|
|
|
|
etree_type *n;
|
|
|
|
|
|
|
|
n = (etree_type *) stat_alloc (sizeof (n->assert_s));
|
|
|
|
n->assert_s.type.node_code = '!';
|
|
|
|
n->assert_s.type.node_class = etree_assert;
|
|
|
|
n->assert_s.child = exp;
|
|
|
|
n->assert_s.message = message;
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2001-04-23 16:27:01 +04:00
|
|
|
void
|
2000-07-26 04:22:40 +04:00
|
|
|
exp_print_tree (tree)
|
|
|
|
etree_type *tree;
|
|
|
|
{
|
2001-04-23 16:27:01 +04:00
|
|
|
switch (tree->type.node_class)
|
|
|
|
{
|
|
|
|
case etree_value:
|
|
|
|
minfo ("0x%v", tree->value.value);
|
|
|
|
return;
|
|
|
|
case etree_rel:
|
|
|
|
if (tree->rel.section->owner != NULL)
|
|
|
|
minfo ("%B:", tree->rel.section->owner);
|
|
|
|
minfo ("%s+0x%v", tree->rel.section->name, tree->rel.value);
|
|
|
|
return;
|
|
|
|
case etree_assign:
|
2000-07-26 04:22:40 +04:00
|
|
|
#if 0
|
2001-04-23 16:27:01 +04:00
|
|
|
if (tree->assign.dst->sdefs != (asymbol *) NULL)
|
|
|
|
{
|
|
|
|
fprintf (config.map_file, "%s (%x) ", tree->assign.dst->name,
|
|
|
|
tree->assign.dst->sdefs->value);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf (config.map_file, "%s (UNDEFINED)", tree->assign.dst->name);
|
|
|
|
}
|
2000-07-26 04:22:40 +04:00
|
|
|
#endif
|
2001-04-23 16:27:01 +04:00
|
|
|
fprintf (config.map_file, "%s", tree->assign.dst);
|
|
|
|
exp_print_token (tree->type.node_code);
|
|
|
|
exp_print_tree (tree->assign.src);
|
|
|
|
break;
|
|
|
|
case etree_provide:
|
2001-08-14 06:57:43 +04:00
|
|
|
case etree_provided:
|
2001-04-23 16:27:01 +04:00
|
|
|
fprintf (config.map_file, "PROVIDE (%s, ", tree->assign.dst);
|
|
|
|
exp_print_tree (tree->assign.src);
|
|
|
|
fprintf (config.map_file, ")");
|
|
|
|
break;
|
|
|
|
case etree_binary:
|
|
|
|
fprintf (config.map_file, "(");
|
|
|
|
exp_print_tree (tree->binary.lhs);
|
|
|
|
exp_print_token (tree->type.node_code);
|
|
|
|
exp_print_tree (tree->binary.rhs);
|
|
|
|
fprintf (config.map_file, ")");
|
|
|
|
break;
|
|
|
|
case etree_trinary:
|
|
|
|
exp_print_tree (tree->trinary.cond);
|
|
|
|
fprintf (config.map_file, "?");
|
|
|
|
exp_print_tree (tree->trinary.lhs);
|
|
|
|
fprintf (config.map_file, ":");
|
|
|
|
exp_print_tree (tree->trinary.rhs);
|
|
|
|
break;
|
|
|
|
case etree_unary:
|
|
|
|
exp_print_token (tree->unary.type.node_code);
|
|
|
|
if (tree->unary.child)
|
|
|
|
{
|
|
|
|
fprintf (config.map_file, "(");
|
|
|
|
exp_print_tree (tree->unary.child);
|
|
|
|
fprintf (config.map_file, ")");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case etree_assert:
|
|
|
|
fprintf (config.map_file, "ASSERT (");
|
|
|
|
exp_print_tree (tree->assert_s.child);
|
|
|
|
fprintf (config.map_file, ", %s)", tree->assert_s.message);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case etree_undef:
|
|
|
|
fprintf (config.map_file, "????????");
|
|
|
|
break;
|
|
|
|
case etree_name:
|
|
|
|
if (tree->type.node_code == NAME)
|
|
|
|
{
|
|
|
|
fprintf (config.map_file, "%s", tree->name.name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
exp_print_token (tree->type.node_code);
|
|
|
|
if (tree->name.name)
|
|
|
|
fprintf (config.map_file, "(%s)", tree->name.name);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
FAIL ();
|
|
|
|
break;
|
2000-07-26 04:22:40 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bfd_vma
|
|
|
|
exp_get_vma (tree, def, name, allocation_done)
|
|
|
|
etree_type *tree;
|
|
|
|
bfd_vma def;
|
|
|
|
char *name;
|
|
|
|
lang_phase_type allocation_done;
|
|
|
|
{
|
|
|
|
etree_value_type r;
|
|
|
|
|
|
|
|
if (tree != NULL)
|
|
|
|
{
|
|
|
|
r = exp_fold_tree_no_dot (tree, abs_output_section, allocation_done);
|
|
|
|
if (! r.valid_p && name != NULL)
|
|
|
|
einfo (_("%F%S nonconstant expression for %s\n"), name);
|
|
|
|
return r.value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return def;
|
|
|
|
}
|
|
|
|
|
2001-04-23 16:27:01 +04:00
|
|
|
int
|
|
|
|
exp_get_value_int (tree, def, name, allocation_done)
|
2000-07-26 04:22:40 +04:00
|
|
|
etree_type *tree;
|
|
|
|
int def;
|
|
|
|
char *name;
|
|
|
|
lang_phase_type allocation_done;
|
|
|
|
{
|
2001-04-23 16:27:01 +04:00
|
|
|
return (int) exp_get_vma (tree, (bfd_vma) def, name, allocation_done);
|
2000-07-26 04:22:40 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
bfd_vma
|
|
|
|
exp_get_abs_int (tree, def, name, allocation_done)
|
|
|
|
etree_type *tree;
|
|
|
|
int def ATTRIBUTE_UNUSED;
|
|
|
|
char *name;
|
|
|
|
lang_phase_type allocation_done;
|
|
|
|
{
|
|
|
|
etree_value_type res;
|
|
|
|
res = exp_fold_tree_no_dot (tree, abs_output_section, allocation_done);
|
|
|
|
|
|
|
|
if (res.valid_p)
|
|
|
|
{
|
|
|
|
res.value += res.section->bfd_section->vma;
|
|
|
|
}
|
2001-04-23 16:27:01 +04:00
|
|
|
else
|
|
|
|
{
|
|
|
|
einfo (_("%F%S non constant expression for %s\n"), name);
|
|
|
|
}
|
2000-07-26 04:22:40 +04:00
|
|
|
return res.value;
|
|
|
|
}
|