#define TARGET_PAGE_SIZE 4096
#define ZMAGIC_DISK_BLOCK_SIZE 1024
#define SEGMENT_SIZE TARGET_PAGE_SIZE
#define TEXT_START_ADDR 0x0
#define N_SHARED_LIB(x) 0
#define BYTES_IN_WORD 4
#define MACHTYPE_OK(mtype) ((mtype) == M_SPARC || (mtype) == M_UNKNOWN)
#include "bfd.h"
#include "sysdep.h"
#include "libbfd.h"
#include "aout/aout64.h"
#include "aout/stab_gnu.h"
#include "aout/ar.h"
#include "libaout.h"
#define DEFAULT_ARCH bfd_arch_sparc
#define MY(OP) CONCAT2 (sparclinux_,OP)
#define TARGETNAME "a.out-sparc-linux"
extern const bfd_target MY(vec);
static void MY_final_link_callback
PARAMS ((bfd *, file_ptr *, file_ptr *, file_ptr *));
static boolean sparclinux_bfd_final_link
PARAMS ((bfd *abfd, struct bfd_link_info *info));
static boolean
sparclinux_bfd_final_link (abfd, info)
bfd *abfd;
struct bfd_link_info *info;
{
obj_aout_subformat (abfd) = q_magic_format;
return NAME(aout,final_link) (abfd, info, MY_final_link_callback);
}
#define MY_bfd_final_link sparclinux_bfd_final_link
static boolean sparclinux_write_object_contents PARAMS ((bfd *abfd));
static boolean
sparclinux_write_object_contents (abfd)
bfd *abfd;
{
struct external_exec exec_bytes;
struct internal_exec *execp = exec_hdr (abfd);
N_SET_MACHTYPE (*execp, M_SPARC);
obj_reloc_entry_size (abfd) = RELOC_STD_SIZE;
WRITE_HEADERS(abfd, execp);
return true;
}
#define MY_write_object_contents sparclinux_write_object_contents
#ifndef GOT_REF_PREFIX
#define GOT_REF_PREFIX "__GOT_"
#endif
#define IS_GOT_SYM(name) \
(strncmp (name, GOT_REF_PREFIX, sizeof GOT_REF_PREFIX - 1) == 0)
#ifndef PLT_REF_PREFIX
#define PLT_REF_PREFIX "__PLT_"
#endif
#define IS_PLT_SYM(name) \
(strncmp (name, PLT_REF_PREFIX, sizeof PLT_REF_PREFIX - 1) == 0)
#ifndef NEEDS_SHRLIB
#define NEEDS_SHRLIB "__NEEDS_SHRLIB_"
#endif
#ifndef SHARABLE_CONFLICTS
#define SHARABLE_CONFLICTS "__SHARABLE_CONFLICTS__"
#endif
struct fixup
{
struct fixup *next;
struct linux_link_hash_entry *h;
bfd_vma value;
char jump;
char builtin;
};
struct linux_link_hash_entry
{
struct aout_link_hash_entry root;
};
struct linux_link_hash_table
{
struct aout_link_hash_table root;
bfd *dynobj;
size_t fixup_count;
size_t local_builtins;
struct fixup *fixup_list;
};
static struct bfd_hash_entry *linux_link_hash_newfunc
PARAMS ((struct bfd_hash_entry *, struct bfd_hash_table *, const char *));
static struct bfd_link_hash_table *linux_link_hash_table_create
PARAMS ((bfd *));
static struct fixup *new_fixup
PARAMS ((struct bfd_link_info *, struct linux_link_hash_entry *,
bfd_vma, int));
static boolean linux_link_create_dynamic_sections
PARAMS ((bfd *, struct bfd_link_info *));
static boolean linux_add_one_symbol
PARAMS ((struct bfd_link_info *, bfd *, const char *, flagword, asection *,
bfd_vma, const char *, boolean, boolean,
struct bfd_link_hash_entry **));
static boolean linux_tally_symbols
PARAMS ((struct linux_link_hash_entry *, PTR));
static boolean linux_finish_dynamic_link
PARAMS ((bfd *, struct bfd_link_info *));
static struct bfd_hash_entry *
linux_link_hash_newfunc (entry, table, string)
struct bfd_hash_entry *entry;
struct bfd_hash_table *table;
const char *string;
{
struct linux_link_hash_entry *ret = (struct linux_link_hash_entry *) entry;
if (ret == (struct linux_link_hash_entry *) NULL)
ret = ((struct linux_link_hash_entry *)
bfd_hash_allocate (table, sizeof (struct linux_link_hash_entry)));
if (ret == NULL)
return (struct bfd_hash_entry *) ret;
ret = ((struct linux_link_hash_entry *)
NAME(aout,link_hash_newfunc) ((struct bfd_hash_entry *) ret,
table, string));
if (ret != NULL)
{
}
return (struct bfd_hash_entry *) ret;
}
static struct bfd_link_hash_table *
linux_link_hash_table_create (abfd)
bfd *abfd;
{
struct linux_link_hash_table *ret;
bfd_size_type amt = sizeof (struct linux_link_hash_table);
ret = (struct linux_link_hash_table *) bfd_malloc (amt);
if (ret == (struct linux_link_hash_table *) NULL)
return (struct bfd_link_hash_table *) NULL;
if (! NAME(aout,link_hash_table_init) (&ret->root, abfd,
linux_link_hash_newfunc))
{
free (ret);
return (struct bfd_link_hash_table *) NULL;
}
ret->dynobj = NULL;
ret->fixup_count = 0;
ret->local_builtins = 0;
ret->fixup_list = NULL;
return &ret->root.root;
}
#define linux_link_hash_lookup(table, string, create, copy, follow) \
((struct linux_link_hash_entry *) \
aout_link_hash_lookup (&(table)->root, (string), (create), (copy),\
(follow)))
#define linux_link_hash_traverse(table, func, info) \
(aout_link_hash_traverse \
(&(table)->root, \
(boolean (*) PARAMS ((struct aout_link_hash_entry *, PTR))) (func), \
(info)))
#define linux_hash_table(p) ((struct linux_link_hash_table *) ((p)->hash))
static struct fixup *
new_fixup (info, h, value, builtin)
struct bfd_link_info *info;
struct linux_link_hash_entry *h;
bfd_vma value;
int builtin;
{
struct fixup *f;
f = (struct fixup *) bfd_hash_allocate (&info->hash->table,
sizeof (struct fixup));
if (f == NULL)
return f;
f->next = linux_hash_table (info)->fixup_list;
linux_hash_table (info)->fixup_list = f;
f->h = h;
f->value = value;
f->builtin = builtin;
f->jump = 0;
++linux_hash_table (info)->fixup_count;
return f;
}
static boolean
linux_link_create_dynamic_sections (abfd, info)
bfd *abfd;
struct bfd_link_info *info ATTRIBUTE_UNUSED;
{
flagword flags;
register asection *s;
flags = SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY;
s = bfd_make_section (abfd, ".linux-dynamic");
if (s == NULL
|| ! bfd_set_section_flags (abfd, s, flags)
|| ! bfd_set_section_alignment (abfd, s, 2))
return false;
s->_raw_size = 0;
s->contents = 0;
return true;
}
static boolean
linux_add_one_symbol (info, abfd, name, flags, section, value, string,
copy, collect, hashp)
struct bfd_link_info *info;
bfd *abfd;
const char *name;
flagword flags;
asection *section;
bfd_vma value;
const char *string;
boolean copy;
boolean collect;
struct bfd_link_hash_entry **hashp;
{
struct linux_link_hash_entry *h;
boolean insert;
insert = false;
if (! info->relocateable
&& linux_hash_table (info)->dynobj == NULL
&& strcmp (name, SHARABLE_CONFLICTS) == 0
&& (flags & BSF_CONSTRUCTOR) != 0
&& abfd->xvec == info->hash->creator)
{
if (! linux_link_create_dynamic_sections (abfd, info))
return false;
linux_hash_table (info)->dynobj = abfd;
insert = true;
}
if (bfd_is_abs_section (section)
&& abfd->xvec == info->hash->creator)
{
h = linux_link_hash_lookup (linux_hash_table (info), name, false,
false, false);
if (h != NULL
&& (h->root.root.type == bfd_link_hash_defined
|| h->root.root.type == bfd_link_hash_defweak))
{
struct fixup *f;
if (hashp != NULL)
*hashp = (struct bfd_link_hash_entry *) h;
f = new_fixup (info, h, value, ! IS_PLT_SYM (name));
if (f == NULL)
return false;
f->jump = IS_PLT_SYM (name);
return true;
}
}
if (! _bfd_generic_link_add_one_symbol (info, abfd, name, flags, section,
value, string, copy, collect,
hashp))
return false;
if (insert)
{
asection *s;
s = bfd_get_section_by_name (linux_hash_table (info)->dynobj,
".linux-dynamic");
BFD_ASSERT (s != NULL);
if (! (_bfd_generic_link_add_one_symbol
(info, linux_hash_table (info)->dynobj, SHARABLE_CONFLICTS,
BSF_GLOBAL | BSF_CONSTRUCTOR, s, (bfd_vma) 0, NULL,
false, false, NULL)))
return false;
}
return true;
}
static boolean
linux_tally_symbols (h, data)
struct linux_link_hash_entry *h;
PTR data;
{
struct bfd_link_info *info = (struct bfd_link_info *) data;
struct fixup *f, *f1;
int is_plt;
struct linux_link_hash_entry *h1, *h2;
boolean exists;
if (h->root.root.type == bfd_link_hash_warning)
h = (struct linux_link_hash_entry *) h->root.root.u.i.link;
if (h->root.root.type == bfd_link_hash_undefined
&& strncmp (h->root.root.root.string, NEEDS_SHRLIB,
sizeof NEEDS_SHRLIB - 1) == 0)
{
const char *name;
char *p;
char *alloc = NULL;
name = h->root.root.root.string + sizeof NEEDS_SHRLIB - 1;
p = strrchr (name, '_');
if (p != NULL)
alloc = (char *) bfd_malloc ((bfd_size_type) strlen (name) + 1);
if (p == NULL || alloc == NULL)
(*_bfd_error_handler) (_("Output file requires shared library `%s'\n"),
name);
else
{
strcpy (alloc, name);
p = strrchr (alloc, '_');
*p++ = '\0';
(*_bfd_error_handler)
(_("Output file requires shared library `%s.so.%s'\n"),
alloc, p);
free (alloc);
}
abort ();
}
is_plt = IS_PLT_SYM (h->root.root.root.string);
if (is_plt || IS_GOT_SYM (h->root.root.root.string))
{
h1 = linux_link_hash_lookup (linux_hash_table (info),
(h->root.root.root.string
+ sizeof PLT_REF_PREFIX - 1),
false, false, true);
h2 = linux_link_hash_lookup (linux_hash_table (info),
(h->root.root.root.string
+ sizeof PLT_REF_PREFIX - 1),
false, false, false);
if (h1 != NULL
&& (((h1->root.root.type == bfd_link_hash_defined
|| h1->root.root.type == bfd_link_hash_defweak)
&& ! bfd_is_abs_section (h1->root.root.u.def.section))
|| h2->root.root.type == bfd_link_hash_indirect))
{
exists = false;
for (f1 = linux_hash_table (info)->fixup_list;
f1 != NULL;
f1 = f1->next)
{
if ((f1->h != h && f1->h != h1)
|| (! f1->builtin && ! f1->jump))
continue;
if (f1->h == h1)
exists = true;
if (! exists
&& bfd_is_abs_section (h->root.root.u.def.section))
{
f = new_fixup (info, h1, f1->h->root.root.u.def.value, 0);
f->jump = is_plt;
}
f1->h = h1;
f1->jump = is_plt;
f1->builtin = 0;
exists = true;
}
if (! exists
&& bfd_is_abs_section (h->root.root.u.def.section))
{
f = new_fixup (info, h1, h->root.root.u.def.value, 0);
if (f == NULL)
{
abort ();
}
f->jump = is_plt;
}
}
if (bfd_is_abs_section (h->root.root.u.def.section))
h->root.written = true;
}
return true;
}
boolean
bfd_sparclinux_size_dynamic_sections (output_bfd, info)
bfd *output_bfd;
struct bfd_link_info *info;
{
struct fixup *f;
asection *s;
if (output_bfd->xvec != &MY(vec))
return true;
linux_link_hash_traverse (linux_hash_table (info),
linux_tally_symbols,
(PTR) info);
for (f = linux_hash_table (info)->fixup_list; f != NULL; f = f->next)
{
if (f->builtin)
{
++linux_hash_table (info)->fixup_count;
++linux_hash_table (info)->local_builtins;
break;
}
}
if (linux_hash_table (info)->dynobj == NULL)
{
if (linux_hash_table (info)->fixup_count > 0)
abort ();
return true;
}
s = bfd_get_section_by_name (linux_hash_table (info)->dynobj,
".linux-dynamic");
if (s != NULL)
{
s->_raw_size = linux_hash_table (info)->fixup_count + 1;
s->_raw_size *= 8;
s->contents = (bfd_byte *) bfd_alloc (output_bfd, s->_raw_size);
if (s->contents == NULL)
return false;
memset (s->contents, 0, (size_t) s->_raw_size);
}
return true;
}
static boolean
linux_finish_dynamic_link (output_bfd, info)
bfd *output_bfd;
struct bfd_link_info *info;
{
asection *s, *os, *is;
bfd_byte *fixup_table;
struct linux_link_hash_entry *h;
struct fixup *f;
unsigned int new_addr;
int section_offset;
unsigned int fixups_written;
if (linux_hash_table (info)->dynobj == NULL)
return true;
s = bfd_get_section_by_name (linux_hash_table (info)->dynobj,
".linux-dynamic");
BFD_ASSERT (s != NULL);
os = s->output_section;
fixups_written = 0;
#ifdef LINUX_LINK_DEBUG
printf ("Fixup table file offset: %x VMA: %x\n",
os->filepos + s->output_offset,
os->vma + s->output_offset);
#endif
fixup_table = s->contents;
bfd_put_32 (output_bfd,
(bfd_vma) linux_hash_table (info)->fixup_count, fixup_table);
fixup_table += 4;
for (f = linux_hash_table (info)->fixup_list; f != NULL; f = f->next)
{
if (f->builtin)
continue;
if (f->h->root.root.type != bfd_link_hash_defined
&& f->h->root.root.type != bfd_link_hash_defweak)
{
(*_bfd_error_handler)
(_("Symbol %s not defined for fixups\n"),
f->h->root.root.root.string);
continue;
}
is = f->h->root.root.u.def.section;
section_offset = is->output_section->vma + is->output_offset;
new_addr = f->h->root.root.u.def.value + section_offset;
#ifdef LINUX_LINK_DEBUG
printf ("Fixup(%d) %s: %x %x\n",f->jump, f->h->root.root.string,
new_addr, f->value);
#endif
if (f->jump)
{
new_addr = new_addr - (f->value + 5);
bfd_put_32 (output_bfd, (bfd_vma) new_addr, fixup_table);
fixup_table += 4;
bfd_put_32 (output_bfd, f->value + 1, fixup_table);
fixup_table += 4;
}
else
{
bfd_put_32 (output_bfd, (bfd_vma) new_addr, fixup_table);
fixup_table += 4;
bfd_put_32 (output_bfd, f->value, fixup_table);
fixup_table += 4;
}
++fixups_written;
}
if (linux_hash_table (info)->local_builtins != 0)
{
bfd_put_32 (output_bfd, (bfd_vma) 0, fixup_table);
fixup_table += 4;
bfd_put_32 (output_bfd, (bfd_vma) 0, fixup_table);
fixup_table += 4;
++fixups_written;
for (f = linux_hash_table (info)->fixup_list; f != NULL; f = f->next)
{
if (! f->builtin)
continue;
if (f->h->root.root.type != bfd_link_hash_defined
&& f->h->root.root.type != bfd_link_hash_defweak)
{
(*_bfd_error_handler)
(_("Symbol %s not defined for fixups\n"),
f->h->root.root.root.string);
continue;
}
is = f->h->root.root.u.def.section;
section_offset = is->output_section->vma + is->output_offset;
new_addr = f->h->root.root.u.def.value + section_offset;
#ifdef LINUX_LINK_DEBUG
printf ("Fixup(B) %s: %x %x\n", f->h->root.root.string,
new_addr, f->value);
#endif
bfd_put_32 (output_bfd, (bfd_vma) new_addr, fixup_table);
fixup_table += 4;
bfd_put_32 (output_bfd, f->value, fixup_table);
fixup_table += 4;
++fixups_written;
}
}
if (linux_hash_table (info)->fixup_count != fixups_written)
{
(*_bfd_error_handler) (_("Warning: fixup count mismatch\n"));
while (linux_hash_table (info)->fixup_count > fixups_written)
{
bfd_put_32 (output_bfd, (bfd_vma) 0, fixup_table);
fixup_table += 4;
bfd_put_32 (output_bfd, (bfd_vma) 0, fixup_table);
fixup_table += 4;
++fixups_written;
}
}
h = linux_link_hash_lookup (linux_hash_table (info),
"__BUILTIN_FIXUPS__",
false, false, false);
if (h != NULL
&& (h->root.root.type == bfd_link_hash_defined
|| h->root.root.type == bfd_link_hash_defweak))
{
is = h->root.root.u.def.section;
section_offset = is->output_section->vma + is->output_offset;
new_addr = h->root.root.u.def.value + section_offset;
#ifdef LINUX_LINK_DEBUG
printf ("Builtin fixup table at %x\n", new_addr);
#endif
bfd_put_32 (output_bfd, (bfd_vma) new_addr, fixup_table);
}
else
bfd_put_32 (output_bfd, (bfd_vma) 0, fixup_table);
if (bfd_seek (output_bfd, (file_ptr) (os->filepos + s->output_offset),
SEEK_SET) != 0)
return false;
if (bfd_bwrite ((PTR) s->contents, s->_raw_size, output_bfd)
!= s->_raw_size)
return false;
return true;
}
#define MY_bfd_link_hash_table_create linux_link_hash_table_create
#define MY_add_one_symbol linux_add_one_symbol
#define MY_finish_dynamic_link linux_finish_dynamic_link
#define MY_zmagic_contiguous 1
#include "aout-target.h"