binutils-gdb/ld/ldlang.c

2232 lines
51 KiB
C

/* Copyright (C) 1991 Free Software Foundation, Inc.
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 1, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
/* $Id$
*
*/
#include "sysdep.h"
#include "bfd.h"
#include "ld.h"
#include "ldmain.h"
#include "ldsym.h"
#include "ldgram.tab.h"
#include "ldmisc.h"
#include "ldlang.h"
#include "ldexp.h"
#include "ld-emul.h"
#include "ldlex.h"
/* EXPORTS */
extern unsigned int undefined_global_sym_count;
static char *startup_file;
static lang_input_statement_type *first_file;
lang_statement_list_type statement_list;
lang_statement_list_type *stat_ptr = &statement_list;
lang_statement_list_type lang_output_section_statement;
lang_statement_list_type input_file_chain;
lang_statement_list_type file_chain;
extern char *current_file;
static boolean placed_commons = false;
boolean lang_float_flag;
static lang_output_section_statement_type *default_common_section;
/* FORWARDS */
PROTO(static void, print_statements,(void));
PROTO(static void, print_statement,(lang_statement_union_type *,
lang_output_section_statement_type *));
/* EXPORTS */
boolean lang_has_input_file = false;
extern bfd *output_bfd;
size_t largest_section;
extern enum bfd_architecture ldfile_output_architecture;
extern unsigned long ldfile_output_machine;
extern char *ldfile_output_machine_name;
extern ldsym_type *symbol_head;
bfd_vma print_dot;
unsigned int commons_pending;
extern args_type command_line;
extern ld_config_type config;
char *entry_symbol;
lang_output_section_statement_type *create_object_symbols;
extern boolean had_script;
static boolean map_option_f;
boolean had_output_filename = false;
extern boolean write_map;
size_t longest_section_name = 8;
lang_input_statement_type *script_file;
section_userdata_type common_section_userdata;
asection common_section;
#ifdef __STDC__
#define cat(a,b) a##b
#else
#define cat(a,b) a/**/b
#endif
#define new_stat(x,y) (cat(x,_type)*) new_statement(cat(x,_enum), sizeof(cat(x,_type)),y)
#define outside_section_address(q) ( (q)->output_offset + (q)->output_section->vma)
#define outside_symbol_address(q) ((q)->value + outside_section_address(q->section))
boolean option_longmap = false;
static void lang_list_init(list)
lang_statement_list_type *list;
{
list->head = (lang_statement_union_type *)NULL;
list->tail = &list->head;
}
static void
print_section(name)
char *name;
{
printf("%*s", -longest_section_name, name);
}
static void
print_space()
{
printf(" ");
}
static void
print_nl()
{
printf("\n");
}
static void
print_address(value)
bfd_vma value;
{
printf("%8lx", value);
}
static void
print_size(value)
size_t value;
{
printf("%5x", (unsigned)value);
}
static void
print_alignment(value)
unsigned int value;
{
printf("2**%2u",value);
}
static void
print_fill(value)
fill_type value;
{
printf("%04x",(unsigned)value);
}
static
lang_statement_union_type *new_statement(type, size, list)
enum statement_enum type;
size_t size;
lang_statement_list_type *list;
{
lang_statement_union_type *new = (lang_statement_union_type *)
ldmalloc(size);
new->header.type = type;
new->header.next = (lang_statement_union_type *)NULL;
lang_statement_append(list, new, &new->header.next);
return new;
}
static lang_input_statement_type *
new_afile(name, file_type, target)
char *name;
lang_input_file_enum_type file_type;
char *target;
{
lang_input_statement_type *p = new_stat(lang_input_statement,
stat_ptr);
lang_has_input_file = true;
p->target = target;
switch (file_type) {
case lang_input_file_is_symbols_only_enum:
p->filename = name;
p->is_archive =false;
p->real = true;
p->local_sym_name= name;
p->just_syms_flag = true;
p->search_dirs_flag = false;
break;
case lang_input_file_is_fake_enum:
p->filename = name;
p->is_archive =false;
p->real = false;
p->local_sym_name= name;
p->just_syms_flag = false;
p->search_dirs_flag =false;
break;
case lang_input_file_is_l_enum:
p->is_archive = true;
p->filename = name;
p->real = true;
p->local_sym_name = concat("-l",name,"");
p->just_syms_flag = false;
p->search_dirs_flag = true;
break;
case lang_input_file_is_search_file_enum:
case lang_input_file_is_marker_enum:
p->filename = name;
p->is_archive =false;
p->real = true;
p->local_sym_name= name;
p->just_syms_flag = false;
p->search_dirs_flag =true;
break;
case lang_input_file_is_file_enum:
p->filename = name;
p->is_archive =false;
p->real = true;
p->local_sym_name= name;
p->just_syms_flag = false;
p->search_dirs_flag =false;
break;
default:
FAIL();
}
p->asymbols = (asymbol **)NULL;
p->superfile = (lang_input_statement_type *)NULL;
p->next_real_file = (lang_statement_union_type*)NULL;
p->next = (lang_statement_union_type*)NULL;
p->symbol_count = 0;
p->common_output_section = (asection *)NULL;
lang_statement_append(&input_file_chain,
(lang_statement_union_type *)p,
&p->next_real_file);
return p;
}
lang_input_statement_type *
lang_add_input_file(name,
file_type,
target)
char *name;
lang_input_file_enum_type file_type;
char *target;
{
/* Look it up or build a new one */
lang_input_statement_type *p;
for (p = (lang_input_statement_type *)input_file_chain.head;
p != (lang_input_statement_type *)NULL;
p = (lang_input_statement_type *)(p->next_real_file))
{
/* Sometimes we have incomplete entries in here */
if (p->filename != (char *)NULL) {
if(strcmp(name,p->filename) == 0) return p;
}
}
return new_afile(name, file_type, target);
}
void
lang_init()
{
stat_ptr= &statement_list;
lang_list_init(stat_ptr);
lang_list_init(&input_file_chain);
lang_list_init(&lang_output_section_statement);
lang_list_init(&file_chain);
first_file = lang_add_input_file((char *)NULL,
lang_input_file_is_marker_enum,
(char *)NULL);
}
static void
lang_init2()
{
script_file = lang_add_input_file("script file",
lang_input_file_is_fake_enum,
(char *)NULL);
script_file->the_bfd = bfd_create("script file", output_bfd);
script_file->symbol_count = 0;
common_section.userdata = &common_section_userdata;
}
/* this function mainains a dictionary of regions. If the *default*
region is asked for then a pointer to the first region is
returned. If there is no first pointer then one is created
*/
static lang_memory_region_type *lang_memory_region_list;
static lang_memory_region_type **lang_memory_region_list_tail = &lang_memory_region_list;
lang_memory_region_type *
lang_memory_region_lookup(name)
char *name;
{
lang_memory_region_type *p = lang_memory_region_list;
for (p = lang_memory_region_list;
p != ( lang_memory_region_type *)NULL;
p = p->next) {
if (strcmp(p->name, name) == 0) {
return p;
}
}
if (strcmp(name,"*default*")==0) {
/* This is the default region, dig out first one on the list */
if (lang_memory_region_list != (lang_memory_region_type*)NULL){
return lang_memory_region_list;
}
}
{
lang_memory_region_type *new =
(lang_memory_region_type *)ldmalloc(sizeof(lang_memory_region_type));
new->name = name;
new->next = (lang_memory_region_type *)NULL;
*lang_memory_region_list_tail = new;
lang_memory_region_list_tail = &new->next;
new->origin = 0;
new->length = ~0;
new->current = 0;
return new;
}
}
lang_output_section_statement_type *
lang_output_section_find(name)
char *name;
{
lang_statement_union_type *u;
lang_output_section_statement_type *lookup;
for (u = lang_output_section_statement.head;
u != (lang_statement_union_type *)NULL;
u = lookup->next)
{
lookup = &u->output_section_statement;
if (strcmp(name, lookup->name)==0) {
return lookup;
}
}
return (lang_output_section_statement_type *)NULL;
}
lang_output_section_statement_type *
lang_output_section_statement_lookup(name)
char *name;
{
lang_output_section_statement_type *lookup;
lookup =lang_output_section_find(name);
if (lookup == (lang_output_section_statement_type *)NULL) {
lookup =(lang_output_section_statement_type *)
new_stat(lang_output_section_statement, stat_ptr);
lookup->region = (lang_memory_region_type *)NULL;
lookup->fill = 0;
lookup->block_value = 1;
lookup->name = name;
lookup->next = (lang_statement_union_type*)NULL;
lookup->bfd_section = (asection *)NULL;
lookup->processed = false;
lookup->addr_tree = (etree_type *)NULL;
lang_list_init(&lookup->children);
lang_statement_append(&lang_output_section_statement,
(lang_statement_union_type *)lookup,
&lookup->next);
}
return lookup;
}
static void
print_flags(outfile, ignore_flags)
FILE *outfile;
lang_section_flags_type *ignore_flags;
{
fprintf(outfile,"(");
#if 0
if (flags->flag_read) fprintf(outfile,"R");
if (flags->flag_write) fprintf(outfile,"W");
if (flags->flag_executable) fprintf(outfile,"X");
if (flags->flag_loadable) fprintf(outfile,"L");
#endif
fprintf(outfile,")");
}
void
lang_map(outfile)
FILE *outfile;
{
lang_memory_region_type *m;
fprintf(outfile,"**MEMORY CONFIGURATION**\n\n");
fprintf(outfile,"name\t\torigin\t\tlength\t\tattributes\n");
for (m = lang_memory_region_list;
m != (lang_memory_region_type *)NULL;
m = m->next)
{
fprintf(outfile,"%-16s", m->name);
fprintf(outfile,"%08lx\t%08lx\t", m->origin, m->length);
print_flags(outfile, &m->flags);
fprintf(outfile,"\n");
}
fprintf(outfile,"\n\n**LINK EDITOR MEMORY MAP**\n\n");
fprintf(outfile,"output\t\tinput\t\tvirtual\n");
fprintf(outfile,"section\t\tsection\t\taddress\tsize\n\n");
print_statements();
}
/*
*
*/
static void init_os(s)
lang_output_section_statement_type *s;
{
section_userdata_type *new =
(section_userdata_type *)
ldmalloc(sizeof(section_userdata_type));
s->bfd_section = bfd_make_section(output_bfd, s->name);
s->bfd_section->output_section = s->bfd_section;
s->bfd_section->flags = SEC_NO_FLAGS;
/* We initialize an output sections output offset to minus its own */
/* vma to allow us to output a section through itself */
s->bfd_section->output_offset = 0;
get_userdata( s->bfd_section) = new;
}
static void
wild_doit(ptr, section,output, file)
lang_statement_list_type *ptr;
asection *section;
lang_output_section_statement_type *output;
lang_input_statement_type *file;
{
if(output->bfd_section == (asection *)NULL)
{
init_os(output);
}
if (section != (asection *)NULL
&& section->output_section == (asection *)NULL) {
/* Add a section reference to the list */
lang_input_section_type *new = new_stat(lang_input_section, ptr);
new->section = section;
new->ifile = file;
section->output_section = output->bfd_section;
section->output_section->flags |= section->flags;
if (section->alignment_power > output->bfd_section->alignment_power) {
output->bfd_section->alignment_power = section->alignment_power;
}
}
}
static asection *
our_bfd_get_section_by_name(abfd, section)
bfd *abfd;
char *section;
{
return bfd_get_section_by_name(abfd, section);
}
static void
wild_section(ptr, section, file , output)
lang_wild_statement_type *ptr;
char *section;
lang_input_statement_type *file;
lang_output_section_statement_type *output;
{
asection *s;
if (section == (char *)NULL) {
/* Do the creation to all sections in the file */
for (s = file->the_bfd->sections; s != (asection *)NULL; s=s->next) {
wild_doit(&ptr->children, s, output, file);
}
}
else {
/* Do the creation to the named section only */
wild_doit(&ptr->children,
our_bfd_get_section_by_name(file->the_bfd, section),
output, file);
}
}
static
lang_input_statement_type *lookup_name(name, target)
char *name;
char *target;
{
lang_input_statement_type *search;
for(search = (lang_input_statement_type *)input_file_chain.head;
search != (lang_input_statement_type *)NULL;
search = (lang_input_statement_type *)search->next_real_file)
{
if (search->filename == (char *)NULL && name == (char *)NULL) {
return search;
}
if (search->filename != (char *)NULL && name != (char *)NULL) {
if (strcmp(search->filename, name) == 0) {
Q_read_file_symbols(search);
return search;
}
}
}
/* There isn't an afile entry for this file yet, this must be */
/* because the name has only appeared inside a load script and not */
/* on the command line */
search = new_afile(name, lang_input_file_is_file_enum, target);
Q_read_file_symbols(search);
return search;
}
static void
wild(s, section, file, target, output)
lang_wild_statement_type *s;
char *section;
char *file;
char *target;
lang_output_section_statement_type *output;
{
lang_input_statement_type *f;
if (file == (char *)NULL) {
/* Perform the iteration over all files in the list */
for (f = (lang_input_statement_type *)file_chain.head;
f != (lang_input_statement_type *)NULL;
f = (lang_input_statement_type *)f->next) {
wild_section(s, section, f, output);
}
}
else {
/* Perform the iteration over a single file */
wild_section( s, section, lookup_name(file, target), output);
}
}
/*
read in all the files
*/
static bfd *
open_output(name, target)
char *name;
char *target;
{
extern char *output_filename;
bfd * output = bfd_openw(name, target);
output_filename = name;
if (output == (bfd *)NULL)
{
if (bfd_error == invalid_target) {
info("%P%F target %s not found\n", target);
}
info("%P%F problem opening output file %s, %E", name);
}
output->flags |= D_PAGED;
bfd_set_format(output, bfd_object);
return output;
}
extern char *default_target;
static void
lang_phase_0(sh,target)
lang_statement_union_type *sh;
char *target;
{
lang_statement_union_type *s = (lang_statement_union_type *)sh;
for (; s != (lang_statement_union_type *)NULL ; s = s->next)
{
switch (s->header.type) {
case lang_output_section_statement_enum:
lang_phase_0(s->output_section_statement.children.head,
target);
break;
case lang_output_statement_enum:
#if 1
output_bfd = open_output(s->output_statement.name,
target == (char *)NULL ?
default_target : target);
ldemul_set_output_arch();
#endif
break;
case lang_target_statement_enum:
target = s->target_statement.target;
break;
case lang_wild_statement_enum:
/* Maybe we should load the file's symbols */
if (s->wild_statement.filename) {
(void) lookup_name(s->wild_statement.filename, target);
}
break;
/* Attatch this to the current output section */
case lang_common_statement_enum:
case lang_fill_statement_enum:
case lang_input_section_enum:
case lang_object_symbols_statement_enum:
case lang_address_statement_enum:
case lang_data_statement_enum:
break;
case lang_afile_asection_pair_statement_enum:
FAIL();
break;
case lang_input_statement_enum:
if (s->input_statement.real == true) {
s->input_statement.target = target;
lookup_name(s->input_statement.filename, target);
}
break;
case lang_assignment_statement_enum:
#if 0
(void) exp_fold_tree(s->assignment_statement.exp,
output_section,
false);
#endif
break;
case lang_padding_statement_enum:
break;
}
}
}
/* If there are [COMMONS] statements, put a wild one into the bss section */
static void
lang_reasonable_defaults()
{
lang_output_section_statement_lookup(".text");
lang_output_section_statement_lookup(".data");
default_common_section =
lang_output_section_statement_lookup(".bss");
if (placed_commons == false) {
lang_wild_statement_type *new =
new_stat(lang_wild_statement,
&default_common_section->children);
new->section_name = "COMMON";
new->filename = (char *)NULL;
lang_list_init(&new->children);
}
}
static void lang()
{
if (had_script == false) {
parse_line(ldemul_get_script());
}
lang_reasonable_defaults();
lang_phase_0(statement_list.head,default_target);
}
/* Open input files and attatch to output sections */
static void
lang_open_input(s, target, output_section_statement)
lang_statement_union_type *s;
char *target;
lang_output_section_statement_type *output_section_statement;
{
for (; s != (lang_statement_union_type *)NULL ; s = s->next)
{
switch (s->header.type) {
case lang_wild_statement_enum:
wild(&s->wild_statement, s->wild_statement.section_name,
s->wild_statement.filename, target,
output_section_statement);
break;
case lang_output_section_statement_enum:
lang_open_input(s->output_section_statement.children.head,
target,
&s->output_section_statement);
break;
case lang_output_statement_enum:
break;
case lang_target_statement_enum:
target = s->target_statement.target;
break;
case lang_common_statement_enum:
case lang_fill_statement_enum:
case lang_input_section_enum:
case lang_object_symbols_statement_enum:
case lang_data_statement_enum:
break;
case lang_afile_asection_pair_statement_enum:
FAIL();
break;
case lang_assignment_statement_enum:
case lang_padding_statement_enum:
break;
case lang_address_statement_enum:
/* Mark the specified section with the supplied address */
{
lang_output_section_statement_type *os =
lang_output_section_statement_lookup
(s->address_statement.section_name);
os->addr_tree = s->address_statement.address;
}
break;
case lang_input_statement_enum:
/* A standard input statement, has no wildcards */
/* Q_read_file_symbols(&s->input_statement);*/
break;
}
}
}
static void
print_output_section_statement(output_section_statement)
lang_output_section_statement_type *output_section_statement;
{
asection *section = output_section_statement->bfd_section;
print_nl();
print_section(output_section_statement->name);
if (section) {
print_dot = section->vma;
print_space();
print_section("");
print_space();
print_address(section->vma);
print_space();
print_size(section->size);
print_space();
print_alignment(section->alignment_power);
print_space();
#if 0
printf("%s flags", output_section_statement->region->name);
print_flags(stdout, &output_section_statement->flags);
#endif
}
else {
printf("No attached output section");
}
print_nl();
print_statement(output_section_statement->children.head,
output_section_statement);
}
static void
print_assignment(assignment, output_section)
lang_assignment_statement_type *assignment;
lang_output_section_statement_type *output_section;
{
etree_value_type result;
print_section("");
print_space();
print_section("");
print_space();
print_address(print_dot);
print_space();
result = exp_fold_tree(assignment->exp->assign.src,
output_section,
lang_final_phase_enum,
print_dot,
&print_dot);
if (result.valid) {
print_address(result.value);
}
else
{
printf("*undefined*");
}
print_space();
exp_print_tree(stdout, assignment->exp);
printf("\n");
}
static void
print_input_statement(statm)
lang_input_statement_type *statm;
{
printf("LOAD %s\n",statm->filename);
}
static void print_symbol(q)
asymbol *q;
{
print_section("");
printf(" ");
print_section("");
printf(" ");
print_address(outside_symbol_address(q));
printf(" %s", q->name ? q->name : " ");
print_nl();
}
static void
print_input_section(in)
lang_input_section_type *in;
{
asection *i = in->section;
if(i->size != 0) {
print_section("");
printf(" ");
print_section(i->name);
printf(" ");
if (i->output_section) {
print_address(i->output_section->vma + i->output_offset);
printf(" ");
print_size(i->size);
printf(" ");
print_alignment(i->alignment_power);
printf(" ");
if (in->ifile) {
bfd *abfd = in->ifile->the_bfd;
printf(" %s ",abfd->xvec->name);
if(abfd->my_archive != (bfd *)NULL) {
printf("[%s]%s", abfd->my_archive->filename,
abfd->filename);
}
else {
printf("%s", abfd->filename);
}
print_nl();
/* Find all the symbols in this file defined in this section */
{
asymbol **p;
for (p = in->ifile->asymbols; *p; p++) {
asymbol *q = *p;
if (bfd_get_section(q) == i && q->flags & BSF_GLOBAL) {
print_symbol(q);
}
}
}
}
else {
print_nl();
}
print_dot = outside_section_address(i) + i->size;
}
else {
printf("No output section allocated\n");
}
}
}
static void
print_common_statement()
{
ldsym_type *lgs;
print_section("");
print_space();
print_section(common_section.output_section->name);
print_space();
print_address(common_section.output_offset +
common_section.output_section->vma);
print_space();
print_size(common_section.size);
print_space();
printf("(common)");
print_nl();
/* Print out all the global symbols */
for (lgs = symbol_head; lgs != (ldsym_type *)NULL; lgs =
lgs->next) {
if (lgs->sdefs_chain) {
asymbol *def = *(lgs->sdefs_chain);
if (def->section == &common_section) {
print_symbol(def);
}
}
}
print_dot = common_section.output_offset +
common_section.output_section->vma + common_section.size;
}
static void
print_fill_statement(fill)
lang_fill_statement_type *fill;
{
printf("FILL mask ");
print_fill( fill->fill);
}
static void
print_data_statement(data)
lang_data_statement_type *data;
{
/* bfd_vma value; */
print_section("");
print_space();
print_section("");
print_space();
ASSERT(print_dot == data->output_vma);
print_address(data->output_vma);
print_space();
print_address(data->value);
print_space();
switch (data->type) {
case BYTE :
printf("BYTE ");
print_dot += BYTE_SIZE;
break;
case SHORT:
printf("SHORT ");
print_dot += SHORT_SIZE;
break;
case LONG:
printf("LONG ");
print_dot += LONG_SIZE;
break;
}
exp_print_tree(stdout, data->exp);
printf("\n");
}
static void
print_padding_statement(s)
lang_padding_statement_type *s;
{
print_section("");
print_space();
print_section("*fill*");
print_space();
print_address(s->output_offset + s->output_section->vma);
print_space();
print_size(s->size);
print_space();
print_fill(s->fill);
print_nl();
}
static void print_wild_statement(w,os)
lang_wild_statement_type *w;
lang_output_section_statement_type *os;
{
if (w->filename != (char *)NULL) {
printf("%s",w->filename);
}
else {
printf("*");
}
if (w->section_name != (char *)NULL) {
printf("(%s)",w->section_name);
}
else {
printf("(*)");
}
print_nl();
print_statement(w->children.head, os);
}
static void
print_statement(s, os)
lang_statement_union_type *s;
lang_output_section_statement_type *os;
{
while (s) {
switch (s->header.type) {
case lang_wild_statement_enum:
print_wild_statement(&s->wild_statement, os);
break;
default:
printf("Fail with %d\n",s->header.type);
FAIL();
break;
case lang_address_statement_enum:
printf("address\n");
break;
case lang_common_statement_enum:
print_common_statement();
break;
case lang_object_symbols_statement_enum:
printf("object symbols\n");
break;
case lang_fill_statement_enum:
print_fill_statement(&s->fill_statement);
break;
case lang_data_statement_enum:
print_data_statement(&s->data_statement);
break;
case lang_input_section_enum:
print_input_section(&s->input_section);
break;
case lang_padding_statement_enum:
print_padding_statement(&s->padding_statement);
break;
case lang_output_section_statement_enum:
print_output_section_statement(&s->output_section_statement);
break;
case lang_assignment_statement_enum:
print_assignment(&s->assignment_statement,
os);
break;
case lang_target_statement_enum:
printf("TARGET(%s)\n", s->target_statement.target);
break;
case lang_output_statement_enum:
printf("OUTPUT(%s)\n", s->output_statement.name);
break;
case lang_input_statement_enum:
print_input_statement(&s->input_statement);
break;
case lang_afile_asection_pair_statement_enum:
FAIL();
break;
}
s = s->next;
}
}
static void
print_statements()
{
print_statement(statement_list.head,
(lang_output_section_statement_type *)NULL);
}
static bfd_vma
insert_pad(this_ptr, fill, power, output_section_statement, dot)
lang_statement_union_type **this_ptr;
fill_type fill;
unsigned int power;
asection * output_section_statement;
bfd_vma dot;
{
/* Align this section first to the
input sections requirement, then
to the output section's requirement.
If this alignment is > than any seen before,
then record it too. Perform the alignment by
inserting a magic 'padding' statement.
*/
unsigned int alignment_needed = align_power(dot, power) - dot;
if (alignment_needed != 0)
{
lang_statement_union_type *new =
(lang_statement_union_type *)
ldmalloc(sizeof(lang_padding_statement_type));
/* Link into existing chain */
new->header.next = *this_ptr;
*this_ptr = new;
new->header.type = lang_padding_statement_enum;
new->padding_statement.output_section = output_section_statement;
new->padding_statement.output_offset =
dot - output_section_statement->vma;
new->padding_statement.fill = fill;
new->padding_statement.size = alignment_needed;
}
/* Remember the most restrictive alignment */
if (power > output_section_statement->alignment_power) {
output_section_statement->alignment_power = power;
}
output_section_statement->size += alignment_needed;
return alignment_needed + dot;
}
/*
size_common runs run though each global symboxl, and works
out how big the common section will be.
*/
static bfd_vma
size_common(output_section_statement, this_ptr, dot)
lang_output_section_statement_type *output_section_statement;
lang_statement_union_type **this_ptr;
bfd_vma dot;
{
extern ldsym_type *symbol_head;
ldsym_type *sp;
/* Make sure that each symbol is only defined once.
Allocate common symbols
Make the ref chain point to the defining asymbol.
*/
/* Now, for each symbol, verify that it is defined globally at most once.
Put the global value into the symbol entry.
Common symbols are allocated here, in the BSS section.
Each defined symbol is given a '->defined' field
which is the correct N_ code for its definition,
except in the case of common symbols with -r.
Then make all the references point at the symbol entry
instead of being chained together. */
common_section.name = output_section_statement->bfd_section->name;
common_section.output_section = output_section_statement->bfd_section;
common_section.output_offset =
dot - output_section_statement->bfd_section->vma;
if (config.relocateable_output == false ||
command_line.force_common_definition== true) {
dot = insert_pad(this_ptr,
0x0, 4, output_section_statement->bfd_section, dot);
for (sp = symbol_head; sp != (ldsym_type *)NULL; sp = sp->next)
{
/* Attatch this symbol to the correct output section*/
/* Allocate as common if wanted */
if (sp->scoms_chain )
{
unsigned long com = (*(sp->scoms_chain))->value;
/* Work out what alignment this common item s
hould be put on. Anything < int is int aligned,
anything bigger is self aligned,
up to the restriction of the machine */
unsigned int align = sizeof(int);
/* Round up size of object to nearest int */
com = ALIGN(com, sizeof(int));
/* See what alignment is necessary -*/
if (com) {
while ((com & align)==0) align <<=1;
/* FIXME */
if (align > 8) {
align = 8;
}
}
dot = ALIGN(dot, align);
/* Transmogrify this from a common symbol
into a definition of a symbol in common
*/
sp->sdefs_chain = sp->scoms_chain;
{
asymbol *com_ptr = *(sp->sdefs_chain);
sp->scoms_chain = (asymbol **)NULL;
commons_pending--;
/* Assign address, but keep section relative */
/* Force the symbol to belong in the bss section */
com_ptr->flags = BSF_EXPORT | BSF_GLOBAL ;
com_ptr->section = &common_section;
common_section.size += com;
if (write_map)
{
printf ("Allocating common %s: %lx at %lx\n",
sp->name,
com,
com_ptr->value);
}
com_ptr->value = common_section.size;
}
}
}
}
if (dot >
(common_section.output_section->vma +
common_section.output_section->size)) {
common_section.output_section->size =
dot - common_section.output_section->vma;
}
return dot + common_section.size;
}
static bfd_vma
size_input_section( this_ptr, output_section_statement, fill, dot)
lang_statement_union_type **this_ptr;
lang_output_section_statement_type*output_section_statement;
unsigned short fill;
bfd_vma dot;
{
lang_input_section_type *is = &((*this_ptr)->input_section);
asection *i = is->section;
dot = insert_pad(this_ptr, fill, i->alignment_power,
output_section_statement->bfd_section, dot);
/* remember the largest size so we can malloc the largest area */
/* needed for the output stage */
if (i->size > largest_section) {
largest_section = i->size;
}
/* Remember where in the output section this input section goes */
i->output_offset = dot - output_section_statement->bfd_section->vma;
/* Mark how big the output section must be to contain this now */
dot += i->size;
output_section_statement->bfd_section->size =
dot - output_section_statement->bfd_section->vma;
return dot ;
}
/* Work out the size of the output sections
from the sizes of the input sections */
static bfd_vma
lang_size_sections(s, output_section_statement, prev, fill, dot)
lang_statement_union_type *s;
lang_output_section_statement_type * output_section_statement;
lang_statement_union_type **prev;
unsigned short fill;
bfd_vma dot;
{
/* Size up the sections from their constituent parts */
for (; s != (lang_statement_union_type *)NULL ; s = s->next)
{
switch (s->header.type) {
case lang_output_section_statement_enum:
{
bfd_vma after;
lang_output_section_statement_type *os =
&(s->output_section_statement);
/* The start of a section */
if (os->addr_tree == (etree_type *)NULL) {
/* No address specified for this section, get one
from the region specification
*/
if (os->region == (lang_memory_region_type *)NULL) {
os->region = lang_memory_region_lookup("*default*");
}
dot = os->region->current;
}
else {
etree_value_type r ;
r = exp_fold_tree(os->addr_tree,
(lang_output_section_statement_type *)NULL,
lang_allocating_phase_enum,
dot, &dot);
if (r.valid == false) {
info("%F%S: non constant address expression for section %s\n",
os->name);
}
dot = r.value;
}
/* The section starts here */
/* First, align to what the section needs */
dot = align_power(dot, os->bfd_section->alignment_power);
os->bfd_section->vma = dot;
os->bfd_section->output_offset = 0;
(void) lang_size_sections(os->children.head, os, &os->children.head,
os->fill, dot);
/* Ignore the size of the input sections, use the vma and size to */
/* align against */
after = ALIGN(os->bfd_section->vma +
os->bfd_section->size,
os->block_value) ;
os->bfd_section->size = after - os->bfd_section->vma;
dot = os->bfd_section->vma + os->bfd_section->size;
os->processed = true;
/* Replace into region ? */
if (os->addr_tree == (etree_type *)NULL
&& os->region !=(lang_memory_region_type*)NULL ) {
os->region->current = dot;
}
}
break;
case lang_data_statement_enum:
{
unsigned int size;
s->data_statement.output_vma = dot;
s->data_statement.output_section =
output_section_statement->bfd_section;
switch (s->data_statement.type) {
case LONG:
size = LONG_SIZE;
break;
case SHORT:
size = SHORT_SIZE;
break;
case BYTE:
size = BYTE_SIZE;
break;
}
dot += size;
output_section_statement->bfd_section->size += size;
}
break;
case lang_wild_statement_enum:
dot = lang_size_sections(s->wild_statement.children.head,
output_section_statement,
&s->wild_statement.children.head,
fill, dot);
break;
case lang_object_symbols_statement_enum:
create_object_symbols = output_section_statement;
break;
case lang_output_statement_enum:
case lang_target_statement_enum:
break;
case lang_common_statement_enum:
dot = size_common(output_section_statement, prev, dot);
break;
case lang_input_section_enum:
dot = size_input_section(prev,
output_section_statement,
output_section_statement->fill, dot);
break;
case lang_input_statement_enum:
break;
case lang_fill_statement_enum:
fill = s->fill_statement.fill;
break;
case lang_assignment_statement_enum:
{
bfd_vma newdot = dot;
exp_fold_tree(s->assignment_statement.exp,
output_section_statement,
lang_allocating_phase_enum,
dot,
&newdot);
if (newdot != dot)
/* We've been moved ! so insert a pad */
{
lang_statement_union_type *new =
(lang_statement_union_type *)
ldmalloc(sizeof(lang_padding_statement_type));
/* Link into existing chain */
new->header.next = *prev;
*prev = new;
new->header.type = lang_padding_statement_enum;
new->padding_statement.output_section =
output_section_statement->bfd_section;
new->padding_statement.output_offset =
dot - output_section_statement->bfd_section->vma;
new->padding_statement.fill = fill;
new->padding_statement.size = newdot - dot;
output_section_statement->bfd_section->size +=
new->padding_statement.size;
dot = newdot;
}
}
break;
case lang_padding_statement_enum:
FAIL();
break;
default:
FAIL();
break;
case lang_address_statement_enum:
break;
}
prev = &s->header.next;
}
return dot;
}
static bfd_vma
lang_do_assignments(s, output_section_statement, fill, dot)
lang_statement_union_type *s;
lang_output_section_statement_type * output_section_statement;
unsigned short fill;
bfd_vma dot;
{
for (; s != (lang_statement_union_type *)NULL ; s = s->next)
{
switch (s->header.type) {
case lang_output_section_statement_enum:
{
lang_output_section_statement_type *os =
&(s->output_section_statement);
dot = os->bfd_section->vma;
(void) lang_do_assignments(os->children.head, os, os->fill, dot);
dot = os->bfd_section->vma + os->bfd_section->size;
}
break;
case lang_wild_statement_enum:
dot = lang_do_assignments(s->wild_statement.children.head,
output_section_statement,
fill, dot);
break;
case lang_object_symbols_statement_enum:
case lang_output_statement_enum:
case lang_target_statement_enum:
case lang_common_statement_enum:
break;
case lang_data_statement_enum:
{
etree_value_type value ;
value = exp_fold_tree(s->data_statement.exp,
0, lang_final_phase_enum, dot, &dot);
s->data_statement.value = value.value;
if (value.valid == false) info("%F%P: Invalid data statement\n");
}
switch (s->data_statement.type) {
case LONG:
dot += LONG_SIZE;
break;
case SHORT:
dot += SHORT_SIZE;
break;
case BYTE:
dot += BYTE_SIZE;
break;
}
break;
case lang_input_section_enum:
{
asection *in = s->input_section.section;
dot += in->size;
}
break;
case lang_input_statement_enum:
break;
case lang_fill_statement_enum:
fill = s->fill_statement.fill;
break;
case lang_assignment_statement_enum:
{
exp_fold_tree(s->assignment_statement.exp,
output_section_statement,
lang_final_phase_enum,
dot,
&dot);
}
break;
case lang_padding_statement_enum:
dot += s->padding_statement.size;
break;
default:
FAIL();
break;
case lang_address_statement_enum:
break;
}
}
return dot;
}
static void lang_relocate_globals()
{
/*
Each ldsym_type maintains a chain of pointers to asymbols which
references the definition. Replace each pointer to the referenence
with a pointer to only one place, preferably the definition. If
the defintion isn't available then the common symbol, and if
there isn't one of them then choose one reference.
*/
FOR_EACH_LDSYM(lgs) {
asymbol *it;
if (lgs->sdefs_chain) {
it = *(lgs->sdefs_chain);
}
else if (lgs->scoms_chain != (asymbol **)NULL) {
it = *(lgs->scoms_chain);
}
else if (lgs->srefs_chain != (asymbol **)NULL) {
it = *(lgs->srefs_chain);
}
else {
FAIL();
}
if (it != (asymbol *)NULL)
{
asymbol **ptr= lgs->srefs_chain;
while (ptr != (asymbol **)NULL) {
asymbol *ref = *ptr;
*ptr = it;
ptr = (asymbol **)(ref->udata);
}
}
}
}
/* now that all the jiggery pokery is finished, copy important data from
* out internal form to the bfd way. Also create a section
* for each dummy file
*/
static void
lang_create_output_section_statements()
{
lang_statement_union_type*os;
for (os = lang_output_section_statement.head;
os != (lang_statement_union_type*)NULL;
os = os->output_section_statement.next) {
lang_output_section_statement_type *s =
&os->output_section_statement;
init_os(s);
}
script_file->the_bfd->sections = output_bfd->sections;
}
static void
lang_finish()
{
ldsym_type *lgs;
if (entry_symbol == (char *)NULL) {
/* No entry has been specified, look for start */
entry_symbol = "start";
}
lgs = ldsym_get_soft(entry_symbol);
if (lgs && lgs->sdefs_chain) {
asymbol *sy = *(lgs->sdefs_chain);
/* We can set the entry address*/
bfd_set_start_address(output_bfd,
outside_symbol_address(sy));
}
else {
/* Can't find anything reasonable,
use the first address in the text section
*/
asection *ts = bfd_get_section_by_name(output_bfd, ".text");
if (ts) {
bfd_set_start_address(output_bfd, ts->vma);
}
}
}
/* By now we know the target architecture, and we may have an */
/* ldfile_output_machine_name */
static void
lang_check()
{
lang_statement_union_type *file;
for (file = file_chain.head;
file != (lang_statement_union_type *)NULL;
file=file->input_statement.next)
{
/* Inspect the architecture and ensure we're linking like
with like
*/
if (bfd_arch_compatible( file->input_statement.the_bfd,
output_bfd,
&ldfile_output_architecture,
&ldfile_output_machine)) {
bfd_set_arch_mach(output_bfd,
ldfile_output_architecture, ldfile_output_machine);
}
else {
enum bfd_architecture this_architecture =
bfd_get_architecture(file->input_statement.the_bfd);
unsigned long this_machine =
bfd_get_machine(file->input_statement.the_bfd);
info("%I: architecture %s",
file,
bfd_printable_arch_mach(this_architecture, this_machine));
info(" incompatible with output %s\n",
bfd_printable_arch_mach(ldfile_output_architecture,
ldfile_output_machine));
ldfile_output_architecture = this_architecture;
ldfile_output_machine = this_machine;
bfd_set_arch_mach(output_bfd,
ldfile_output_architecture,
ldfile_output_machine);
}
}
}
/*
* run through all the global common symbols and tie them
* to the output section requested.
*/
static void
lang_common()
{
ldsym_type *lgs;
if (config.relocateable_output == false ||
command_line.force_common_definition== true) {
for (lgs = symbol_head;
lgs != (ldsym_type *)NULL;
lgs=lgs->next)
{
asymbol *com ;
size_t size;
size_t align;
if (lgs->scoms_chain != (asymbol **)NULL) {
com = *(lgs->scoms_chain);
size = com->value;
align = sizeof(int);
/* Round up size of object to nearest int */
size = ALIGN(size, sizeof(int));
/* Force alignment */
if (size) {
while ((size & align)==0) align<<=1;
if (align > 8) {
align = 8;
}
}
/* Change from a common symbol into a definition of
a symbol */
lgs->sdefs_chain = lgs->scoms_chain;
lgs->scoms_chain = (asymbol **)NULL;
commons_pending--;
/* Point to the correct common section */
com->section =
((lang_input_statement_type *)
(com->the_bfd->usrdata))->common_section;
/* Fix the size of the common section */
com->flags = BSF_EXPORT | BSF_GLOBAL;
if (write_map)
{
printf ("Allocating common %s: %x at %x\n",
lgs->name,
(unsigned) size,
(unsigned) com->section->size);
}
com->value = com->section->size;
com->section->size += size;
}
}
}
}
/*
run through the input files and ensure that every input
section has somewhere to go. If one is found without
a destination then create an input request and place it
into the statement tree.
*/
static void lang_place_orphans()
{
lang_input_statement_type *file;
for (file = (lang_input_statement_type*)file_chain.head;
file != (lang_input_statement_type*)NULL;
file = (lang_input_statement_type*)file->next) {
asection *s;
for (s = file->the_bfd->sections;
s != (asection *)NULL;
s = s->next) {
if ( s->output_section == (asection *)NULL) {
/* This section of the file is not attatched, root
around for a sensible place for it to go */
if (file->common_section == s) {
/* This is a lonely common section which must
have come from an archive. We attatch to the
section with the wildcard */
wild_doit(&default_common_section->children, s,
default_common_section, file);
}
else {
lang_output_section_statement_type *os =
lang_output_section_statement_lookup(s->name);
wild_doit(&os->children, s, os, file);
}
}
}
}
}
/*
* phase_2
*
* peformed after every file has been opened and symbols read
*/
static void
lang_phase_2()
{
lang_init2();
lang_create_output_section_statements();
lang_open_input(statement_list.head, (char *)NULL,
( lang_output_section_statement_type *)NULL);
lang_place_orphans();
lang_common();
ldemul_before_allocation();
lang_size_sections(statement_list.head,
(lang_output_section_statement_type *)NULL,
&(statement_list.head), 0, (bfd_vma)0);
ldemul_after_allocation();
/* Do it once again now that we know the sizes of everything */
lang_do_assignments(statement_list.head,
(lang_output_section_statement_type *)NULL,
0, (bfd_vma)0);
lang_check();
lang_relocate_globals();
lang_finish();
}
void
lang_set_flags(ptr, flags)
lang_section_flags_type *ptr;
char *flags;
{
boolean state = true;
ptr->flag_read = false;
ptr->flag_write = false;
ptr->flag_executable = false;
ptr->flag_loadable= false;
while (*flags)
{
if (*flags == '!') {
state = false;
flags++;
}
else state = true;
switch (*flags) {
case 'R':
ptr->flag_read = state;
break;
case 'W':
ptr->flag_write = state;
break;
case 'X':
ptr->flag_executable= state;
break;
case 'L':
ptr->flag_loadable= state;
break;
default:
info("%P%F illegal syntax in flags\n");
break;
}
flags++;
}
}
void
lang_for_each_file(func)
void (*func)();
{
lang_input_statement_type *f;
for (f = (lang_input_statement_type *)file_chain.head;
f != (lang_input_statement_type *)NULL;
f = (lang_input_statement_type *)f->next)
{
func(f);
}
}
void
lang_for_each_input_section(func)
void (*func)();
{
lang_input_statement_type *f;
for (f = (lang_input_statement_type *)file_chain.head;
f != (lang_input_statement_type *)NULL;
f = (lang_input_statement_type *)f->next)
{
asection *s;
for (s = f->the_bfd->sections;
s != (asection *)NULL;
s = s->next) {
func(f->the_bfd, s);
}
}
}
void
ldlang_add_file(entry)
lang_input_statement_type *entry;
{
lang_has_input_file = true;
lang_statement_append(&file_chain,
(lang_statement_union_type *)entry,
&entry->next);
}
void
lang_add_output(name)
char *name;
{
lang_output_statement_type *new = new_stat(lang_output_statement,
stat_ptr);
new->name = name;
had_output_filename = true;
}
static lang_output_section_statement_type *current_section;
void
lang_enter_output_section_statement(output_section_statement_name,
address_exp,
block_value)
char *output_section_statement_name;
etree_type *address_exp;
bfd_vma block_value;
{
lang_output_section_statement_type *os;
current_section =
os =
lang_output_section_statement_lookup(output_section_statement_name);
/* Add this statement to tree */
/* add_statement(lang_output_section_statement_enum,
output_section_statement);*/
/* Make next things chain into subchain of this */
if (os->addr_tree ==
(etree_type *)NULL) {
os->addr_tree =
address_exp;
}
os->block_value = block_value;
stat_ptr = & os->children;
}
void
lang_final()
{
if (had_output_filename == false) {
lang_add_output("a.out");
}
}
asymbol *create_symbol(name, flags, section)
char *name;
flagword flags;
asection *section;
{
extern lang_input_statement_type *script_file;
asymbol **def_ptr = (asymbol **)ldmalloc(sizeof(asymbol **));
/* Add this definition to script file */
asymbol *def = (asymbol *)bfd_make_empty_symbol(script_file->the_bfd);
def->name = name;
def->udata = 0;
def->flags = flags;
def->section = section;
*def_ptr = def;
Q_enter_global_ref(def_ptr);
return def;
}
void
lang_process()
{
lang();
lang_phase_2();
}
/* EXPORTED TO YACC */
void
lang_section_start(name, address)
char *name;
etree_type *address;
{
lang_address_statement_type *ad =new_stat(lang_address_statement, stat_ptr);
ad->section_name = name;
ad->address = address;
}
void lang_add_entry(name)
char *name;
{
entry_symbol = name;
}
void
lang_add_target(name)
char *name;
{
lang_target_statement_type *new = new_stat(lang_target_statement,
stat_ptr);
new->target = name;
}
void
lang_add_wild(section_name, filename)
char *section_name;
char *filename;
{
lang_wild_statement_type *new = new_stat(lang_wild_statement,
stat_ptr);
if (section_name != (char *)NULL && strcmp(section_name,"COMMON") == 0)
{
placed_commons = true;
}
new->section_name = section_name;
new->filename = filename;
lang_list_init(&new->children);
}
void
lang_add_map(name)
char *name;
{
while (*name) {
switch (*name) {
case 'F':
map_option_f = true;
break;
}
name++;
}
}
void lang_add_fill(exp)
int exp;
{
lang_fill_statement_type *new = new_stat(lang_fill_statement,
stat_ptr);
new->fill = exp;
}
void lang_add_data(type, exp)
int type;
union etree_union *exp;
{
lang_data_statement_type *new = new_stat(lang_data_statement,
stat_ptr);
new->exp = exp;
new->type = type;
}
void
lang_add_assignment(exp)
etree_type *exp;
{
lang_assignment_statement_type *new = new_stat(lang_assignment_statement,
stat_ptr);
new->exp = exp;
}
void
lang_add_attribute(attribute)
enum statement_enum attribute;
{
new_statement(attribute, sizeof(lang_statement_union_type),stat_ptr);
}
void
lang_startup(name)
char *name;
{
if (startup_file != (char *)NULL) {
info("%P%FMultiple STARTUP files\n");
}
first_file->filename = name;
first_file->local_sym_name = name;
startup_file= name;
}
void
lang_float(maybe)
boolean maybe;
{
lang_float_flag = maybe;
}
void
lang_leave_output_section_statement(fill, memspec)
bfd_vma fill;
char *memspec;
{
current_section->fill = fill;
current_section->region = lang_memory_region_lookup(memspec);
stat_ptr = &statement_list;
}
/*
Create an absolute symbol with the given name with the value of the
address of first byte of the section named.
If the symbol already exists, then do nothing.
*/
void
lang_abs_symbol_at_beginning_of(section, name)
char *section;
char *name;
{
if (ldsym_undefined(name)) {
extern bfd *output_bfd;
extern asymbol *create_symbol();
asection *s = bfd_get_section_by_name(output_bfd, section);
asymbol *def = create_symbol(name,
BSF_GLOBAL | BSF_EXPORT |
BSF_ABSOLUTE,
(asection *)NULL);
if (s != (asection *)NULL) {
def->value = s->vma;
}
else {
def->value = 0;
}
}
}
/*
Create an absolute symbol with the given name with the value of the
address of the first byte after the end of the section named.
If the symbol already exists, then do nothing.
*/
void
lang_abs_symbol_at_end_of(section, name)
char *section;
char *name;
{
if (ldsym_undefined(name)){
extern bfd *output_bfd;
extern asymbol *create_symbol();
asection *s = bfd_get_section_by_name(output_bfd, section);
/* Add a symbol called _end */
asymbol *def = create_symbol(name,
BSF_GLOBAL | BSF_EXPORT |
BSF_ABSOLUTE,
(asection *)NULL);
if (s != (asection *)NULL) {
def->value = s->vma + s->size;
}
else {
def->value = 0;
}
}
}
void
lang_statement_append(list, element, field)
lang_statement_list_type *list;
lang_statement_union_type *element;
lang_statement_union_type **field;
{
*(list->tail) = element;
list->tail = field;
}
static void
lang_for_each_statement_worker(func, s)
void (*func)();
lang_statement_union_type *s;
{
for (; s != (lang_statement_union_type *)NULL ; s = s->next)
{
func(s);
switch (s->header.type) {
case lang_output_section_statement_enum:
lang_for_each_statement_worker
(func,
s->output_section_statement.children.head);
break;
case lang_wild_statement_enum:
lang_for_each_statement_worker
(func,
s->wild_statement.children.head);
break;
case lang_data_statement_enum:
case lang_object_symbols_statement_enum:
case lang_output_statement_enum:
case lang_target_statement_enum:
case lang_common_statement_enum:
case lang_input_section_enum:
case lang_input_statement_enum:
case lang_fill_statement_enum:
case lang_assignment_statement_enum:
case lang_padding_statement_enum:
case lang_address_statement_enum:
break;
default:
FAIL();
break;
}
}
}
void lang_for_each_statement(func)
void (*func)();
{
lang_for_each_statement_worker(func,
statement_list.head);
}