// output.cc -- manage the output file for gold
-// Copyright 2006, 2007, 2008 Free Software Foundation, Inc.
+// Copyright 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
// Written by Ian Lance Taylor <iant@google.com>.
// This file is part of gold.
#include "symtab.h"
#include "reloc.h"
#include "merge.h"
+#include "descriptors.h"
#include "output.h"
// Some BSD systems still use MAP_ANON instead of MAP_ANONYMOUS
# define MAP_ANONYMOUS MAP_ANON
#endif
+#ifndef HAVE_POSIX_FALLOCATE
+// A dummy, non general, version of posix_fallocate. Here we just set
+// the file size and hope that there is enough disk space. FIXME: We
+// could allocate disk space by walking block by block and writing a
+// zero byte into each block.
+static int
+posix_fallocate(int o, off_t offset, off_t len)
+{
+ return ftruncate(o, offset + len);
+}
+#endif // !defined(HAVE_POSIX_FALLOCATE)
+
namespace gold
{
? elfcpp::ELFDATA2MSB
: elfcpp::ELFDATA2LSB);
e_ident[elfcpp::EI_VERSION] = elfcpp::EV_CURRENT;
- // FIXME: Some targets may need to set EI_OSABI and EI_ABIVERSION.
oehdr.put_e_ident(e_ident);
elfcpp::ET e_type;
else
oehdr.put_e_shstrndx(elfcpp::SHN_XINDEX);
+ // Let the target adjust the ELF header, e.g., to set EI_OSABI in
+ // the e_ident field.
+ parameters->target().adjust_elf_header(view, ehdr_size);
+
of->write_output_view(0, ehdr_size, view);
}
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
Symbol* gsym,
unsigned int type,
- Relobj* relobj,
+ Sized_relobj<size, big_endian>* relobj,
unsigned int shndx,
Address address,
bool is_relative)
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
Output_section* os,
unsigned int type,
- Relobj* relobj,
+ Sized_relobj<size, big_endian>* relobj,
unsigned int shndx,
Address address)
: address_(address), local_sym_index_(SECTION_CODE), type_(type),
if (!this->is_section_symbol_)
this->u1_.relobj->set_needs_output_dynsym_entry(lsi);
else
- {
- section_offset_type dummy;
- Output_section* os = this->u1_.relobj->output_section(lsi, &dummy);
- gold_assert(os != NULL);
- os->set_needs_dynsym_index();
- }
+ this->u1_.relobj->output_section(lsi)->set_needs_dynsym_index();
}
break;
}
}
else
{
- section_offset_type dummy;
- Output_section* os = this->u1_.relobj->output_section(lsi, &dummy);
+ Output_section* os = this->u1_.relobj->output_section(lsi);
gold_assert(os != NULL);
if (dynamic)
index = os->dynsym_index();
// within the input section.
template<bool dynamic, int size, bool big_endian>
-section_offset_type
+typename elfcpp::Elf_types<size>::Elf_Addr
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::
local_section_offset(Addend addend) const
{
&& this->local_sym_index_ != INVALID_CODE
&& this->is_section_symbol_);
const unsigned int lsi = this->local_sym_index_;
- section_offset_type offset;
- Output_section* os = this->u1_.relobj->output_section(lsi, &offset);
+ Output_section* os = this->u1_.relobj->output_section(lsi);
gold_assert(os != NULL);
- if (offset != -1)
+ Address offset = this->u1_.relobj->get_output_section_offset(lsi);
+ if (offset != invalid_address)
return offset + addend;
// This is a merge section.
offset = os->output_address(this->u1_.relobj, lsi, addend);
- gold_assert(offset != -1);
+ gold_assert(offset != invalid_address);
return offset;
}
-// Write out the offset and info fields of a Rel or Rela relocation
-// entry.
+// Get the output address of a relocation.
template<bool dynamic, int size, bool big_endian>
-template<typename Write_rel>
-void
-Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::write_rel(
- Write_rel* wr) const
+typename elfcpp::Elf_types<size>::Elf_Addr
+Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::get_address() const
{
Address address = this->address_;
if (this->shndx_ != INVALID_CODE)
{
- section_offset_type off;
- Output_section* os = this->u2_.relobj->output_section(this->shndx_,
- &off);
+ Output_section* os = this->u2_.relobj->output_section(this->shndx_);
gold_assert(os != NULL);
- if (off != -1)
+ Address off = this->u2_.relobj->get_output_section_offset(this->shndx_);
+ if (off != invalid_address)
address += os->address() + off;
else
{
address = os->output_address(this->u2_.relobj, this->shndx_,
address);
- gold_assert(address != -1U);
+ gold_assert(address != invalid_address);
}
}
else if (this->u2_.od != NULL)
address += this->u2_.od->address();
- wr->put_r_offset(address);
+ return address;
+}
+
+// Write out the offset and info fields of a Rel or Rela relocation
+// entry.
+
+template<bool dynamic, int size, bool big_endian>
+template<typename Write_rel>
+void
+Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::write_rel(
+ Write_rel* wr) const
+{
+ wr->put_r_offset(this->get_address());
unsigned int sym_index = this->is_relative_ ? 0 : this->get_symbol_index();
wr->put_r_info(elfcpp::elf_r_info<size>(sym_index, this->type_));
}
return symval->value(this->u1_.relobj, addend);
}
+// Reloc comparison. This function sorts the dynamic relocs for the
+// benefit of the dynamic linker. First we sort all relative relocs
+// to the front. Among relative relocs, we sort by output address.
+// Among non-relative relocs, we sort by symbol index, then by output
+// address.
+
+template<bool dynamic, int size, bool big_endian>
+int
+Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::
+ compare(const Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>& r2)
+ const
+{
+ if (this->is_relative_)
+ {
+ if (!r2.is_relative_)
+ return -1;
+ // Otherwise sort by reloc address below.
+ }
+ else if (r2.is_relative_)
+ return 1;
+ else
+ {
+ unsigned int sym1 = this->get_symbol_index();
+ unsigned int sym2 = r2.get_symbol_index();
+ if (sym1 < sym2)
+ return -1;
+ else if (sym1 > sym2)
+ return 1;
+ // Otherwise sort by reloc address.
+ }
+
+ section_offset_type addr1 = this->get_address();
+ section_offset_type addr2 = r2.get_address();
+ if (addr1 < addr2)
+ return -1;
+ else if (addr1 > addr2)
+ return 1;
+
+ // Final tie breaker, in order to generate the same output on any
+ // host: reloc type.
+ unsigned int type1 = this->type_;
+ unsigned int type2 = r2.type_;
+ if (type1 < type2)
+ return -1;
+ else if (type1 > type2)
+ return 1;
+
+ // These relocs appear to be exactly the same.
+ return 0;
+}
+
// Write out a Rela relocation.
template<bool dynamic, int size, bool big_endian>
const off_t oview_size = this->data_size();
unsigned char* const oview = of->get_output_view(off, oview_size);
+ if (this->sort_relocs_)
+ {
+ gold_assert(dynamic);
+ std::sort(this->relocs_.begin(), this->relocs_.end(),
+ Sort_relocs_comparison());
+ }
+
unsigned char* pov = oview;
for (typename Relocs::const_iterator p = this->relocs_.begin();
p != this->relocs_.end();
p != this->input_shndxes_.end();
++p, ++contents)
{
- section_offset_type dummy;
- Output_section* os = this->relobj_->output_section(*p, &dummy);
+ Output_section* os = this->relobj_->output_section(*p);
unsigned int output_shndx;
if (os != NULL)
this->entries_.push_back(Got_entry());
unsigned int got_offset = this->last_got_offset();
object->set_local_got_offset(symndx, got_type, got_offset);
- section_offset_type off;
- Output_section* os = object->output_section(shndx, &off);
+ Output_section* os = object->output_section(shndx);
rel_dyn->add_output_section(os, r_type_1, this, got_offset);
this->entries_.push_back(Got_entry(object, symndx));
this->entries_.push_back(Got_entry());
unsigned int got_offset = this->last_got_offset();
object->set_local_got_offset(symndx, got_type, got_offset);
- section_offset_type off;
- Output_section* os = object->output_section(shndx, &off);
+ Output_section* os = object->output_section(shndx);
rela_dyn->add_output_section(os, r_type_1, this, got_offset, 0);
this->entries_.push_back(Got_entry(object, symndx));
this->u2_.posd->write_to_buffer(buffer);
}
+// Print to a map file.
+
+void
+Output_section::Input_section::print_to_mapfile(Mapfile* mapfile) const
+{
+ switch (this->shndx_)
+ {
+ case OUTPUT_SECTION_CODE:
+ case MERGE_DATA_SECTION_CODE:
+ case MERGE_STRING_SECTION_CODE:
+ this->u2_.posd->print_to_mapfile(mapfile);
+ break;
+
+ default:
+ mapfile->print_input_section(this->u2_.object, this->shndx_);
+ break;
+ }
+}
+
// Output_section methods.
// Construct an Output_section. NAME will point into a Stringpool.
may_sort_attached_input_sections_(false),
must_sort_attached_input_sections_(false),
attached_input_sections_are_sorted_(false),
+ is_relro_(false),
+ is_relro_local_(false),
tls_offset_(0)
{
// An unallocated section has no address. Forcing this means that
// If this is a SHF_MERGE section, we pass all the input sections to
// a Output_data_merge. We don't try to handle relocations for such
- // a section.
+ // a section. We don't try to handle empty merge sections--they
+ // mess up the mappings, and are useless anyhow.
if ((sh_flags & elfcpp::SHF_MERGE) != 0
- && reloc_shndx == 0)
+ && reloc_shndx == 0
+ && shdr.get_sh_size() > 0)
{
if (this->add_merge_input_section(object, shndx, sh_flags,
entsize, addralign))
if (have_sections_script
|| !this->input_sections_.empty()
|| this->may_sort_attached_input_sections()
- || this->must_sort_attached_input_sections())
+ || this->must_sort_attached_input_sections()
+ || parameters->options().user_set_Map())
this->input_sections_.push_back(Input_section(object, shndx,
shdr.get_sh_size(),
addralign));
unsigned int shndx,
off_t offset) const
{
- gold_assert(object->is_section_specially_mapped(shndx));
-
for (Input_section_list::const_iterator p = this->input_sections_.begin();
p != this->input_sections_.end();
++p)
Output_section::output_offset(const Relobj* object, unsigned int shndx,
section_offset_type offset) const
{
- gold_assert(object->is_section_specially_mapped(shndx));
// This can only be called meaningfully when layout is complete.
gold_assert(Output_data::is_layout_complete());
Output_section::output_address(const Relobj* object, unsigned int shndx,
off_t offset) const
{
- gold_assert(object->is_section_specially_mapped(shndx));
-
uint64_t addr = this->address() + this->first_input_offset_;
for (Input_section_list::const_iterator p = this->input_sections_.begin();
p != this->input_sections_.end();
if (p->output_offset(object, shndx, offset, &output_offset))
{
if (output_offset == -1)
- return -1U;
+ return -1ULL;
return addr + output_offset;
}
addr += p->data_size();
gold_unreachable();
}
-// Return the output address of the start of the merged section for
+// Find the output address of the start of the merged section for
// input section SHNDX in object OBJECT.
-uint64_t
-Output_section::starting_output_address(const Relobj* object,
- unsigned int shndx) const
+bool
+Output_section::find_starting_output_address(const Relobj* object,
+ unsigned int shndx,
+ uint64_t* paddr) const
{
- gold_assert(object->is_section_specially_mapped(shndx));
-
uint64_t addr = this->address() + this->first_input_offset_;
for (Input_section_list::const_iterator p = this->input_sections_.begin();
p != this->input_sections_.end();
// Unfortunately we don't know for sure that input offset 0 is
// mapped at all.
if (p->is_merge_section_for(object, shndx))
- return addr;
+ {
+ *paddr = addr;
+ return true;
+ }
addr += p->data_size();
}
- gold_unreachable();
+
+ // We couldn't find a merge output section for this input section.
+ return false;
}
// Set the data size of an Output_section. This is where we handle
data_size, addralign));
}
+// Print to the map file.
+
+void
+Output_section::do_print_to_mapfile(Mapfile* mapfile) const
+{
+ mapfile->print_output_section(this);
+
+ for (Input_section_list::const_iterator p = this->input_sections_.begin();
+ p != this->input_sections_.end();
+ ++p)
+ p->print_to_mapfile(mapfile);
+}
+
// Print stats for merge sections to stderr.
void
void
Output_segment::add_output_section(Output_section* os,
- elfcpp::Elf_Word seg_flags,
- bool front)
+ elfcpp::Elf_Word seg_flags)
{
gold_assert((os->flags() & elfcpp::SHF_ALLOC) != 0);
gold_assert(!this->is_max_align_known_);
--p;
if ((*p)->is_section_type(elfcpp::SHT_NOTE))
{
- // We don't worry about the FRONT parameter.
++p;
pdl->insert(p, os);
return;
// and the PT_TLS segment -- we do this grouping only for the
// PT_LOAD segment.
if (this->type_ != elfcpp::PT_TLS
- && (os->flags() & elfcpp::SHF_TLS) != 0
- && !this->output_data_.empty())
+ && (os->flags() & elfcpp::SHF_TLS) != 0)
{
pdl = &this->output_data_;
bool nobits = os->type() == elfcpp::SHT_NOBITS;
{
sawtls = true;
// Put a NOBITS section after the first TLS section.
- // But a PROGBITS section after the first TLS/PROGBITS
+ // Put a PROGBITS section after the first TLS/PROGBITS
// section.
insert = nobits || !(*p)->is_section_type(elfcpp::SHT_NOBITS);
}
if (insert)
{
- // We don't worry about the FRONT parameter.
++p;
pdl->insert(p, os);
return;
// location in the section list.
}
- if (front)
- pdl->push_front(os);
- else
- pdl->push_back(os);
+ // For the PT_GNU_RELRO segment, we need to group relro sections,
+ // and we need to put them before any non-relro sections. Also,
+ // relro local sections go before relro non-local sections.
+ if (parameters->options().relro() && os->is_relro())
+ {
+ gold_assert(pdl == &this->output_data_);
+ Output_segment::Output_data_list::iterator p;
+ for (p = pdl->begin(); p != pdl->end(); ++p)
+ {
+ if (!(*p)->is_section())
+ break;
+
+ Output_section* pos = (*p)->output_section();
+ if (!pos->is_relro()
+ || (os->is_relro_local() && !pos->is_relro_local()))
+ break;
+ }
+
+ pdl->insert(p, os);
+ return;
+ }
+
+ pdl->push_back(os);
}
// Remove an Output_section from this segment. It is an error if it
this->output_data_.push_front(od);
}
+// Return whether the first data section is a relro section.
+
+bool
+Output_segment::is_first_section_relro() const
+{
+ return (!this->output_data_.empty()
+ && this->output_data_.front()->is_section()
+ && this->output_data_.front()->output_section()->is_relro());
+}
+
// Return the maximum alignment of the Output_data in Output_segment.
uint64_t
if (addralign > this->max_align_)
this->max_align_ = addralign;
+ // If -z relro is in effect, and the first section in this
+ // segment is a relro section, then the segment must be aligned
+ // to at least the common page size. This ensures that the
+ // PT_GNU_RELRO segment will start at a page boundary.
+ if (this->type_ == elfcpp::PT_LOAD
+ && parameters->options().relro()
+ && this->is_first_section_relro())
+ {
+ addralign = parameters->target().common_pagesize();
+ if (addralign > this->max_align_)
+ this->max_align_ = addralign;
+ }
+
this->is_max_align_known_ = true;
}
bool in_tls = false;
+ bool in_relro = (parameters->options().relro()
+ && this->is_first_section_relro());
+
off_t orig_off = *poff;
this->offset_ = orig_off;
addr = this->set_section_list_addresses(layout, reset, &this->output_data_,
- addr, poff, pshndx, &in_tls);
+ addr, poff, pshndx, &in_tls,
+ &in_relro);
this->filesz_ = *poff - orig_off;
off_t off = *poff;
uint64_t ret = this->set_section_list_addresses(layout, reset,
&this->output_bss_,
addr, poff, pshndx,
- &in_tls);
+ &in_tls, &in_relro);
// If the last section was a TLS section, align upward to the
// alignment of the TLS segment, so that the overall size of the TLS
*poff = align_address(*poff, segment_align);
}
+ // If all the sections were relro sections, align upward to the
+ // common page size.
+ if (in_relro)
+ {
+ uint64_t page_align = parameters->target().common_pagesize();
+ *poff = align_address(*poff, page_align);
+ }
+
this->memsz_ = *poff - orig_off;
// Ignore the file offset adjustments made by the BSS Output_data
Output_data_list* pdl,
uint64_t addr, off_t* poff,
unsigned int* pshndx,
- bool* in_tls)
+ bool* in_tls, bool* in_relro)
{
off_t startoff = *poff;
}
}
+ // If this is a non-relro section after a relro section,
+ // align it to a common page boundary so that the dynamic
+ // linker has a page to mark as read-only.
+ if (*in_relro
+ && (!(*p)->is_section()
+ || !(*p)->output_section()->is_relro()))
+ {
+ uint64_t page_align = parameters->target().common_pagesize();
+ if (page_align > align)
+ align = page_align;
+ *in_relro = false;
+ }
+
off = align_address(off, align);
(*p)->set_address_and_file_offset(addr + (off - startoff), off);
}
gold_assert(this->vaddr_ == align_address(this->vaddr_, segment_align));
this->memsz_ = align_address(this->memsz_, segment_align);
}
+
+ // If this is a RELRO segment, align the memory size. The code in
+ // set_section_list ensures that the section after the RELRO segment
+ // is aligned to give us room.
+ if (this->type_ == elfcpp::PT_GNU_RELRO)
+ {
+ uint64_t page_align = parameters->target().common_pagesize();
+ gold_assert(this->vaddr_ == align_address(this->vaddr_, page_align));
+ this->memsz_ = align_address(this->memsz_, page_align);
+ }
}
// Set the TLS offsets of the sections in the PT_TLS segment.
return v;
}
+// Print the output sections to the map file.
+
+void
+Output_segment::print_sections_to_mapfile(Mapfile* mapfile) const
+{
+ if (this->type() != elfcpp::PT_LOAD)
+ return;
+ this->print_section_list_to_mapfile(mapfile, &this->output_data_);
+ this->print_section_list_to_mapfile(mapfile, &this->output_bss_);
+}
+
+// Print an output section list to the map file.
+
+void
+Output_segment::print_section_list_to_mapfile(Mapfile* mapfile,
+ const Output_data_list* pdl) const
+{
+ for (Output_data_list::const_iterator p = pdl->begin();
+ p != pdl->end();
+ ++p)
+ (*p)->print_to_mapfile(mapfile);
+}
+
// Output_file methods.
Output_file::Output_file(const char* name)
unlink_if_ordinary(this->name_);
int mode = parameters->options().relocatable() ? 0666 : 0777;
- int o = ::open(this->name_, O_RDWR | O_CREAT | O_TRUNC, mode);
+ int o = open_descriptor(-1, this->name_, O_RDWR | O_CREAT | O_TRUNC,
+ mode);
if (o < 0)
gold_fatal(_("%s: open: %s"), this->name_, strerror(errno));
this->o_ = o;
}
}
+// Map a block of memory which will later be written to the file.
+// Return a pointer to the memory.
+
+void*
+Output_file::map_anonymous()
+{
+ this->map_is_anonymous_ = true;
+ return ::mmap(NULL, this->file_size_, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+}
+
// Map the file into memory.
void
|| ::fstat(o, &statbuf) != 0
|| !S_ISREG(statbuf.st_mode)
|| this->is_temporary_)
- {
- this->map_is_anonymous_ = true;
- base = ::mmap(NULL, this->file_size_, PROT_READ | PROT_WRITE,
- MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- }
+ base = this->map_anonymous();
else
{
- // Write out one byte to make the file the right size.
- if (::lseek(o, this->file_size_ - 1, SEEK_SET) < 0)
- gold_fatal(_("%s: lseek: %s"), this->name_, strerror(errno));
- char b = 0;
- if (::write(o, &b, 1) != 1)
- gold_fatal(_("%s: write: %s"), this->name_, strerror(errno));
+ // Ensure that we have disk space available for the file. If we
+ // don't do this, it is possible that we will call munmap,
+ // close, and exit with dirty buffers still in the cache with no
+ // assigned disk blocks. If the disk is out of space at that
+ // point, the output file will wind up incomplete, but we will
+ // have already exited. The alternative to fallocate would be
+ // to use fdatasync, but that would be a more significant
+ // performance hit.
+ if (::posix_fallocate(o, 0, this->file_size_) < 0)
+ gold_fatal(_("%s: %s"), this->name_, strerror(errno));
// Map the file into memory.
this->map_is_anonymous_ = false;
base = ::mmap(NULL, this->file_size_, PROT_READ | PROT_WRITE,
MAP_SHARED, o, 0);
+
+ // The mmap call might fail because of file system issues: the
+ // file system might not support mmap at all, or it might not
+ // support mmap with PROT_WRITE. I'm not sure which errno
+ // values we will see in all cases, so if the mmap fails for any
+ // reason try for an anonymous map.
+ if (base == MAP_FAILED)
+ base = this->map_anonymous();
}
if (base == MAP_FAILED)
- gold_fatal(_("%s: mmap: %s"), this->name_, strerror(errno));
+ gold_fatal(_("%s: mmap: failed to allocate %lu bytes for output file: %s"),
+ this->name_, static_cast<unsigned long>(this->file_size_),
+ strerror(errno));
this->base_ = static_cast<unsigned char*>(base);
}
if (this->map_is_anonymous_ && !this->is_temporary_)
{
size_t bytes_to_write = this->file_size_;
+ size_t offset = 0;
while (bytes_to_write > 0)
{
- ssize_t bytes_written = ::write(this->o_, this->base_, bytes_to_write);
+ ssize_t bytes_written = ::write(this->o_, this->base_ + offset,
+ bytes_to_write);
if (bytes_written == 0)
gold_error(_("%s: write: unexpected 0 return-value"), this->name_);
else if (bytes_written < 0)
gold_error(_("%s: write: %s"), this->name_, strerror(errno));
else
- bytes_to_write -= bytes_written;
+ {
+ bytes_to_write -= bytes_written;
+ offset += bytes_written;
+ }
}
}
this->unmap();
bool have_sections_script);
#endif
+#ifdef HAVE_TARGET_32_LITTLE
+template
+class Output_reloc<elfcpp::SHT_REL, false, 32, false>;
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+class Output_reloc<elfcpp::SHT_REL, false, 32, true>;
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+class Output_reloc<elfcpp::SHT_REL, false, 64, false>;
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+class Output_reloc<elfcpp::SHT_REL, false, 64, true>;
+#endif
+
+#ifdef HAVE_TARGET_32_LITTLE
+template
+class Output_reloc<elfcpp::SHT_REL, true, 32, false>;
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+class Output_reloc<elfcpp::SHT_REL, true, 32, true>;
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+class Output_reloc<elfcpp::SHT_REL, true, 64, false>;
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+class Output_reloc<elfcpp::SHT_REL, true, 64, true>;
+#endif
+
+#ifdef HAVE_TARGET_32_LITTLE
+template
+class Output_reloc<elfcpp::SHT_RELA, false, 32, false>;
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+class Output_reloc<elfcpp::SHT_RELA, false, 32, true>;
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+class Output_reloc<elfcpp::SHT_RELA, false, 64, false>;
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+class Output_reloc<elfcpp::SHT_RELA, false, 64, true>;
+#endif
+
+#ifdef HAVE_TARGET_32_LITTLE
+template
+class Output_reloc<elfcpp::SHT_RELA, true, 32, false>;
+#endif
+
+#ifdef HAVE_TARGET_32_BIG
+template
+class Output_reloc<elfcpp::SHT_RELA, true, 32, true>;
+#endif
+
+#ifdef HAVE_TARGET_64_LITTLE
+template
+class Output_reloc<elfcpp::SHT_RELA, true, 64, false>;
+#endif
+
+#ifdef HAVE_TARGET_64_BIG
+template
+class Output_reloc<elfcpp::SHT_RELA, true, 64, true>;
+#endif
+
#ifdef HAVE_TARGET_32_LITTLE
template
class Output_data_reloc<elfcpp::SHT_REL, false, 32, false>;