/* Intel 80386/80486-specific support for 32-bit ELF
Copyright 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
- 2003, 2004, 2005 Free Software Foundation, Inc.
+ 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
This file is part of BFD, the Binary File Descriptor library.
#include "bfdlink.h"
#include "libbfd.h"
#include "elf-bfd.h"
+#include "elf-vxworks.h"
/* 386 uses REL relocations instead of RELA. */
#define USE_REL 1
HOWTO(R_386_TLS_TPOFF32, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
bfd_elf_generic_reloc, "R_386_TLS_TPOFF32",
TRUE, 0xffffffff, 0xffffffff, FALSE),
+ EMPTY_HOWTO (38),
+ HOWTO(R_386_TLS_GOTDESC, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_386_TLS_GOTDESC",
+ TRUE, 0xffffffff, 0xffffffff, FALSE),
+ HOWTO(R_386_TLS_DESC_CALL, 0, 0, 0, FALSE, 0, complain_overflow_dont,
+ bfd_elf_generic_reloc, "R_386_TLS_DESC_CALL",
+ FALSE, 0, 0, FALSE),
+ HOWTO(R_386_TLS_DESC, 0, 2, 32, FALSE, 0, complain_overflow_bitfield,
+ bfd_elf_generic_reloc, "R_386_TLS_DESC",
+ TRUE, 0xffffffff, 0xffffffff, FALSE),
/* Another gap. */
-#define R_386_tls (R_386_TLS_TPOFF32 + 1 - R_386_tls_offset)
+#define R_386_tls (R_386_TLS_DESC + 1 - R_386_tls_offset)
#define R_386_vt_offset (R_386_GNU_VTINHERIT - R_386_tls)
/* GNU extension to record C++ vtable hierarchy. */
TRACE ("BFD_RELOC_386_TLS_TPOFF32");
return &elf_howto_table[R_386_TLS_TPOFF32 - R_386_tls_offset];
+ case BFD_RELOC_386_TLS_GOTDESC:
+ TRACE ("BFD_RELOC_386_TLS_GOTDESC");
+ return &elf_howto_table[R_386_TLS_GOTDESC - R_386_tls_offset];
+
+ case BFD_RELOC_386_TLS_DESC_CALL:
+ TRACE ("BFD_RELOC_386_TLS_DESC_CALL");
+ return &elf_howto_table[R_386_TLS_DESC_CALL - R_386_tls_offset];
+
+ case BFD_RELOC_386_TLS_DESC:
+ TRACE ("BFD_RELOC_386_TLS_DESC");
+ return &elf_howto_table[R_386_TLS_DESC - R_386_tls_offset];
+
case BFD_RELOC_VTABLE_INHERIT:
TRACE ("BFD_RELOC_VTABLE_INHERIT");
return &elf_howto_table[R_386_GNU_VTINHERIT - R_386_vt_offset];
#define PLT_ENTRY_SIZE 16
/* The first entry in an absolute procedure linkage table looks like
- this. See the SVR4 ABI i386 supplement to see how this works. */
+ this. See the SVR4 ABI i386 supplement to see how this works.
+ Will be padded to PLT_ENTRY_SIZE with htab->plt0_pad_byte. */
-static const bfd_byte elf_i386_plt0_entry[PLT_ENTRY_SIZE] =
+static const bfd_byte elf_i386_plt0_entry[12] =
{
0xff, 0x35, /* pushl contents of address */
0, 0, 0, 0, /* replaced with address of .got + 4. */
0xff, 0x25, /* jmp indirect */
- 0, 0, 0, 0, /* replaced with address of .got + 8. */
- 0, 0, 0, 0 /* pad out to 16 bytes. */
+ 0, 0, 0, 0 /* replaced with address of .got + 8. */
};
/* Subsequent entries in an absolute procedure linkage table look like
0, 0, 0, 0 /* replaced with offset to start of .plt. */
};
-/* The first entry in a PIC procedure linkage table look like this. */
+/* The first entry in a PIC procedure linkage table look like this.
+ Will be padded to PLT_ENTRY_SIZE with htab->plt0_pad_byte. */
-static const bfd_byte elf_i386_pic_plt0_entry[PLT_ENTRY_SIZE] =
+static const bfd_byte elf_i386_pic_plt0_entry[12] =
{
0xff, 0xb3, 4, 0, 0, 0, /* pushl 4(%ebx) */
- 0xff, 0xa3, 8, 0, 0, 0, /* jmp *8(%ebx) */
- 0, 0, 0, 0 /* pad out to 16 bytes. */
+ 0xff, 0xa3, 8, 0, 0, 0 /* jmp *8(%ebx) */
};
/* Subsequent entries in a PIC procedure linkage table look like this. */
0, 0, 0, 0 /* replaced with offset to start of .plt. */
};
+/* On VxWorks, the .rel.plt.unloaded section has absolute relocations
+ for the PLTResolve stub and then for each PLT entry. */
+#define PLTRESOLVE_RELOCS_SHLIB 0
+#define PLTRESOLVE_RELOCS 2
+#define PLT_NON_JUMP_SLOT_RELOCS 2
+
/* The i386 linker needs to keep track of the number of relocs that it
decides to copy as dynamic relocs in check_relocs for each symbol.
This is so that it can later discard them if they are found to be
#define GOT_TLS_IE_POS 5
#define GOT_TLS_IE_NEG 6
#define GOT_TLS_IE_BOTH 7
+#define GOT_TLS_GDESC 8
+#define GOT_TLS_GD_BOTH_P(type) \
+ ((type) == (GOT_TLS_GD | GOT_TLS_GDESC))
+#define GOT_TLS_GD_P(type) \
+ ((type) == GOT_TLS_GD || GOT_TLS_GD_BOTH_P (type))
+#define GOT_TLS_GDESC_P(type) \
+ ((type) == GOT_TLS_GDESC || GOT_TLS_GD_BOTH_P (type))
+#define GOT_TLS_GD_ANY_P(type) \
+ (GOT_TLS_GD_P (type) || GOT_TLS_GDESC_P (type))
unsigned char tls_type;
+
+ /* Offset of the GOTPLT entry reserved for the TLS descriptor,
+ starting at the end of the jump table. */
+ bfd_vma tlsdesc_got;
};
#define elf_i386_hash_entry(ent) ((struct elf_i386_link_hash_entry *)(ent))
/* tls_type for each local got entry. */
char *local_got_tls_type;
+
+ /* GOTPLT entries for TLS descriptors. */
+ bfd_vma *local_tlsdesc_gotent;
};
#define elf_i386_tdata(abfd) \
#define elf_i386_local_got_tls_type(abfd) \
(elf_i386_tdata (abfd)->local_got_tls_type)
+#define elf_i386_local_tlsdesc_gotent(abfd) \
+ (elf_i386_tdata (abfd)->local_tlsdesc_gotent)
+
static bfd_boolean
elf_i386_mkobject (bfd *abfd)
{
asection *sdynbss;
asection *srelbss;
+ /* The (unloaded but important) .rel.plt.unloaded section on VxWorks. */
+ asection *srelplt2;
+
+ /* True if the target system is VxWorks. */
+ int is_vxworks;
+
+ /* Value used to fill the last word of the first plt entry. */
+ bfd_byte plt0_pad_byte;
+
union {
bfd_signed_vma refcount;
bfd_vma offset;
} tls_ldm_got;
+ /* The amount of space used by the reserved portion of the sgotplt
+ section, plus whatever space is used by the jump slots. */
+ bfd_vma sgotplt_jump_table_size;
+
/* Small local sym to section mapping cache. */
struct sym_sec_cache sym_sec;
};
#define elf_i386_hash_table(p) \
((struct elf_i386_link_hash_table *) ((p)->hash))
+#define elf_i386_compute_jump_table_size(htab) \
+ ((htab)->srelplt->reloc_count * 4)
+
/* Create an entry in an i386 ELF linker hash table. */
static struct bfd_hash_entry *
eh = (struct elf_i386_link_hash_entry *) entry;
eh->dyn_relocs = NULL;
eh->tls_type = GOT_UNKNOWN;
+ eh->tlsdesc_got = (bfd_vma) -1;
}
return entry;
ret->sdynbss = NULL;
ret->srelbss = NULL;
ret->tls_ldm_got.refcount = 0;
+ ret->sgotplt_jump_table_size = 0;
ret->sym_sec.abfd = NULL;
+ ret->is_vxworks = 0;
+ ret->srelplt2 = NULL;
+ ret->plt0_pad_byte = 0;
return &ret->elf.root;
}
elf_i386_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info)
{
struct elf_i386_link_hash_table *htab;
+ asection * s;
+ int flags;
+ const struct elf_backend_data *bed = get_elf_backend_data (dynobj);
htab = elf_i386_hash_table (info);
if (!htab->sgot && !create_got_section (dynobj, info))
|| (!info->shared && !htab->srelbss))
abort ();
+ if (htab->is_vxworks && !info->shared)
+ {
+ s = bfd_make_section (dynobj, ".rel.plt.unloaded");
+ flags = (SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_READONLY
+ | SEC_LINKER_CREATED);
+ if (s == NULL
+ || ! bfd_set_section_flags (dynobj, s, flags)
+ || ! bfd_set_section_alignment (dynobj, s, bed->s->log_file_align))
+ return FALSE;
+ htab->srelplt2 = s;
+ }
+
return TRUE;
}
/* Copy the extra info we tack onto an elf_link_hash_entry. */
static void
-elf_i386_copy_indirect_symbol (const struct elf_backend_data *bed,
+elf_i386_copy_indirect_symbol (struct bfd_link_info *info,
struct elf_link_hash_entry *dir,
struct elf_link_hash_entry *ind)
{
struct elf_i386_dyn_relocs **pp;
struct elf_i386_dyn_relocs *p;
- if (ind->root.type == bfd_link_hash_indirect)
- abort ();
-
- /* Add reloc counts against the weak sym to the strong sym
+ /* Add reloc counts against the indirect sym to the direct sym
list. Merge any entries against the same section. */
for (pp = &eind->dyn_relocs; (p = *pp) != NULL; )
{
dir->pointer_equality_needed |= ind->pointer_equality_needed;
}
else
- _bfd_elf_link_hash_copy_indirect (bed, dir, ind);
+ _bfd_elf_link_hash_copy_indirect (info, dir, ind);
}
static int
switch (r_type)
{
case R_386_TLS_GD:
+ case R_386_TLS_GOTDESC:
+ case R_386_TLS_DESC_CALL:
case R_386_TLS_IE_32:
if (is_local)
return R_386_TLS_LE_32;
if (r_symndx < symtab_hdr->sh_info)
h = NULL;
else
- h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+ {
+ h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ }
r_type = elf_i386_tls_transition (info, r_type, h == NULL);
case R_386_GOT32:
case R_386_TLS_GD:
+ case R_386_TLS_GOTDESC:
+ case R_386_TLS_DESC_CALL:
/* This symbol requires a global offset table entry. */
{
int tls_type, old_tls_type;
default:
case R_386_GOT32: tls_type = GOT_NORMAL; break;
case R_386_TLS_GD: tls_type = GOT_TLS_GD; break;
+ case R_386_TLS_GOTDESC:
+ case R_386_TLS_DESC_CALL:
+ tls_type = GOT_TLS_GDESC; break;
case R_386_TLS_IE_32:
if (ELF32_R_TYPE (rel->r_info) == r_type)
tls_type = GOT_TLS_IE_NEG;
bfd_size_type size;
size = symtab_hdr->sh_info;
- size *= (sizeof (bfd_signed_vma) + sizeof(char));
+ size *= (sizeof (bfd_signed_vma)
+ + sizeof (bfd_vma) + sizeof(char));
local_got_refcounts = bfd_zalloc (abfd, size);
if (local_got_refcounts == NULL)
return FALSE;
elf_local_got_refcounts (abfd) = local_got_refcounts;
+ elf_i386_local_tlsdesc_gotent (abfd)
+ = (bfd_vma *) (local_got_refcounts + symtab_hdr->sh_info);
elf_i386_local_got_tls_type (abfd)
- = (char *) (local_got_refcounts + symtab_hdr->sh_info);
+ = (char *) (local_got_refcounts + 2 * symtab_hdr->sh_info);
}
local_got_refcounts[r_symndx] += 1;
old_tls_type = elf_i386_local_got_tls_type (abfd) [r_symndx];
/* If a TLS symbol is accessed using IE at least once,
there is no point to use dynamic model for it. */
else if (old_tls_type != tls_type && old_tls_type != GOT_UNKNOWN
- && (old_tls_type != GOT_TLS_GD
+ && (! GOT_TLS_GD_ANY_P (old_tls_type)
|| (tls_type & GOT_TLS_IE) == 0))
{
- if ((old_tls_type & GOT_TLS_IE) && tls_type == GOT_TLS_GD)
+ if ((old_tls_type & GOT_TLS_IE) && GOT_TLS_GD_ANY_P (tls_type))
tls_type = old_tls_type;
+ else if (GOT_TLS_GD_ANY_P (old_tls_type)
+ && GOT_TLS_GD_ANY_P (tls_type))
+ tls_type |= old_tls_type;
else
{
(*_bfd_error_handler)
}
else
{
+ void **vpp;
/* Track dynamic relocs needed for local syms too.
We really need local syms available to do this
easily. Oh well. */
if (s == NULL)
return FALSE;
- head = ((struct elf_i386_dyn_relocs **)
- &elf_section_data (s)->local_dynrel);
+ vpp = &elf_section_data (s)->local_dynrel;
+ head = (struct elf_i386_dyn_relocs **)vpp;
}
p = *head;
break;
case R_386_TLS_GD:
+ case R_386_TLS_GOTDESC:
+ case R_386_TLS_DESC_CALL:
case R_386_TLS_IE_32:
case R_386_TLS_IE:
case R_386_TLS_GOTIE:
return TRUE;
}
- if (ELIMINATE_COPY_RELOCS)
+ htab = elf_i386_hash_table (info);
+
+ /* If there aren't any dynamic relocs in read-only sections, then
+ we can keep the dynamic relocs and avoid the copy reloc. This
+ doesn't work on VxWorks, where we can not have dynamic relocations
+ (other than copy and jump slot relocations) in an executable. */
+ if (ELIMINATE_COPY_RELOCS && !htab->is_vxworks)
{
struct elf_i386_link_hash_entry * eh;
struct elf_i386_dyn_relocs *p;
break;
}
- /* If we didn't find any dynamic relocs in read-only sections, then
- we'll be keeping the dynamic relocs and avoiding the copy reloc. */
if (p == NULL)
{
h->non_got_ref = 0;
}
}
+ if (h->size == 0)
+ {
+ (*_bfd_error_handler) (_("dynamic variable `%s' is zero size"),
+ h->root.root.string);
+ return TRUE;
+ }
+
/* We must allocate the symbol in our .dynbss section, which will
become part of the .bss section of the executable. There will be
an entry for this symbol in the .dynsym section. The dynamic
both the dynamic object and the regular object will refer to the
same memory location for the variable. */
- htab = elf_i386_hash_table (info);
-
/* We must generate a R_386_COPY reloc to tell the dynamic linker to
copy the initial value out of the dynamic object and into the
runtime process image. */
/* We also need to make an entry in the .rel.plt section. */
htab->srelplt->size += sizeof (Elf32_External_Rel);
+ htab->srelplt->reloc_count++;
+
+ if (htab->is_vxworks && !info->shared)
+ {
+ /* VxWorks has a second set of relocations for each PLT entry
+ in executables. They go in a separate relocation section,
+ which is processed by the kernel loader. */
+
+ /* There are two relocations for the initial PLT entry: an
+ R_386_32 relocation for _GLOBAL_OFFSET_TABLE_ + 4 and an
+ R_386_32 relocation for _GLOBAL_OFFSET_TABLE_ + 8. */
+
+ if (h->plt.offset == PLT_ENTRY_SIZE)
+ htab->srelplt2->size += (sizeof (Elf32_External_Rel) * 2);
+
+ /* There are two extra relocations for each subsequent PLT entry:
+ an R_386_32 relocation for the GOT entry, and an R_386_32
+ relocation for the PLT entry. */
+
+ htab->srelplt2->size += (sizeof (Elf32_External_Rel) * 2);
+ }
}
else
{
h->needs_plt = 0;
}
+ eh = (struct elf_i386_link_hash_entry *) h;
+ eh->tlsdesc_got = (bfd_vma) -1;
+
/* If R_386_TLS_{IE_32,IE,GOTIE} symbol is now local to the binary,
make it a R_386_TLS_LE_32 requiring no TLS entry. */
if (h->got.refcount > 0
}
s = htab->sgot;
- h->got.offset = s->size;
- s->size += 4;
- /* R_386_TLS_GD needs 2 consecutive GOT slots. */
- if (tls_type == GOT_TLS_GD || tls_type == GOT_TLS_IE_BOTH)
- s->size += 4;
+ if (GOT_TLS_GDESC_P (tls_type))
+ {
+ eh->tlsdesc_got = htab->sgotplt->size
+ - elf_i386_compute_jump_table_size (htab);
+ htab->sgotplt->size += 8;
+ h->got.offset = (bfd_vma) -2;
+ }
+ if (! GOT_TLS_GDESC_P (tls_type)
+ || GOT_TLS_GD_P (tls_type))
+ {
+ h->got.offset = s->size;
+ s->size += 4;
+ /* R_386_TLS_GD needs 2 consecutive GOT slots. */
+ if (GOT_TLS_GD_P (tls_type) || tls_type == GOT_TLS_IE_BOTH)
+ s->size += 4;
+ }
dyn = htab->elf.dynamic_sections_created;
/* R_386_TLS_IE_32 needs one dynamic relocation,
R_386_TLS_IE resp. R_386_TLS_GOTIE needs one dynamic relocation,
global. */
if (tls_type == GOT_TLS_IE_BOTH)
htab->srelgot->size += 2 * sizeof (Elf32_External_Rel);
- else if ((tls_type == GOT_TLS_GD && h->dynindx == -1)
+ else if ((GOT_TLS_GD_P (tls_type) && h->dynindx == -1)
|| (tls_type & GOT_TLS_IE))
htab->srelgot->size += sizeof (Elf32_External_Rel);
- else if (tls_type == GOT_TLS_GD)
+ else if (GOT_TLS_GD_P (tls_type))
htab->srelgot->size += 2 * sizeof (Elf32_External_Rel);
- else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
- || h->root.type != bfd_link_hash_undefweak)
+ else if (! GOT_TLS_GDESC_P (tls_type)
+ && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT
+ || h->root.type != bfd_link_hash_undefweak)
&& (info->shared
|| WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h)))
htab->srelgot->size += sizeof (Elf32_External_Rel);
+ if (GOT_TLS_GDESC_P (tls_type))
+ htab->srelplt->size += sizeof (Elf32_External_Rel);
}
else
h->got.offset = (bfd_vma) -1;
- eh = (struct elf_i386_link_hash_entry *) h;
if (eh->dyn_relocs == NULL)
return TRUE;
/* Also discard relocs on undefined weak syms with non-default
visibility. */
- if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
+ if (eh->dyn_relocs != NULL
&& h->root.type == bfd_link_hash_undefweak)
- eh->dyn_relocs = NULL;
+ {
+ if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT)
+ eh->dyn_relocs = NULL;
+
+ /* Make sure undefined weak symbols are output as a dynamic
+ symbol in PIEs. */
+ else if (h->dynindx == -1
+ && !h->forced_local)
+ {
+ if (! bfd_elf_link_record_dynamic_symbol (info, h))
+ return FALSE;
+ }
+ }
}
else if (ELIMINATE_COPY_RELOCS)
{
bfd_signed_vma *local_got;
bfd_signed_vma *end_local_got;
char *local_tls_type;
+ bfd_vma *local_tlsdesc_gotent;
bfd_size_type locsymcount;
Elf_Internal_Shdr *symtab_hdr;
asection *srel;
{
struct elf_i386_dyn_relocs *p;
- for (p = *((struct elf_i386_dyn_relocs **)
- &elf_section_data (s)->local_dynrel);
+ for (p = ((struct elf_i386_dyn_relocs *)
+ elf_section_data (s)->local_dynrel);
p != NULL;
p = p->next)
{
locsymcount = symtab_hdr->sh_info;
end_local_got = local_got + locsymcount;
local_tls_type = elf_i386_local_got_tls_type (ibfd);
+ local_tlsdesc_gotent = elf_i386_local_tlsdesc_gotent (ibfd);
s = htab->sgot;
srel = htab->srelgot;
- for (; local_got < end_local_got; ++local_got, ++local_tls_type)
+ for (; local_got < end_local_got;
+ ++local_got, ++local_tls_type, ++local_tlsdesc_gotent)
{
+ *local_tlsdesc_gotent = (bfd_vma) -1;
if (*local_got > 0)
{
- *local_got = s->size;
- s->size += 4;
- if (*local_tls_type == GOT_TLS_GD
- || *local_tls_type == GOT_TLS_IE_BOTH)
- s->size += 4;
+ if (GOT_TLS_GDESC_P (*local_tls_type))
+ {
+ *local_tlsdesc_gotent = htab->sgotplt->size
+ - elf_i386_compute_jump_table_size (htab);
+ htab->sgotplt->size += 8;
+ *local_got = (bfd_vma) -2;
+ }
+ if (! GOT_TLS_GDESC_P (*local_tls_type)
+ || GOT_TLS_GD_P (*local_tls_type))
+ {
+ *local_got = s->size;
+ s->size += 4;
+ if (GOT_TLS_GD_P (*local_tls_type)
+ || *local_tls_type == GOT_TLS_IE_BOTH)
+ s->size += 4;
+ }
if (info->shared
- || *local_tls_type == GOT_TLS_GD
+ || GOT_TLS_GD_ANY_P (*local_tls_type)
|| (*local_tls_type & GOT_TLS_IE))
{
if (*local_tls_type == GOT_TLS_IE_BOTH)
srel->size += 2 * sizeof (Elf32_External_Rel);
- else
+ else if (GOT_TLS_GD_P (*local_tls_type)
+ || ! GOT_TLS_GDESC_P (*local_tls_type))
srel->size += sizeof (Elf32_External_Rel);
+ if (GOT_TLS_GDESC_P (*local_tls_type))
+ htab->srelplt->size += sizeof (Elf32_External_Rel);
}
}
else
else
htab->tls_ldm_got.offset = -1;
+ if (htab->is_vxworks)
+ {
+ /* Mark the GOT and PLT symbols as having relocations; they might
+ not, but we won't know for sure until we build the GOT in
+ finish_dynamic_symbol. */
+ if (htab->elf.hgot)
+ htab->elf.hgot->indx = -2;
+ if (htab->elf.hplt)
+ {
+ htab->elf.hplt->indx = -2;
+ if (htab->splt->flags & SEC_CODE)
+ htab->elf.hplt->type = STT_FUNC;
+ }
+ }
+
/* Allocate global sym .plt and .got entries, and space for global
sym dynamic relocs. */
elf_link_hash_traverse (&htab->elf, allocate_dynrelocs, (PTR) info);
+ /* For every jump slot reserved in the sgotplt, reloc_count is
+ incremented. However, when we reserve space for TLS descriptors,
+ it's not incremented, so in order to compute the space reserved
+ for them, it suffices to multiply the reloc count by the jump
+ slot size. */
+ if (htab->srelplt)
+ htab->sgotplt_jump_table_size = htab->srelplt->reloc_count * 4;
+
/* We now have determined the sizes of the various dynamic sections.
Allocate memory for them. */
relocs = FALSE;
for (s = dynobj->sections; s != NULL; s = s->next)
{
+ bfd_boolean strip_section = TRUE;
+
if ((s->flags & SEC_LINKER_CREATED) == 0)
continue;
if (s == htab->splt
|| s == htab->sgot
- || s == htab->sgotplt)
+ || s == htab->sgotplt
+ || s == htab->sdynbss)
{
/* Strip this section if we don't need it; see the
comment below. */
+ /* We'd like to strip these sections if they aren't needed, but if
+ we've exported dynamic symbols from them we must leave them.
+ It's too late to tell BFD to get rid of the symbols. */
+
+ if (htab->elf.hplt != NULL)
+ strip_section = FALSE;
}
else if (strncmp (bfd_get_section_name (dynobj, s), ".rel", 4) == 0)
{
- if (s->size != 0 && s != htab->srelplt)
+ if (s->size != 0 && s != htab->srelplt && s != htab->srelplt2)
relocs = TRUE;
/* We use the reloc_count field as a counter if we need
to copy relocs into the output file. */
- s->reloc_count = 0;
+ if (s != htab->srelplt)
+ s->reloc_count = 0;
}
else
{
adjust_dynamic_symbol is called, and it is that
function which decides whether anything needs to go
into these sections. */
-
- s->flags |= SEC_EXCLUDE;
+ if (strip_section)
+ s->flags |= SEC_EXCLUDE;
continue;
}
+ if ((s->flags & SEC_HAS_CONTENTS) == 0)
+ continue;
+
/* Allocate memory for the section contents. We use bfd_zalloc
here in case unused entries are not reclaimed before the
section's contents are written out. This should not happen,
return TRUE;
}
+static bfd_boolean
+elf_i386_always_size_sections (bfd *output_bfd,
+ struct bfd_link_info *info)
+{
+ asection *tls_sec = elf_hash_table (info)->tls_sec;
+
+ if (tls_sec)
+ {
+ struct elf_link_hash_entry *tlsbase;
+
+ tlsbase = elf_link_hash_lookup (elf_hash_table (info),
+ "_TLS_MODULE_BASE_",
+ FALSE, FALSE, FALSE);
+
+ if (tlsbase && tlsbase->type == STT_TLS)
+ {
+ struct bfd_link_hash_entry *bh = NULL;
+ const struct elf_backend_data *bed
+ = get_elf_backend_data (output_bfd);
+
+ if (!(_bfd_generic_link_add_one_symbol
+ (info, output_bfd, "_TLS_MODULE_BASE_", BSF_LOCAL,
+ tls_sec, 0, NULL, FALSE,
+ bed->collect, &bh)))
+ return FALSE;
+ tlsbase = (struct elf_link_hash_entry *)bh;
+ tlsbase->def_regular = 1;
+ tlsbase->other = STV_HIDDEN;
+ (*bed->elf_backend_hide_symbol) (info, tlsbase, TRUE);
+ }
+ }
+
+ return TRUE;
+}
+
/* Set the correct type for an x86 ELF section. We do this by the
section name, which is a hack, but ought to work. */
Elf_Internal_Shdr *symtab_hdr;
struct elf_link_hash_entry **sym_hashes;
bfd_vma *local_got_offsets;
+ bfd_vma *local_tlsdesc_gotents;
Elf_Internal_Rela *rel;
Elf_Internal_Rela *relend;
symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
sym_hashes = elf_sym_hashes (input_bfd);
local_got_offsets = elf_local_got_offsets (input_bfd);
+ local_tlsdesc_gotents = elf_i386_local_tlsdesc_gotent (input_bfd);
rel = relocs;
relend = relocs + input_section->reloc_count;
struct elf_link_hash_entry *h;
Elf_Internal_Sym *sym;
asection *sec;
- bfd_vma off;
+ bfd_vma off, offplt;
bfd_vma relocation;
bfd_boolean unresolved_reloc;
bfd_reloc_status_type r;
unresolved_reloc, warned);
}
+ if (r_symndx == 0)
+ {
+ /* r_symndx will be zero only for relocs against symbols from
+ removed linkonce sections, or sections discarded by a linker
+ script. For these relocs, we just want the section contents
+ zeroed. Avoid any special processing in the switch below. */
+ r_type = R_386_NONE;
+
+ relocation = 0;
+ if (howto->pc_relative)
+ relocation = (input_section->output_section->vma
+ + input_section->output_offset
+ + rel->r_offset);
+ }
+
switch (r_type)
{
case R_386_GOT32:
for shared library since it may not be local when used
as function address. */
if (info->shared
+ && !info->executable
&& h
&& h->def_regular
&& h->type == STT_FUNC
case R_386_32:
case R_386_PC32:
- /* r_symndx will be zero only for relocs against symbols
- from removed linkonce sections, or sections discarded by
- a linker script. */
- if (r_symndx == 0)
- {
- /* Zero the section contents. eh_frame generated by old
- versions of gcc isn't edited by elf-eh-frame.c, so
- FDEs for discarded linkonce functions might remain.
- Putting zeros here will zero such FDE's address range.
- This is a hint to unwinders and other consumers of
- exception handling info that the FDE is invalid. */
- bfd_put_32 (input_bfd, 0, contents + rel->r_offset);
- break;
- }
-
if ((input_section->flags & SEC_ALLOC) == 0)
break;
/* Fall through */
case R_386_TLS_GD:
+ case R_386_TLS_GOTDESC:
+ case R_386_TLS_DESC_CALL:
case R_386_TLS_IE_32:
case R_386_TLS_GOTIE:
r_type = elf_i386_tls_transition (info, r_type, h == NULL);
}
if (tls_type == GOT_TLS_IE)
tls_type = GOT_TLS_IE_NEG;
- if (r_type == R_386_TLS_GD)
+ if (r_type == R_386_TLS_GD
+ || r_type == R_386_TLS_GOTDESC
+ || r_type == R_386_TLS_DESC_CALL)
{
if (tls_type == GOT_TLS_IE_POS)
r_type = R_386_TLS_GOTIE;
rel++;
continue;
}
+ else if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_GOTDESC)
+ {
+ /* GDesc -> LE transition.
+ It's originally something like:
+ leal x@tlsdesc(%ebx), %eax
+
+ leal x@ntpoff, %eax
+
+ Registers other than %eax may be set up here. */
+
+ unsigned int val, type;
+ bfd_vma roff;
+
+ /* First, make sure it's a leal adding ebx to a
+ 32-bit offset into any register, although it's
+ probably almost always going to be eax. */
+ roff = rel->r_offset;
+ BFD_ASSERT (roff >= 2);
+ type = bfd_get_8 (input_bfd, contents + roff - 2);
+ BFD_ASSERT (type == 0x8d);
+ val = bfd_get_8 (input_bfd, contents + roff - 1);
+ BFD_ASSERT ((val & 0xc7) == 0x83);
+ BFD_ASSERT (roff + 4 <= input_section->size);
+
+ /* Now modify the instruction as appropriate. */
+ /* aoliva FIXME: remove the above and xor the byte
+ below with 0x86. */
+ bfd_put_8 (output_bfd, val ^ 0x86,
+ contents + roff - 1);
+ bfd_put_32 (output_bfd, -tpoff (info, relocation),
+ contents + roff);
+ continue;
+ }
+ else if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_DESC_CALL)
+ {
+ /* GDesc -> LE transition.
+ It's originally:
+ call *(%eax)
+ Turn it into:
+ nop; nop */
+
+ unsigned int val, type;
+ bfd_vma roff;
+
+ /* First, make sure it's a call *(%eax). */
+ roff = rel->r_offset;
+ BFD_ASSERT (roff + 2 <= input_section->size);
+ type = bfd_get_8 (input_bfd, contents + roff);
+ BFD_ASSERT (type == 0xff);
+ val = bfd_get_8 (input_bfd, contents + roff + 1);
+ BFD_ASSERT (val == 0x10);
+
+ /* Now modify the instruction as appropriate. */
+ bfd_put_8 (output_bfd, 0x90, contents + roff);
+ bfd_put_8 (output_bfd, 0x90, contents + roff + 1);
+ continue;
+ }
else if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_IE)
{
unsigned int val, type;
abort ();
if (h != NULL)
- off = h->got.offset;
+ {
+ off = h->got.offset;
+ offplt = elf_i386_hash_entry (h)->tlsdesc_got;
+ }
else
{
if (local_got_offsets == NULL)
abort ();
off = local_got_offsets[r_symndx];
+ offplt = local_tlsdesc_gotents[r_symndx];
}
if ((off & 1) != 0)
Elf_Internal_Rela outrel;
bfd_byte *loc;
int dr_type, indx;
+ asection *sreloc;
if (htab->srelgot == NULL)
abort ();
+ indx = h && h->dynindx != -1 ? h->dynindx : 0;
+
+ if (GOT_TLS_GDESC_P (tls_type))
+ {
+ outrel.r_info = ELF32_R_INFO (indx, R_386_TLS_DESC);
+ BFD_ASSERT (htab->sgotplt_jump_table_size + offplt + 8
+ <= htab->sgotplt->size);
+ outrel.r_offset = (htab->sgotplt->output_section->vma
+ + htab->sgotplt->output_offset
+ + offplt
+ + htab->sgotplt_jump_table_size);
+ sreloc = htab->srelplt;
+ loc = sreloc->contents;
+ loc += sreloc->reloc_count++
+ * sizeof (Elf32_External_Rel);
+ BFD_ASSERT (loc + sizeof (Elf32_External_Rel)
+ <= sreloc->contents + sreloc->size);
+ bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
+ if (indx == 0)
+ {
+ BFD_ASSERT (! unresolved_reloc);
+ bfd_put_32 (output_bfd,
+ relocation - dtpoff_base (info),
+ htab->sgotplt->contents + offplt
+ + htab->sgotplt_jump_table_size + 4);
+ }
+ else
+ {
+ bfd_put_32 (output_bfd, 0,
+ htab->sgotplt->contents + offplt
+ + htab->sgotplt_jump_table_size + 4);
+ }
+ }
+
+ sreloc = htab->srelgot;
+
outrel.r_offset = (htab->sgot->output_section->vma
+ htab->sgot->output_offset + off);
- indx = h && h->dynindx != -1 ? h->dynindx : 0;
- if (r_type == R_386_TLS_GD)
+ if (GOT_TLS_GD_P (tls_type))
dr_type = R_386_TLS_DTPMOD32;
+ else if (GOT_TLS_GDESC_P (tls_type))
+ goto dr_done;
else if (tls_type == GOT_TLS_IE_POS)
dr_type = R_386_TLS_TPOFF;
else
dr_type = R_386_TLS_TPOFF32;
+
if (dr_type == R_386_TLS_TPOFF && indx == 0)
bfd_put_32 (output_bfd, relocation - dtpoff_base (info),
htab->sgot->contents + off);
else if (dr_type == R_386_TLS_TPOFF32 && indx == 0)
bfd_put_32 (output_bfd, dtpoff_base (info) - relocation,
htab->sgot->contents + off);
- else
+ else if (dr_type != R_386_TLS_DESC)
bfd_put_32 (output_bfd, 0,
htab->sgot->contents + off);
outrel.r_info = ELF32_R_INFO (indx, dr_type);
- loc = htab->srelgot->contents;
- loc += htab->srelgot->reloc_count++ * sizeof (Elf32_External_Rel);
+
+ loc = sreloc->contents;
+ loc += sreloc->reloc_count++ * sizeof (Elf32_External_Rel);
+ BFD_ASSERT (loc + sizeof (Elf32_External_Rel)
+ <= sreloc->contents + sreloc->size);
bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
- if (r_type == R_386_TLS_GD)
+ if (GOT_TLS_GD_P (tls_type))
{
if (indx == 0)
{
outrel.r_info = ELF32_R_INFO (indx,
R_386_TLS_DTPOFF32);
outrel.r_offset += 4;
- htab->srelgot->reloc_count++;
+ sreloc->reloc_count++;
loc += sizeof (Elf32_External_Rel);
+ BFD_ASSERT (loc + sizeof (Elf32_External_Rel)
+ <= sreloc->contents + sreloc->size);
bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
}
}
htab->sgot->contents + off + 4);
outrel.r_info = ELF32_R_INFO (indx, R_386_TLS_TPOFF);
outrel.r_offset += 4;
- htab->srelgot->reloc_count++;
+ sreloc->reloc_count++;
loc += sizeof (Elf32_External_Rel);
bfd_elf32_swap_reloc_out (output_bfd, &outrel, loc);
}
+ dr_done:
if (h != NULL)
h->got.offset |= 1;
else
local_got_offsets[r_symndx] |= 1;
}
- if (off >= (bfd_vma) -2)
+ if (off >= (bfd_vma) -2
+ && ! GOT_TLS_GDESC_P (tls_type))
abort ();
- if (r_type == ELF32_R_TYPE (rel->r_info))
+ if (r_type == R_386_TLS_GOTDESC
+ || r_type == R_386_TLS_DESC_CALL)
+ {
+ relocation = htab->sgotplt_jump_table_size + offplt;
+ unresolved_reloc = FALSE;
+ }
+ else if (r_type == ELF32_R_TYPE (rel->r_info))
{
bfd_vma g_o_t = htab->sgotplt->output_section->vma
+ htab->sgotplt->output_offset;
relocation = htab->sgot->output_section->vma
- + htab->sgot->output_offset + off - g_o_t;
+ + htab->sgot->output_offset + off - g_o_t;
if ((r_type == R_386_TLS_IE || r_type == R_386_TLS_GOTIE)
&& tls_type == GOT_TLS_IE_BOTH)
relocation += 4;
relocation += g_o_t;
unresolved_reloc = FALSE;
}
- else
+ else if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_GD)
{
unsigned int val, type;
bfd_vma roff;
rel++;
continue;
}
+ else if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_GOTDESC)
+ {
+ /* GDesc -> IE transition.
+ It's originally something like:
+ leal x@tlsdesc(%ebx), %eax
+
+ Change it to:
+ movl x@gotntpoff(%ebx), %eax # before nop; nop
+ or:
+ movl x@gottpoff(%ebx), %eax # before negl %eax
+
+ Registers other than %eax may be set up here. */
+
+ unsigned int val, type;
+ bfd_vma roff;
+
+ /* First, make sure it's a leal adding ebx to a 32-bit
+ offset into any register, although it's probably
+ almost always going to be eax. */
+ roff = rel->r_offset;
+ BFD_ASSERT (roff >= 2);
+ type = bfd_get_8 (input_bfd, contents + roff - 2);
+ BFD_ASSERT (type == 0x8d);
+ val = bfd_get_8 (input_bfd, contents + roff - 1);
+ BFD_ASSERT ((val & 0xc7) == 0x83);
+ BFD_ASSERT (roff + 4 <= input_section->size);
+
+ /* Now modify the instruction as appropriate. */
+ /* To turn a leal into a movl in the form we use it, it
+ suffices to change the first byte from 0x8d to 0x8b.
+ aoliva FIXME: should we decide to keep the leal, all
+ we have to do is remove the statement below, and
+ adjust the relaxation of R_386_TLS_DESC_CALL. */
+ bfd_put_8 (output_bfd, 0x8b, contents + roff - 2);
+
+ if (tls_type == GOT_TLS_IE_BOTH)
+ off += 4;
+
+ bfd_put_32 (output_bfd,
+ htab->sgot->output_section->vma
+ + htab->sgot->output_offset + off
+ - htab->sgotplt->output_section->vma
+ - htab->sgotplt->output_offset,
+ contents + roff);
+ continue;
+ }
+ else if (ELF32_R_TYPE (rel->r_info) == R_386_TLS_DESC_CALL)
+ {
+ /* GDesc -> IE transition.
+ It's originally:
+ call *(%eax)
+
+ Change it to:
+ nop; nop
+ or
+ negl %eax
+ depending on how we transformed the TLS_GOTDESC above.
+ */
+
+ unsigned int val, type;
+ bfd_vma roff;
+
+ /* First, make sure it's a call *(%eax). */
+ roff = rel->r_offset;
+ BFD_ASSERT (roff + 2 <= input_section->size);
+ type = bfd_get_8 (input_bfd, contents + roff);
+ BFD_ASSERT (type == 0xff);
+ val = bfd_get_8 (input_bfd, contents + roff + 1);
+ BFD_ASSERT (val == 0x10);
+
+ /* Now modify the instruction as appropriate. */
+ if (tls_type != GOT_TLS_IE_NEG)
+ {
+ /* nop; nop */
+ bfd_put_8 (output_bfd, 0x90, contents + roff);
+ bfd_put_8 (output_bfd, 0x90, contents + roff + 1);
+ }
+ else
+ {
+ /* negl %eax */
+ bfd_put_8 (output_bfd, 0xf7, contents + roff);
+ bfd_put_8 (output_bfd, 0xd8, contents + roff + 1);
+ }
+
+ continue;
+ }
+ else
+ BFD_ASSERT (FALSE);
break;
case R_386_TLS_LDM:
&& h->def_dynamic))
{
(*_bfd_error_handler)
- (_("%B(%A+0x%lx): unresolvable relocation against symbol `%s'"),
+ (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"),
input_bfd,
input_section,
(long) rel->r_offset,
+ howto->name,
h->root.root.string);
return FALSE;
}
+ htab->sgotplt->output_offset
+ got_offset),
htab->splt->contents + h->plt.offset + 2);
+
+ if (htab->is_vxworks)
+ {
+ int s, k, reloc_index;
+
+ /* Create the R_386_32 relocation referencing the GOT
+ for this PLT entry. */
+
+ /* S: Current slot number (zero-based). */
+ s = (h->plt.offset - PLT_ENTRY_SIZE) / PLT_ENTRY_SIZE;
+ /* K: Number of relocations for PLTResolve. */
+ if (info->shared)
+ k = PLTRESOLVE_RELOCS_SHLIB;
+ else
+ k = PLTRESOLVE_RELOCS;
+ /* Skip the PLTresolve relocations, and the relocations for
+ the other PLT slots. */
+ reloc_index = k + s * PLT_NON_JUMP_SLOT_RELOCS;
+ loc = (htab->srelplt2->contents + reloc_index
+ * sizeof (Elf32_External_Rel));
+
+ rel.r_offset = (htab->splt->output_section->vma
+ + htab->splt->output_offset
+ + h->plt.offset + 2),
+ rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_386_32);
+ bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
+
+ /* Create the R_386_32 relocation referencing the beginning of
+ the PLT for this GOT entry. */
+ rel.r_offset = (htab->sgotplt->output_section->vma
+ + htab->sgotplt->output_offset
+ + got_offset);
+ rel.r_info = ELF32_R_INFO (htab->elf.hplt->indx, R_386_32);
+ bfd_elf32_swap_reloc_out (output_bfd, &rel,
+ loc + sizeof (Elf32_External_Rel));
+ }
}
else
{
}
if (h->got.offset != (bfd_vma) -1
- && elf_i386_hash_entry(h)->tls_type != GOT_TLS_GD
+ && ! GOT_TLS_GD_ANY_P (elf_i386_hash_entry(h)->tls_type)
&& (elf_i386_hash_entry(h)->tls_type & GOT_TLS_IE) == 0)
{
Elf_Internal_Rela rel;
bfd_elf32_swap_reloc_out (output_bfd, &rel, loc);
}
- /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. */
+ /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute.
+ On VxWorks, the _GLOBAL_OFFSET_TABLE_ symbol is not absolute: it
+ is relative to the ".got" section. */
if (strcmp (h->root.root.string, "_DYNAMIC") == 0
- || strcmp (h->root.root.string, "_GLOBAL_OFFSET_TABLE_") == 0)
+ || (!htab->is_vxworks && h == htab->elf.hgot))
sym->st_shndx = SHN_ABS;
return TRUE;
if (htab->splt && htab->splt->size > 0)
{
if (info->shared)
- memcpy (htab->splt->contents,
- elf_i386_pic_plt0_entry, PLT_ENTRY_SIZE);
+ {
+ memcpy (htab->splt->contents, elf_i386_pic_plt0_entry,
+ sizeof (elf_i386_pic_plt0_entry));
+ memset (htab->splt->contents + sizeof (elf_i386_pic_plt0_entry),
+ htab->plt0_pad_byte,
+ PLT_ENTRY_SIZE - sizeof (elf_i386_pic_plt0_entry));
+ }
else
{
- memcpy (htab->splt->contents,
- elf_i386_plt0_entry, PLT_ENTRY_SIZE);
+ memcpy (htab->splt->contents, elf_i386_plt0_entry,
+ sizeof(elf_i386_plt0_entry));
+ memset (htab->splt->contents + sizeof (elf_i386_plt0_entry),
+ htab->plt0_pad_byte,
+ PLT_ENTRY_SIZE - sizeof (elf_i386_plt0_entry));
bfd_put_32 (output_bfd,
(htab->sgotplt->output_section->vma
+ htab->sgotplt->output_offset
+ htab->sgotplt->output_offset
+ 8),
htab->splt->contents + 8);
+
+ if (htab->is_vxworks)
+ {
+ Elf_Internal_Rela rel;
+
+ /* Generate a relocation for _GLOBAL_OFFSET_TABLE_ + 4.
+ On IA32 we use REL relocations so the addend goes in
+ the PLT directly. */
+ rel.r_offset = (htab->splt->output_section->vma
+ + htab->splt->output_offset
+ + 2);
+ rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_386_32);
+ bfd_elf32_swap_reloc_out (output_bfd, &rel,
+ htab->srelplt2->contents);
+ /* Generate a relocation for _GLOBAL_OFFSET_TABLE_ + 8. */
+ rel.r_offset = (htab->splt->output_section->vma
+ + htab->splt->output_offset
+ + 8);
+ rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_386_32);
+ bfd_elf32_swap_reloc_out (output_bfd, &rel,
+ htab->srelplt2->contents +
+ sizeof (Elf32_External_Rel));
+ }
}
/* UnixWare sets the entsize of .plt to 4, although that doesn't
really seem like the right value. */
elf_section_data (htab->splt->output_section)
->this_hdr.sh_entsize = 4;
+
+ /* Correct the .rel.plt.unloaded relocations. */
+ if (htab->is_vxworks && !info->shared)
+ {
+ int num_plts = (htab->splt->size / PLT_ENTRY_SIZE) - 1;
+ unsigned char *p;
+
+ p = htab->srelplt2->contents;
+ if (info->shared)
+ p += PLTRESOLVE_RELOCS_SHLIB * sizeof (Elf32_External_Rel);
+ else
+ p += PLTRESOLVE_RELOCS * sizeof (Elf32_External_Rel);
+
+ for (; num_plts; num_plts--)
+ {
+ Elf_Internal_Rela rel;
+ bfd_elf32_swap_reloc_in (output_bfd, p, &rel);
+ rel.r_info = ELF32_R_INFO (htab->elf.hgot->indx, R_386_32);
+ bfd_elf32_swap_reloc_out (output_bfd, &rel, p);
+ p += sizeof (Elf32_External_Rel);
+
+ bfd_elf32_swap_reloc_in (output_bfd, p, &rel);
+ rel.r_info = ELF32_R_INFO (htab->elf.hplt->indx, R_386_32);
+ bfd_elf32_swap_reloc_out (output_bfd, &rel, p);
+ p += sizeof (Elf32_External_Rel);
+ }
+ }
}
}
#define elf_backend_reloc_type_class elf_i386_reloc_type_class
#define elf_backend_relocate_section elf_i386_relocate_section
#define elf_backend_size_dynamic_sections elf_i386_size_dynamic_sections
+#define elf_backend_always_size_sections elf_i386_always_size_sections
#define elf_backend_plt_sym_val elf_i386_plt_sym_val
#include "elf32-target.h"
#define elf32_bed elf32_i386_fbsd_bed
#include "elf32-target.h"
+
+/* VxWorks support. */
+
+#undef TARGET_LITTLE_SYM
+#define TARGET_LITTLE_SYM bfd_elf32_i386_vxworks_vec
+#undef TARGET_LITTLE_NAME
+#define TARGET_LITTLE_NAME "elf32-i386-vxworks"
+
+
+/* Like elf_i386_link_hash_table_create but with tweaks for VxWorks. */
+
+static struct bfd_link_hash_table *
+elf_i386_vxworks_link_hash_table_create (bfd *abfd)
+{
+ struct bfd_link_hash_table *ret;
+ struct elf_i386_link_hash_table *htab;
+
+ ret = elf_i386_link_hash_table_create (abfd);
+ if (ret)
+ {
+ htab = (struct elf_i386_link_hash_table *) ret;
+ htab->is_vxworks = 1;
+ htab->plt0_pad_byte = 0x90;
+ }
+
+ return ret;
+}
+
+
+/* Tweak magic VxWorks symbols as they are written to the output file. */
+static bfd_boolean
+elf_i386_vxworks_link_output_symbol_hook (struct bfd_link_info *info
+ ATTRIBUTE_UNUSED,
+ const char *name,
+ Elf_Internal_Sym *sym,
+ asection *input_sec ATTRIBUTE_UNUSED,
+ struct elf_link_hash_entry *h
+ ATTRIBUTE_UNUSED)
+{
+ /* Ignore the first dummy symbol. */
+ if (!name)
+ return TRUE;
+
+ return elf_vxworks_link_output_symbol_hook (name, sym);
+}
+
+#undef elf_backend_post_process_headers
+#undef bfd_elf32_bfd_link_hash_table_create
+#define bfd_elf32_bfd_link_hash_table_create \
+ elf_i386_vxworks_link_hash_table_create
+#undef elf_backend_add_symbol_hook
+#define elf_backend_add_symbol_hook \
+ elf_vxworks_add_symbol_hook
+#undef elf_backend_link_output_symbol_hook
+#define elf_backend_link_output_symbol_hook \
+ elf_i386_vxworks_link_output_symbol_hook
+#undef elf_backend_emit_relocs
+#define elf_backend_emit_relocs elf_vxworks_emit_relocs
+#undef elf_backend_final_write_processing
+#define elf_backend_final_write_processing \
+ elf_vxworks_final_write_processing
+
+/* On VxWorks, we emit relocations against _PROCEDURE_LINKAGE_TABLE_, so
+ define it. */
+#undef elf_backend_want_plt_sym
+#define elf_backend_want_plt_sym 1
+
+#undef elf32_bed
+#define elf32_bed elf32_i386_vxworks_bed
+
+#include "elf32-target.h"