diff options
Diffstat (limited to 'ld/ldexp.c')
-rw-r--r-- | ld/ldexp.c | 985 |
1 files changed, 985 insertions, 0 deletions
diff --git a/ld/ldexp.c b/ld/ldexp.c new file mode 100644 index 0000000..6c9d726 --- /dev/null +++ b/ld/ldexp.c @@ -0,0 +1,985 @@ +/* This module handles expression trees. +Copyright (C) 1991, 92, 93, 94, 95, 96, 97, 1998 Free Software Foundation, Inc. +Written by Steve Chamberlain of Cygnus Support (sac@cygnus.com). + +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. */ + +/* +This module is in charge of working out the contents of expressions. + +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. + +*/ + + +#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; +{ + 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 } + }; + unsigned int idx; + + for (idx = 0; table[idx].name != (char*)NULL; idx++) { + if (table[idx].code == code) { + fprintf(config.map_file, "%s", table[idx].name); + return; + } + } + /* Not in table, just print it alone */ + fprintf(config.map_file, "%c",code); +} + +static void +make_abs (ptr) + etree_value_type *ptr; +{ + asection *s = ptr->section->bfd_section; + ptr->value += s->vma; + ptr->section = abs_output_section; +} + +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; +} + +static void +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; +{ + etree_type *new = (etree_type *) stat_alloc(sizeof(new->value)); + 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; + + new.value -= section->bfd_section->vma; + + return new; +} + +static etree_value_type +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, + allocation_done, dot,dotp) ; + 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) + { + make_abs(&result); + make_abs(&other); + } + + switch (tree->type.node_code) + { + 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; + BOP('+',+); + BOP('*',*); + BOP('-',-); + BOP(LSHIFT,<<); + BOP(RSHIFT,>>); + BOP(EQ,==); + BOP(NE,!=); + BOP('<',<); + BOP('>',>); + BOP(LE,<=); + BOP(GE,>=); + BOP('&',&); + BOP('^',^); + BOP('|',|); + BOP(ANDAND,&&); + BOP(OROR,||); + + case MAX_K: + if (result.value < other.value) + result = other; + break; + + case MIN_K: + if (result.value > other.value) + result = other; + break; + + default: + FAIL(); + } + } + else + { + result.valid_p = false; + } + } + + return result; +} + +etree_value_type +invalid () +{ + etree_value_type new; + new.valid_p = false; + return new; +} + +static etree_value_type +fold_name (tree, current_section, allocation_done, dot) + etree_type *tree; + lang_output_section_statement_type *current_section; + lang_phase_type allocation_done; + bfd_vma dot; +{ + etree_value_type result; + 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 + { + result.valid_p = false; + } + break; + case DEFINED: + if (allocation_done == lang_first_phase_enum) + result.valid_p = false; + 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) + result = new_rel (0, os); + else + result = exp_fold_tree_no_dot (os->load_base, + abs_output_section, + allocation_done); + } + else + result = invalid (); + break; + + case SIZEOF: + 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, "SIZEOF"); + result = new_abs (os->bfd_section->_raw_size); + } + else + result = invalid (); + break; + + default: + FAIL(); + break; + } + + return result; +} +etree_value_type +exp_fold_tree (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; + + if (tree == NULL) + { + result.valid_p = false; + return result; + } + + switch (tree->type.node_class) + { + 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, + current_section, + allocation_done, dot, dotp); + 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) + { + switch (tree->type.node_code) + { + 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; + } + else + 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: + if (tree->assign.dst[0] == '.' && tree->assign.dst[1] == 0) + { + /* Assignment to dot can only be done during allocation */ + if (tree->type.node_class == etree_provide) + 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 + *dotp = nextdot; + } + } + } + } + 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; + } + } + } + break; + + case etree_name: + result = fold_name (tree, current_section, allocation_done, dot); + break; + + default: + FAIL (); + break; + } + + return result; +} + +static etree_value_type +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; +{ +return exp_fold_tree(tree, current_section, allocation_done, (bfd_vma) + 0, (bfd_vma *)NULL); +} + +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; + r = exp_fold_tree_no_dot(&value, + abs_output_section, + lang_first_phase_enum ); + if (r.valid_p) + { + return exp_intop(r.value); + } + new = (etree_type *) stat_alloc (sizeof (new->binary)); + memcpy((char *)new, (char *)&value, sizeof(new->binary)); + 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; + 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); + } + new = (etree_type *) stat_alloc (sizeof (new->trinary)); + memcpy((char *)new,(char *) &value, sizeof(new->trinary)); + 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; + r = exp_fold_tree_no_dot(&value,abs_output_section, + lang_first_phase_enum); + if (r.valid_p) { + return exp_intop(r.value); + } + new = (etree_type *) stat_alloc (sizeof (new->unary)); + memcpy((char *)new, (char *)&value, sizeof(new->unary)); + 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; + + + 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); + } + new = (etree_type *) stat_alloc (sizeof (new->name)); + memcpy((char *)new, (char *)&value, sizeof(new->name)); + 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 + if (exp_fold_tree_no_dot(&value, &result)) { + return exp_intop(result); + } +#endif + new = (etree_type*) stat_alloc (sizeof (new->assign)); + memcpy((char *)new, (char *)&value, sizeof(new->assign)); + 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; +} + +void +exp_print_tree (tree) + etree_type *tree; +{ + 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: +#if 0 + 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); + } +#endif + 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: + 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; + } +} + +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; +} + +int +exp_get_value_int (tree,def,name, allocation_done) + etree_type *tree; + int def; + char *name; + lang_phase_type allocation_done; +{ + return (int)exp_get_vma(tree,(bfd_vma)def,name, allocation_done); +} + + +bfd_vma +exp_get_abs_int (tree, def, name, allocation_done) + etree_type *tree; + int def; + 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; + } + else { + einfo (_("%F%S non constant expression for %s\n"),name); + } + return res.value; +} |