binutils-gdb/ld/emultempl/ppc.em
1994-02-25 03:39:17 +00:00

374 lines
11 KiB
Plaintext

# This shell script emits a C file. -*- C -*-
# It does some substitutions.
cat >em_${EMULATION_NAME}.c <<EOF
/* This file is is generated by a shell script. DO NOT EDIT! */
/* PowerPC/POWER emulation for the linker.
Copyright (C) 1994 Free Software Foundation, Inc.
Written by Ian Lance Taylor, Cygnus Support
This file is part of GLD, the Gnu Linker.
This program 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 of the License, or
(at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* This file is similar to generic.em. However, we need to build
linker stubs to handle calls to different modules. A PowerPC call
is emitted as a two instruction sequence:
bl .{function}
cror 31,31,31
When linking, if the function symbol is defined and in the same
module, we leave it alone (actually, if it is more than 26 bits
away we should treat it as though it were in a different module).
If the function symbol is not defined, we must construct a linker
stub. We reroute the call to go to the linker stub instead, and we
change the cror instruction to reload the TOC register value. The
linker stub looks like this
l r12,{function}(r2)
st r2,20(r1)
l r0,0(r12)
l r2,4(r12)
mtctr r0
bcc
Since we do not yet support dynamic linking, all function symbols
are in the same module. However, we still need to create a stub
for an undefined symbol.
This is actually probably not correct for the PowerPC ABI.
However, I do not have that ABI yet. */
#define TARGET_IS_${EMULATION_NAME}
#include "bfd.h"
#include "sysdep.h"
#include "bfdlink.h"
/* FIXME! */
#include "genlink.h"
#include "ld.h"
#include "config.h"
#include "ldmain.h"
#include "ldemul.h"
#include "ldfile.h"
#include "ldmisc.h"
#include "ldexp.h"
#include "ldlang.h"
static void gld${EMULATION_NAME}_before_parse PARAMS ((void));
static char *gld${EMULATION_NAME}_get_script PARAMS ((int *isfile));
static void gld${EMULATION_NAME}_create_output_section_statements
PARAMS ((void));
static void gld${EMULATION_NAME}_before_allocation PARAMS ((void));
static boolean gld${EMULATION_NAME}_check_symbol
PARAMS ((struct bfd_link_hash_entry *, PTR));
static void
gld${EMULATION_NAME}_before_parse()
{
ldfile_output_architecture = bfd_arch_${ARCH};
}
/* Section in which we build stubs. */
static asection *stub_sec;
/* Section in which we build TOC entries for stubs. */
static asection *stub_toc_sec;
/* This is called before the input files are opened. We create a new
fake input file to hold the stub section. */
static void
gld${EMULATION_NAME}_create_output_section_statements ()
{
lang_input_statement_type *stub_file;
stub_file = lang_add_input_file ("linker stubs",
lang_input_file_is_fake_enum,
(char *) NULL);
stub_file->the_bfd = bfd_create ("linker stubs", output_bfd);
if (stub_file->the_bfd == (bfd *) NULL
|| ! bfd_set_arch_mach (stub_file->the_bfd,
bfd_get_arch (output_bfd),
bfd_get_mach (output_bfd)))
{
einfo ("%X%P: can not create BFD: %E");
return;
}
stub_sec = bfd_make_section_old_way (stub_file->the_bfd, ".text");
stub_toc_sec = bfd_make_section_old_way (stub_file->the_bfd, ".toc");
if (stub_sec == (asection *) NULL
|| stub_toc_sec == (asection *) NULL
|| ! bfd_set_section_flags (stub_file->the_bfd, stub_sec,
(SEC_HAS_CONTENTS
| SEC_ALLOC
| SEC_LOAD
| SEC_CODE
| SEC_RELOC
| SEC_IN_MEMORY))
|| ! bfd_set_section_flags (stub_file->the_bfd, stub_toc_sec,
(SEC_HAS_CONTENTS
| SEC_ALLOC
| SEC_LOAD
| SEC_DATA
| SEC_RELOC
| SEC_IN_MEMORY)))
{
einfo ("%X%P: can not create stub sections: %E");
return;
}
ldlang_add_file (stub_file);
}
/* This is called after the input sections have been attached to the
output sections but before the output section sizes have been set.
We can identify the required stubs because they are undefined
symbols beginning with ".". For each such symbol, we build a stub
in the .text section. */
static void
gld${EMULATION_NAME}_before_allocation ()
{
bfd_link_hash_traverse (link_info.hash,
gld${EMULATION_NAME}_check_symbol,
(PTR) NULL);
}
/* If a particular symbol is undefined and starts with a ".", then we
need to make a stub for it. */
/*ARGSUSED*/
static boolean
gld${EMULATION_NAME}_check_symbol (h, info)
struct bfd_link_hash_entry *h;
PTR info;
{
bfd *stub_bfd;
bfd_byte *p;
arelent *r;
struct bfd_link_hash_entry *hnew;
asymbol *sym;
if (h->type != bfd_link_hash_undefined
|| h->root.string[0] != '.')
return true;
stub_bfd = stub_sec->owner;
/* Define this symbol to be the current location in stub_sec. */
h->type = bfd_link_hash_defined;
h->u.def.value = bfd_section_size (stub_bfd, stub_sec);
h->u.def.section = stub_sec;
/* We want to add this:
LONG (0x81820000) lwz r12,{TOC index}(r2)
LONG (0x90410014) stw r2,20(r1)
LONG (0x800c0000) lwz r0,0(r12)
LONG (0x804c0004) lwz r2,4(r12)
LONG (0x7c0903a6) mtctr r0
LONG (0x4e800420) bctr
LONG (0) Traceback table
LONG (0xc8000)
LONG (0)
*/
if (! bfd_set_section_size (stub_bfd, stub_sec,
h->u.def.value + 9 * 4))
{
einfo ("%P%X: can not set section size: %E");
return false;
}
stub_sec->contents = ((bfd_byte *)
xrealloc ((PTR) stub_sec->contents,
bfd_section_size (stub_bfd, stub_sec)));
p = stub_sec->contents + h->u.def.value;
bfd_put_32 (stub_bfd,
(bfd_vma) 0x81820000 + bfd_section_size (stub_bfd, stub_toc_sec),
p);
p += 4;
bfd_put_32 (stub_bfd, (bfd_vma) 0x90410014, p);
p += 4;
bfd_put_32 (stub_bfd, (bfd_vma) 0x800c0000, p);
p += 4;
bfd_put_32 (stub_bfd, (bfd_vma) 0x804c0004, p);
p += 4;
bfd_put_32 (stub_bfd, (bfd_vma) 0x7c0903a6, p);
p += 4;
bfd_put_32 (stub_bfd, (bfd_vma) 0x4e800420, p);
p += 4;
bfd_put_32 (stub_bfd, (bfd_vma) 0, p);
p += 4;
bfd_put_32 (stub_bfd, (bfd_vma) 0xc8000, p);
p += 4;
bfd_put_32 (stub_bfd, (bfd_vma) 0, p);
/* Add an undefined symbol for the TOC reference. This is the name
without the leading ".". */
hnew = bfd_link_hash_lookup (link_info.hash, h->root.string + 1, true,
false, true);
if (hnew == (struct bfd_link_hash_entry *) NULL)
einfo ("%P%F: bfd_link_hash_lookup failed: %E");
if (hnew->type == bfd_link_hash_new)
{
hnew->type = bfd_link_hash_undefined;
hnew->u.undef.abfd = stub_bfd;
bfd_link_add_undef (link_info.hash, hnew);
}
/* Add a relocation entry for the TOC reference in the first
instruction. */
++stub_sec->reloc_count;
stub_sec->relocation = ((arelent *)
xrealloc ((PTR) stub_sec->relocation,
(stub_sec->reloc_count
* sizeof (arelent))));
r = &stub_sec->relocation[stub_sec->reloc_count - 1];
r->sym_ptr_ptr = stub_toc_sec->symbol_ptr_ptr;
r->address = h->u.def.value;
if (stub_bfd->xvec->byteorder_big_p)
r->address += 2;
r->addend = 0;
r->howto = bfd_reloc_type_lookup (stub_bfd, BFD_RELOC_PPC_TOC16);
if (r->howto == (reloc_howto_type *) NULL)
einfo ("%P%F: no howto for TOC reference: %E");
/* Add a relocation entry in the TOC section. */
++stub_toc_sec->reloc_count;
stub_toc_sec->relocation = ((arelent *)
xrealloc ((PTR) stub_toc_sec->relocation,
(stub_toc_sec->reloc_count
* sizeof (arelent))));
r = &stub_toc_sec->relocation[stub_toc_sec->reloc_count - 1];
sym = bfd_make_empty_symbol (stub_bfd);
sym->name = hnew->root.string;
sym->value = 0;
sym->flags = BSF_NO_FLAGS;
sym->section = &bfd_und_section;
sym->udata = NULL;
/* FIXME! */
((struct generic_link_hash_entry *) hnew)->sym = sym;
r->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
*r->sym_ptr_ptr = sym;
r->address = bfd_section_size (stub_bfd, stub_toc_sec);
r->addend = 0;
r->howto = bfd_reloc_type_lookup (stub_bfd, BFD_RELOC_32);
if (r->howto == (reloc_howto_type *) NULL)
einfo ("%P%F: no howto for TOC entry: %E");
/* Add more space to the .toc section. */
if (! bfd_set_section_size (stub_bfd, stub_toc_sec,
bfd_section_size (stub_bfd, stub_toc_sec) + 4))
{
einfo ("%P%X: can not set section size: %E");
return false;
}
stub_toc_sec->contents = ((bfd_byte *)
xrealloc ((PTR) stub_toc_sec->contents,
bfd_section_size (stub_bfd,
stub_toc_sec)));
bfd_put_32 (stub_bfd, (bfd_vma) 0,
(stub_toc_sec->contents
+ bfd_section_size (stub_bfd, stub_toc_sec)
- 4));
return true;
}
static char *
gld${EMULATION_NAME}_get_script(isfile)
int *isfile;
EOF
if test -n "$COMPILE_IN"
then
# Scripts compiled in.
# sed commands to quote an ld script as a C string.
sc='s/["\\]/\\&/g
s/$/\\n\\/
1s/^/"/
$s/$/n"/
'
cat >>em_${EMULATION_NAME}.c <<EOF
{
*isfile = 0;
if (link_info.relocateable == true && config.build_constructors == true)
return `sed "$sc" ldscripts/${EMULATION_NAME}.xu`;
else if (link_info.relocateable == true)
return `sed "$sc" ldscripts/${EMULATION_NAME}.xr`;
else if (!config.text_read_only)
return `sed "$sc" ldscripts/${EMULATION_NAME}.xbn`;
else if (!config.magic_demand_paged)
return `sed "$sc" ldscripts/${EMULATION_NAME}.xn`;
else
return `sed "$sc" ldscripts/${EMULATION_NAME}.x`;
}
EOF
else
# Scripts read from the filesystem.
cat >>em_${EMULATION_NAME}.c <<EOF
{
*isfile = 1;
if (link_info.relocateable == true && config.build_constructors == true)
return "ldscripts/${EMULATION_NAME}.xu";
else if (link_info.relocateable == true)
return "ldscripts/${EMULATION_NAME}.xr";
else if (!config.text_read_only)
return "ldscripts/${EMULATION_NAME}.xbn";
else if (!config.magic_demand_paged)
return "ldscripts/${EMULATION_NAME}.xn";
else
return "ldscripts/${EMULATION_NAME}.x";
}
EOF
fi
cat >>em_${EMULATION_NAME}.c <<EOF
struct ld_emulation_xfer_struct ld_${EMULATION_NAME}_emulation =
{
gld${EMULATION_NAME}_before_parse,
syslib_default,
hll_default,
after_parse_default,
after_allocation_default,
set_output_arch_default,
ldemul_default_target,
gld${EMULATION_NAME}_before_allocation,
gld${EMULATION_NAME}_get_script,
"${EMULATION_NAME}",
"${OUTPUT_FORMAT}",
0,
gld${EMULATION_NAME}_create_output_section_statements
};
EOF