mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-21 04:42:53 +08:00
bce5a025d2
During a -r (or --emit-relocs) link, if two sections had the same name but different section types, gold would put relocations for both sections into the same relocation section even though the data sections remained separate. For .eh_frame sections, when one section is PROGBITS and another is X86_64_UNWIND, we really should be using the UNWIND section type and combining the sections anyway. For other sections, we should be creating one relocation section for each output data section. gold/ PR gold/23016 * incremental.cc (can_incremental_update): Check for unwind section type. * layout.h (Layout::layout): Add sh_type parameter. * layout.cc (Layout::layout): Likewise. (Layout::layout_reloc): Create new output reloc section if data section does not already have one. (Layout::layout_eh_frame): Check for unwind section type. (Layout::make_eh_frame_section): Use unwind section type for .eh_frame and .eh_frame_hdr. * object.h (Sized_relobj_file::Shdr_write): New typedef. (Sized_relobj_file::layout_section): Add sh_type parameter. (Sized_relobj_file::Deferred_layout::Deferred_layout): Add sh_type parameter. * object.cc (Sized_relobj_file::check_eh_frame_flags): Check for unwind section type. (Sized_relobj_file::layout_section): Add sh_type parameter; pass it to Layout::layout. (Sized_relobj_file::do_layout): Make local copy of sh_type. Force .eh_frame sections to unwind section type. Pass sh_type to layout_section. (Sized_relobj_file<size, big_endian>::do_layout_deferred_sections): Pass sh_type to layout_section. * output.cc (Output_section::Output_section): Initialize reloc_section_. * output.h (Output_section::reloc_section): New method. (Output_section::set_reloc_section): New method. (Output_section::reloc_section_): New data member. * target.h (Target::unwind_section_type): New method. (Target::Target_info::unwind_section_type): New data member. * aarch64.cc (aarch64_info): Add unwind_section_type. * arm.cc (arm_info, arm_nacl_info): Likewise. * i386.cc (i386_info, i386_nacl_info, iamcu_info): Likewise. * mips.cc (mips_info, mips_nacl_info): Likewise. * powerpc.cc (powerpc_info): Likewise. * s390.cc (s390_info): Likewise. * sparc.cc (sparc_info): Likewise. * tilegx.cc (tilegx_info): Likewise. * x86_64.cc (x86_64_info, x86_64_nacl_info): Likewise. * testsuite/Makefile.am (pr23016_1, pr23016_2): New test cases. * testsuite/Makefile.in: Regenerate. * testsuite/testfile.cc: Add unwind_section_type. * testsuite/pr23016_1.sh: New test script. * testsuite/pr23016_1a.s: New source file. * testsuite/pr23016_1b.s: New source file. * testsuite/pr23016_2.sh: New test script. * testsuite/pr23016_2a.s: New source file. * testsuite/pr23016_2b.s: New source file.
5615 lines
158 KiB
C++
5615 lines
158 KiB
C++
// output.cc -- manage the output file for gold
|
|
|
|
// Copyright (C) 2006-2018 Free Software Foundation, Inc.
|
|
// Written by Ian Lance Taylor <iant@google.com>.
|
|
|
|
// This file is part of gold.
|
|
|
|
// 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 3 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., 51 Franklin Street - Fifth Floor, Boston,
|
|
// MA 02110-1301, USA.
|
|
|
|
#include "gold.h"
|
|
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <cerrno>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <algorithm>
|
|
|
|
#ifdef HAVE_SYS_MMAN_H
|
|
#include <sys/mman.h>
|
|
#endif
|
|
|
|
#include "libiberty.h"
|
|
|
|
#include "dwarf.h"
|
|
#include "parameters.h"
|
|
#include "object.h"
|
|
#include "symtab.h"
|
|
#include "reloc.h"
|
|
#include "merge.h"
|
|
#include "descriptors.h"
|
|
#include "layout.h"
|
|
#include "output.h"
|
|
|
|
// For systems without mmap support.
|
|
#ifndef HAVE_MMAP
|
|
# define mmap gold_mmap
|
|
# define munmap gold_munmap
|
|
# define mremap gold_mremap
|
|
# ifndef MAP_FAILED
|
|
# define MAP_FAILED (reinterpret_cast<void*>(-1))
|
|
# endif
|
|
# ifndef PROT_READ
|
|
# define PROT_READ 0
|
|
# endif
|
|
# ifndef PROT_WRITE
|
|
# define PROT_WRITE 0
|
|
# endif
|
|
# ifndef MAP_PRIVATE
|
|
# define MAP_PRIVATE 0
|
|
# endif
|
|
# ifndef MAP_ANONYMOUS
|
|
# define MAP_ANONYMOUS 0
|
|
# endif
|
|
# ifndef MAP_SHARED
|
|
# define MAP_SHARED 0
|
|
# endif
|
|
|
|
# ifndef ENOSYS
|
|
# define ENOSYS EINVAL
|
|
# endif
|
|
|
|
static void *
|
|
gold_mmap(void *, size_t, int, int, int, off_t)
|
|
{
|
|
errno = ENOSYS;
|
|
return MAP_FAILED;
|
|
}
|
|
|
|
static int
|
|
gold_munmap(void *, size_t)
|
|
{
|
|
errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
static void *
|
|
gold_mremap(void *, size_t, size_t, int)
|
|
{
|
|
errno = ENOSYS;
|
|
return MAP_FAILED;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if defined(HAVE_MMAP) && !defined(HAVE_MREMAP)
|
|
# define mremap gold_mremap
|
|
extern "C" void *gold_mremap(void *, size_t, size_t, int);
|
|
#endif
|
|
|
|
// Some BSD systems still use MAP_ANON instead of MAP_ANONYMOUS
|
|
#ifndef MAP_ANONYMOUS
|
|
# define MAP_ANONYMOUS MAP_ANON
|
|
#endif
|
|
|
|
#ifndef MREMAP_MAYMOVE
|
|
# define MREMAP_MAYMOVE 1
|
|
#endif
|
|
|
|
// Mingw does not have S_ISLNK.
|
|
#ifndef S_ISLNK
|
|
# define S_ISLNK(mode) 0
|
|
#endif
|
|
|
|
namespace gold
|
|
{
|
|
|
|
// A wrapper around posix_fallocate. If we don't have posix_fallocate,
|
|
// or the --no-posix-fallocate option is set, we try the fallocate
|
|
// system call directly. If that fails, we use ftruncate to set
|
|
// the file size and hope that there is enough disk space.
|
|
|
|
static int
|
|
gold_fallocate(int o, off_t offset, off_t len)
|
|
{
|
|
if (len <= 0)
|
|
return 0;
|
|
|
|
#ifdef HAVE_POSIX_FALLOCATE
|
|
if (parameters->options().posix_fallocate())
|
|
{
|
|
int err = ::posix_fallocate(o, offset, len);
|
|
if (err != EINVAL && err != ENOSYS && err != EOPNOTSUPP)
|
|
return err;
|
|
}
|
|
#endif // defined(HAVE_POSIX_FALLOCATE)
|
|
|
|
#ifdef HAVE_FALLOCATE
|
|
{
|
|
int err = ::fallocate(o, 0, offset, len);
|
|
if (err != EINVAL && err != ENOSYS && err != EOPNOTSUPP)
|
|
return err;
|
|
}
|
|
#endif // defined(HAVE_FALLOCATE)
|
|
|
|
if (::ftruncate(o, offset + len) < 0)
|
|
return errno;
|
|
return 0;
|
|
}
|
|
|
|
// Output_data variables.
|
|
|
|
bool Output_data::allocated_sizes_are_fixed;
|
|
|
|
// Output_data methods.
|
|
|
|
Output_data::~Output_data()
|
|
{
|
|
}
|
|
|
|
// Return the default alignment for the target size.
|
|
|
|
uint64_t
|
|
Output_data::default_alignment()
|
|
{
|
|
return Output_data::default_alignment_for_size(
|
|
parameters->target().get_size());
|
|
}
|
|
|
|
// Return the default alignment for a size--32 or 64.
|
|
|
|
uint64_t
|
|
Output_data::default_alignment_for_size(int size)
|
|
{
|
|
if (size == 32)
|
|
return 4;
|
|
else if (size == 64)
|
|
return 8;
|
|
else
|
|
gold_unreachable();
|
|
}
|
|
|
|
// Output_section_header methods. This currently assumes that the
|
|
// segment and section lists are complete at construction time.
|
|
|
|
Output_section_headers::Output_section_headers(
|
|
const Layout* layout,
|
|
const Layout::Segment_list* segment_list,
|
|
const Layout::Section_list* section_list,
|
|
const Layout::Section_list* unattached_section_list,
|
|
const Stringpool* secnamepool,
|
|
const Output_section* shstrtab_section)
|
|
: layout_(layout),
|
|
segment_list_(segment_list),
|
|
section_list_(section_list),
|
|
unattached_section_list_(unattached_section_list),
|
|
secnamepool_(secnamepool),
|
|
shstrtab_section_(shstrtab_section)
|
|
{
|
|
}
|
|
|
|
// Compute the current data size.
|
|
|
|
off_t
|
|
Output_section_headers::do_size() const
|
|
{
|
|
// Count all the sections. Start with 1 for the null section.
|
|
off_t count = 1;
|
|
if (!parameters->options().relocatable())
|
|
{
|
|
for (Layout::Segment_list::const_iterator p =
|
|
this->segment_list_->begin();
|
|
p != this->segment_list_->end();
|
|
++p)
|
|
if ((*p)->type() == elfcpp::PT_LOAD)
|
|
count += (*p)->output_section_count();
|
|
}
|
|
else
|
|
{
|
|
for (Layout::Section_list::const_iterator p =
|
|
this->section_list_->begin();
|
|
p != this->section_list_->end();
|
|
++p)
|
|
if (((*p)->flags() & elfcpp::SHF_ALLOC) != 0)
|
|
++count;
|
|
}
|
|
count += this->unattached_section_list_->size();
|
|
|
|
const int size = parameters->target().get_size();
|
|
int shdr_size;
|
|
if (size == 32)
|
|
shdr_size = elfcpp::Elf_sizes<32>::shdr_size;
|
|
else if (size == 64)
|
|
shdr_size = elfcpp::Elf_sizes<64>::shdr_size;
|
|
else
|
|
gold_unreachable();
|
|
|
|
return count * shdr_size;
|
|
}
|
|
|
|
// Write out the section headers.
|
|
|
|
void
|
|
Output_section_headers::do_write(Output_file* of)
|
|
{
|
|
switch (parameters->size_and_endianness())
|
|
{
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
case Parameters::TARGET_32_LITTLE:
|
|
this->do_sized_write<32, false>(of);
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
case Parameters::TARGET_32_BIG:
|
|
this->do_sized_write<32, true>(of);
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
case Parameters::TARGET_64_LITTLE:
|
|
this->do_sized_write<64, false>(of);
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
case Parameters::TARGET_64_BIG:
|
|
this->do_sized_write<64, true>(of);
|
|
break;
|
|
#endif
|
|
default:
|
|
gold_unreachable();
|
|
}
|
|
}
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Output_section_headers::do_sized_write(Output_file* of)
|
|
{
|
|
off_t all_shdrs_size = this->data_size();
|
|
unsigned char* view = of->get_output_view(this->offset(), all_shdrs_size);
|
|
|
|
const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
|
|
unsigned char* v = view;
|
|
|
|
{
|
|
typename elfcpp::Shdr_write<size, big_endian> oshdr(v);
|
|
oshdr.put_sh_name(0);
|
|
oshdr.put_sh_type(elfcpp::SHT_NULL);
|
|
oshdr.put_sh_flags(0);
|
|
oshdr.put_sh_addr(0);
|
|
oshdr.put_sh_offset(0);
|
|
|
|
size_t section_count = (this->data_size()
|
|
/ elfcpp::Elf_sizes<size>::shdr_size);
|
|
if (section_count < elfcpp::SHN_LORESERVE)
|
|
oshdr.put_sh_size(0);
|
|
else
|
|
oshdr.put_sh_size(section_count);
|
|
|
|
unsigned int shstrndx = this->shstrtab_section_->out_shndx();
|
|
if (shstrndx < elfcpp::SHN_LORESERVE)
|
|
oshdr.put_sh_link(0);
|
|
else
|
|
oshdr.put_sh_link(shstrndx);
|
|
|
|
size_t segment_count = this->segment_list_->size();
|
|
oshdr.put_sh_info(segment_count >= elfcpp::PN_XNUM ? segment_count : 0);
|
|
|
|
oshdr.put_sh_addralign(0);
|
|
oshdr.put_sh_entsize(0);
|
|
}
|
|
|
|
v += shdr_size;
|
|
|
|
unsigned int shndx = 1;
|
|
if (!parameters->options().relocatable())
|
|
{
|
|
for (Layout::Segment_list::const_iterator p =
|
|
this->segment_list_->begin();
|
|
p != this->segment_list_->end();
|
|
++p)
|
|
v = (*p)->write_section_headers<size, big_endian>(this->layout_,
|
|
this->secnamepool_,
|
|
v,
|
|
&shndx);
|
|
}
|
|
else
|
|
{
|
|
for (Layout::Section_list::const_iterator p =
|
|
this->section_list_->begin();
|
|
p != this->section_list_->end();
|
|
++p)
|
|
{
|
|
// We do unallocated sections below, except that group
|
|
// sections have to come first.
|
|
if (((*p)->flags() & elfcpp::SHF_ALLOC) == 0
|
|
&& (*p)->type() != elfcpp::SHT_GROUP)
|
|
continue;
|
|
gold_assert(shndx == (*p)->out_shndx());
|
|
elfcpp::Shdr_write<size, big_endian> oshdr(v);
|
|
(*p)->write_header(this->layout_, this->secnamepool_, &oshdr);
|
|
v += shdr_size;
|
|
++shndx;
|
|
}
|
|
}
|
|
|
|
for (Layout::Section_list::const_iterator p =
|
|
this->unattached_section_list_->begin();
|
|
p != this->unattached_section_list_->end();
|
|
++p)
|
|
{
|
|
// For a relocatable link, we did unallocated group sections
|
|
// above, since they have to come first.
|
|
if ((*p)->type() == elfcpp::SHT_GROUP
|
|
&& parameters->options().relocatable())
|
|
continue;
|
|
gold_assert(shndx == (*p)->out_shndx());
|
|
elfcpp::Shdr_write<size, big_endian> oshdr(v);
|
|
(*p)->write_header(this->layout_, this->secnamepool_, &oshdr);
|
|
v += shdr_size;
|
|
++shndx;
|
|
}
|
|
|
|
of->write_output_view(this->offset(), all_shdrs_size, view);
|
|
}
|
|
|
|
// Output_segment_header methods.
|
|
|
|
Output_segment_headers::Output_segment_headers(
|
|
const Layout::Segment_list& segment_list)
|
|
: segment_list_(segment_list)
|
|
{
|
|
this->set_current_data_size_for_child(this->do_size());
|
|
}
|
|
|
|
void
|
|
Output_segment_headers::do_write(Output_file* of)
|
|
{
|
|
switch (parameters->size_and_endianness())
|
|
{
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
case Parameters::TARGET_32_LITTLE:
|
|
this->do_sized_write<32, false>(of);
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
case Parameters::TARGET_32_BIG:
|
|
this->do_sized_write<32, true>(of);
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
case Parameters::TARGET_64_LITTLE:
|
|
this->do_sized_write<64, false>(of);
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
case Parameters::TARGET_64_BIG:
|
|
this->do_sized_write<64, true>(of);
|
|
break;
|
|
#endif
|
|
default:
|
|
gold_unreachable();
|
|
}
|
|
}
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Output_segment_headers::do_sized_write(Output_file* of)
|
|
{
|
|
const int phdr_size = elfcpp::Elf_sizes<size>::phdr_size;
|
|
off_t all_phdrs_size = this->segment_list_.size() * phdr_size;
|
|
gold_assert(all_phdrs_size == this->data_size());
|
|
unsigned char* view = of->get_output_view(this->offset(),
|
|
all_phdrs_size);
|
|
unsigned char* v = view;
|
|
for (Layout::Segment_list::const_iterator p = this->segment_list_.begin();
|
|
p != this->segment_list_.end();
|
|
++p)
|
|
{
|
|
elfcpp::Phdr_write<size, big_endian> ophdr(v);
|
|
(*p)->write_header(&ophdr);
|
|
v += phdr_size;
|
|
}
|
|
|
|
gold_assert(v - view == all_phdrs_size);
|
|
|
|
of->write_output_view(this->offset(), all_phdrs_size, view);
|
|
}
|
|
|
|
off_t
|
|
Output_segment_headers::do_size() const
|
|
{
|
|
const int size = parameters->target().get_size();
|
|
int phdr_size;
|
|
if (size == 32)
|
|
phdr_size = elfcpp::Elf_sizes<32>::phdr_size;
|
|
else if (size == 64)
|
|
phdr_size = elfcpp::Elf_sizes<64>::phdr_size;
|
|
else
|
|
gold_unreachable();
|
|
|
|
return this->segment_list_.size() * phdr_size;
|
|
}
|
|
|
|
// Output_file_header methods.
|
|
|
|
Output_file_header::Output_file_header(Target* target,
|
|
const Symbol_table* symtab,
|
|
const Output_segment_headers* osh)
|
|
: target_(target),
|
|
symtab_(symtab),
|
|
segment_header_(osh),
|
|
section_header_(NULL),
|
|
shstrtab_(NULL)
|
|
{
|
|
this->set_data_size(this->do_size());
|
|
}
|
|
|
|
// Set the section table information for a file header.
|
|
|
|
void
|
|
Output_file_header::set_section_info(const Output_section_headers* shdrs,
|
|
const Output_section* shstrtab)
|
|
{
|
|
this->section_header_ = shdrs;
|
|
this->shstrtab_ = shstrtab;
|
|
}
|
|
|
|
// Write out the file header.
|
|
|
|
void
|
|
Output_file_header::do_write(Output_file* of)
|
|
{
|
|
gold_assert(this->offset() == 0);
|
|
|
|
switch (parameters->size_and_endianness())
|
|
{
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
case Parameters::TARGET_32_LITTLE:
|
|
this->do_sized_write<32, false>(of);
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
case Parameters::TARGET_32_BIG:
|
|
this->do_sized_write<32, true>(of);
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
case Parameters::TARGET_64_LITTLE:
|
|
this->do_sized_write<64, false>(of);
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
case Parameters::TARGET_64_BIG:
|
|
this->do_sized_write<64, true>(of);
|
|
break;
|
|
#endif
|
|
default:
|
|
gold_unreachable();
|
|
}
|
|
}
|
|
|
|
// Write out the file header with appropriate size and endianness.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Output_file_header::do_sized_write(Output_file* of)
|
|
{
|
|
gold_assert(this->offset() == 0);
|
|
|
|
int ehdr_size = elfcpp::Elf_sizes<size>::ehdr_size;
|
|
unsigned char* view = of->get_output_view(0, ehdr_size);
|
|
elfcpp::Ehdr_write<size, big_endian> oehdr(view);
|
|
|
|
unsigned char e_ident[elfcpp::EI_NIDENT];
|
|
memset(e_ident, 0, elfcpp::EI_NIDENT);
|
|
e_ident[elfcpp::EI_MAG0] = elfcpp::ELFMAG0;
|
|
e_ident[elfcpp::EI_MAG1] = elfcpp::ELFMAG1;
|
|
e_ident[elfcpp::EI_MAG2] = elfcpp::ELFMAG2;
|
|
e_ident[elfcpp::EI_MAG3] = elfcpp::ELFMAG3;
|
|
if (size == 32)
|
|
e_ident[elfcpp::EI_CLASS] = elfcpp::ELFCLASS32;
|
|
else if (size == 64)
|
|
e_ident[elfcpp::EI_CLASS] = elfcpp::ELFCLASS64;
|
|
else
|
|
gold_unreachable();
|
|
e_ident[elfcpp::EI_DATA] = (big_endian
|
|
? elfcpp::ELFDATA2MSB
|
|
: elfcpp::ELFDATA2LSB);
|
|
e_ident[elfcpp::EI_VERSION] = elfcpp::EV_CURRENT;
|
|
oehdr.put_e_ident(e_ident);
|
|
|
|
elfcpp::ET e_type;
|
|
if (parameters->options().relocatable())
|
|
e_type = elfcpp::ET_REL;
|
|
else if (parameters->options().output_is_position_independent())
|
|
e_type = elfcpp::ET_DYN;
|
|
else
|
|
e_type = elfcpp::ET_EXEC;
|
|
oehdr.put_e_type(e_type);
|
|
|
|
oehdr.put_e_machine(this->target_->machine_code());
|
|
oehdr.put_e_version(elfcpp::EV_CURRENT);
|
|
|
|
oehdr.put_e_entry(this->entry<size>());
|
|
|
|
if (this->segment_header_ == NULL)
|
|
oehdr.put_e_phoff(0);
|
|
else
|
|
oehdr.put_e_phoff(this->segment_header_->offset());
|
|
|
|
oehdr.put_e_shoff(this->section_header_->offset());
|
|
oehdr.put_e_flags(this->target_->processor_specific_flags());
|
|
oehdr.put_e_ehsize(elfcpp::Elf_sizes<size>::ehdr_size);
|
|
|
|
if (this->segment_header_ == NULL)
|
|
{
|
|
oehdr.put_e_phentsize(0);
|
|
oehdr.put_e_phnum(0);
|
|
}
|
|
else
|
|
{
|
|
oehdr.put_e_phentsize(elfcpp::Elf_sizes<size>::phdr_size);
|
|
size_t phnum = (this->segment_header_->data_size()
|
|
/ elfcpp::Elf_sizes<size>::phdr_size);
|
|
if (phnum > elfcpp::PN_XNUM)
|
|
phnum = elfcpp::PN_XNUM;
|
|
oehdr.put_e_phnum(phnum);
|
|
}
|
|
|
|
oehdr.put_e_shentsize(elfcpp::Elf_sizes<size>::shdr_size);
|
|
size_t section_count = (this->section_header_->data_size()
|
|
/ elfcpp::Elf_sizes<size>::shdr_size);
|
|
|
|
if (section_count < elfcpp::SHN_LORESERVE)
|
|
oehdr.put_e_shnum(this->section_header_->data_size()
|
|
/ elfcpp::Elf_sizes<size>::shdr_size);
|
|
else
|
|
oehdr.put_e_shnum(0);
|
|
|
|
unsigned int shstrndx = this->shstrtab_->out_shndx();
|
|
if (shstrndx < elfcpp::SHN_LORESERVE)
|
|
oehdr.put_e_shstrndx(this->shstrtab_->out_shndx());
|
|
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.
|
|
this->target_->adjust_elf_header(view, ehdr_size);
|
|
|
|
of->write_output_view(0, ehdr_size, view);
|
|
}
|
|
|
|
// Return the value to use for the entry address.
|
|
|
|
template<int size>
|
|
typename elfcpp::Elf_types<size>::Elf_Addr
|
|
Output_file_header::entry()
|
|
{
|
|
const bool should_issue_warning = (parameters->options().entry() != NULL
|
|
&& !parameters->options().relocatable()
|
|
&& !parameters->options().shared());
|
|
const char* entry = parameters->entry();
|
|
Symbol* sym = this->symtab_->lookup(entry);
|
|
|
|
typename Sized_symbol<size>::Value_type v;
|
|
if (sym != NULL)
|
|
{
|
|
Sized_symbol<size>* ssym;
|
|
ssym = this->symtab_->get_sized_symbol<size>(sym);
|
|
if (!ssym->is_defined() && should_issue_warning)
|
|
gold_warning("entry symbol '%s' exists but is not defined", entry);
|
|
v = ssym->value();
|
|
}
|
|
else
|
|
{
|
|
// We couldn't find the entry symbol. See if we can parse it as
|
|
// a number. This supports, e.g., -e 0x1000.
|
|
char* endptr;
|
|
v = strtoull(entry, &endptr, 0);
|
|
if (*endptr != '\0')
|
|
{
|
|
if (should_issue_warning)
|
|
gold_warning("cannot find entry symbol '%s'", entry);
|
|
v = 0;
|
|
}
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
// Compute the current data size.
|
|
|
|
off_t
|
|
Output_file_header::do_size() const
|
|
{
|
|
const int size = parameters->target().get_size();
|
|
if (size == 32)
|
|
return elfcpp::Elf_sizes<32>::ehdr_size;
|
|
else if (size == 64)
|
|
return elfcpp::Elf_sizes<64>::ehdr_size;
|
|
else
|
|
gold_unreachable();
|
|
}
|
|
|
|
// Output_data_const methods.
|
|
|
|
void
|
|
Output_data_const::do_write(Output_file* of)
|
|
{
|
|
of->write(this->offset(), this->data_.data(), this->data_.size());
|
|
}
|
|
|
|
// Output_data_const_buffer methods.
|
|
|
|
void
|
|
Output_data_const_buffer::do_write(Output_file* of)
|
|
{
|
|
of->write(this->offset(), this->p_, this->data_size());
|
|
}
|
|
|
|
// Output_section_data methods.
|
|
|
|
// Record the output section, and set the entry size and such.
|
|
|
|
void
|
|
Output_section_data::set_output_section(Output_section* os)
|
|
{
|
|
gold_assert(this->output_section_ == NULL);
|
|
this->output_section_ = os;
|
|
this->do_adjust_output_section(os);
|
|
}
|
|
|
|
// Return the section index of the output section.
|
|
|
|
unsigned int
|
|
Output_section_data::do_out_shndx() const
|
|
{
|
|
gold_assert(this->output_section_ != NULL);
|
|
return this->output_section_->out_shndx();
|
|
}
|
|
|
|
// Set the alignment, which means we may need to update the alignment
|
|
// of the output section.
|
|
|
|
void
|
|
Output_section_data::set_addralign(uint64_t addralign)
|
|
{
|
|
this->addralign_ = addralign;
|
|
if (this->output_section_ != NULL
|
|
&& this->output_section_->addralign() < addralign)
|
|
this->output_section_->set_addralign(addralign);
|
|
}
|
|
|
|
// Output_data_strtab methods.
|
|
|
|
// Set the final data size.
|
|
|
|
void
|
|
Output_data_strtab::set_final_data_size()
|
|
{
|
|
this->strtab_->set_string_offsets();
|
|
this->set_data_size(this->strtab_->get_strtab_size());
|
|
}
|
|
|
|
// Write out a string table.
|
|
|
|
void
|
|
Output_data_strtab::do_write(Output_file* of)
|
|
{
|
|
this->strtab_->write(of, this->offset());
|
|
}
|
|
|
|
// Output_reloc methods.
|
|
|
|
// A reloc against a global symbol.
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
|
|
Symbol* gsym,
|
|
unsigned int type,
|
|
Output_data* od,
|
|
Address address,
|
|
bool is_relative,
|
|
bool is_symbolless,
|
|
bool use_plt_offset)
|
|
: address_(address), local_sym_index_(GSYM_CODE), type_(type),
|
|
is_relative_(is_relative), is_symbolless_(is_symbolless),
|
|
is_section_symbol_(false), use_plt_offset_(use_plt_offset), shndx_(INVALID_CODE)
|
|
{
|
|
// this->type_ is a bitfield; make sure TYPE fits.
|
|
gold_assert(this->type_ == type);
|
|
this->u1_.gsym = gsym;
|
|
this->u2_.od = od;
|
|
if (dynamic)
|
|
this->set_needs_dynsym_index();
|
|
}
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
|
|
Symbol* gsym,
|
|
unsigned int type,
|
|
Sized_relobj<size, big_endian>* relobj,
|
|
unsigned int shndx,
|
|
Address address,
|
|
bool is_relative,
|
|
bool is_symbolless,
|
|
bool use_plt_offset)
|
|
: address_(address), local_sym_index_(GSYM_CODE), type_(type),
|
|
is_relative_(is_relative), is_symbolless_(is_symbolless),
|
|
is_section_symbol_(false), use_plt_offset_(use_plt_offset), shndx_(shndx)
|
|
{
|
|
gold_assert(shndx != INVALID_CODE);
|
|
// this->type_ is a bitfield; make sure TYPE fits.
|
|
gold_assert(this->type_ == type);
|
|
this->u1_.gsym = gsym;
|
|
this->u2_.relobj = relobj;
|
|
if (dynamic)
|
|
this->set_needs_dynsym_index();
|
|
}
|
|
|
|
// A reloc against a local symbol.
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
|
|
Sized_relobj<size, big_endian>* relobj,
|
|
unsigned int local_sym_index,
|
|
unsigned int type,
|
|
Output_data* od,
|
|
Address address,
|
|
bool is_relative,
|
|
bool is_symbolless,
|
|
bool is_section_symbol,
|
|
bool use_plt_offset)
|
|
: address_(address), local_sym_index_(local_sym_index), type_(type),
|
|
is_relative_(is_relative), is_symbolless_(is_symbolless),
|
|
is_section_symbol_(is_section_symbol), use_plt_offset_(use_plt_offset),
|
|
shndx_(INVALID_CODE)
|
|
{
|
|
gold_assert(local_sym_index != GSYM_CODE
|
|
&& local_sym_index != INVALID_CODE);
|
|
// this->type_ is a bitfield; make sure TYPE fits.
|
|
gold_assert(this->type_ == type);
|
|
this->u1_.relobj = relobj;
|
|
this->u2_.od = od;
|
|
if (dynamic)
|
|
this->set_needs_dynsym_index();
|
|
}
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
|
|
Sized_relobj<size, big_endian>* relobj,
|
|
unsigned int local_sym_index,
|
|
unsigned int type,
|
|
unsigned int shndx,
|
|
Address address,
|
|
bool is_relative,
|
|
bool is_symbolless,
|
|
bool is_section_symbol,
|
|
bool use_plt_offset)
|
|
: address_(address), local_sym_index_(local_sym_index), type_(type),
|
|
is_relative_(is_relative), is_symbolless_(is_symbolless),
|
|
is_section_symbol_(is_section_symbol), use_plt_offset_(use_plt_offset),
|
|
shndx_(shndx)
|
|
{
|
|
gold_assert(local_sym_index != GSYM_CODE
|
|
&& local_sym_index != INVALID_CODE);
|
|
gold_assert(shndx != INVALID_CODE);
|
|
// this->type_ is a bitfield; make sure TYPE fits.
|
|
gold_assert(this->type_ == type);
|
|
this->u1_.relobj = relobj;
|
|
this->u2_.relobj = relobj;
|
|
if (dynamic)
|
|
this->set_needs_dynsym_index();
|
|
}
|
|
|
|
// A reloc against the STT_SECTION symbol of an output section.
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
|
|
Output_section* os,
|
|
unsigned int type,
|
|
Output_data* od,
|
|
Address address,
|
|
bool is_relative)
|
|
: address_(address), local_sym_index_(SECTION_CODE), type_(type),
|
|
is_relative_(is_relative), is_symbolless_(is_relative),
|
|
is_section_symbol_(true), use_plt_offset_(false), shndx_(INVALID_CODE)
|
|
{
|
|
// this->type_ is a bitfield; make sure TYPE fits.
|
|
gold_assert(this->type_ == type);
|
|
this->u1_.os = os;
|
|
this->u2_.od = od;
|
|
if (dynamic)
|
|
this->set_needs_dynsym_index();
|
|
else
|
|
os->set_needs_symtab_index();
|
|
}
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
|
|
Output_section* os,
|
|
unsigned int type,
|
|
Sized_relobj<size, big_endian>* relobj,
|
|
unsigned int shndx,
|
|
Address address,
|
|
bool is_relative)
|
|
: address_(address), local_sym_index_(SECTION_CODE), type_(type),
|
|
is_relative_(is_relative), is_symbolless_(is_relative),
|
|
is_section_symbol_(true), use_plt_offset_(false), shndx_(shndx)
|
|
{
|
|
gold_assert(shndx != INVALID_CODE);
|
|
// this->type_ is a bitfield; make sure TYPE fits.
|
|
gold_assert(this->type_ == type);
|
|
this->u1_.os = os;
|
|
this->u2_.relobj = relobj;
|
|
if (dynamic)
|
|
this->set_needs_dynsym_index();
|
|
else
|
|
os->set_needs_symtab_index();
|
|
}
|
|
|
|
// An absolute or relative relocation.
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
|
|
unsigned int type,
|
|
Output_data* od,
|
|
Address address,
|
|
bool is_relative)
|
|
: address_(address), local_sym_index_(0), type_(type),
|
|
is_relative_(is_relative), is_symbolless_(false),
|
|
is_section_symbol_(false), use_plt_offset_(false), shndx_(INVALID_CODE)
|
|
{
|
|
// this->type_ is a bitfield; make sure TYPE fits.
|
|
gold_assert(this->type_ == type);
|
|
this->u1_.relobj = NULL;
|
|
this->u2_.od = od;
|
|
}
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
|
|
unsigned int type,
|
|
Sized_relobj<size, big_endian>* relobj,
|
|
unsigned int shndx,
|
|
Address address,
|
|
bool is_relative)
|
|
: address_(address), local_sym_index_(0), type_(type),
|
|
is_relative_(is_relative), is_symbolless_(false),
|
|
is_section_symbol_(false), use_plt_offset_(false), shndx_(shndx)
|
|
{
|
|
gold_assert(shndx != INVALID_CODE);
|
|
// this->type_ is a bitfield; make sure TYPE fits.
|
|
gold_assert(this->type_ == type);
|
|
this->u1_.relobj = NULL;
|
|
this->u2_.relobj = relobj;
|
|
}
|
|
|
|
// A target specific relocation.
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
|
|
unsigned int type,
|
|
void* arg,
|
|
Output_data* od,
|
|
Address address)
|
|
: address_(address), local_sym_index_(TARGET_CODE), type_(type),
|
|
is_relative_(false), is_symbolless_(false),
|
|
is_section_symbol_(false), use_plt_offset_(false), shndx_(INVALID_CODE)
|
|
{
|
|
// this->type_ is a bitfield; make sure TYPE fits.
|
|
gold_assert(this->type_ == type);
|
|
this->u1_.arg = arg;
|
|
this->u2_.od = od;
|
|
}
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::Output_reloc(
|
|
unsigned int type,
|
|
void* arg,
|
|
Sized_relobj<size, big_endian>* relobj,
|
|
unsigned int shndx,
|
|
Address address)
|
|
: address_(address), local_sym_index_(TARGET_CODE), type_(type),
|
|
is_relative_(false), is_symbolless_(false),
|
|
is_section_symbol_(false), use_plt_offset_(false), shndx_(shndx)
|
|
{
|
|
gold_assert(shndx != INVALID_CODE);
|
|
// this->type_ is a bitfield; make sure TYPE fits.
|
|
gold_assert(this->type_ == type);
|
|
this->u1_.arg = arg;
|
|
this->u2_.relobj = relobj;
|
|
}
|
|
|
|
// Record that we need a dynamic symbol index for this relocation.
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
void
|
|
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::
|
|
set_needs_dynsym_index()
|
|
{
|
|
if (this->is_symbolless_)
|
|
return;
|
|
switch (this->local_sym_index_)
|
|
{
|
|
case INVALID_CODE:
|
|
gold_unreachable();
|
|
|
|
case GSYM_CODE:
|
|
this->u1_.gsym->set_needs_dynsym_entry();
|
|
break;
|
|
|
|
case SECTION_CODE:
|
|
this->u1_.os->set_needs_dynsym_index();
|
|
break;
|
|
|
|
case TARGET_CODE:
|
|
// The target must take care of this if necessary.
|
|
break;
|
|
|
|
case 0:
|
|
break;
|
|
|
|
default:
|
|
{
|
|
const unsigned int lsi = this->local_sym_index_;
|
|
Sized_relobj_file<size, big_endian>* relobj =
|
|
this->u1_.relobj->sized_relobj();
|
|
gold_assert(relobj != NULL);
|
|
if (!this->is_section_symbol_)
|
|
relobj->set_needs_output_dynsym_entry(lsi);
|
|
else
|
|
relobj->output_section(lsi)->set_needs_dynsym_index();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Get the symbol index of a relocation.
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
unsigned int
|
|
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::get_symbol_index()
|
|
const
|
|
{
|
|
unsigned int index;
|
|
if (this->is_symbolless_)
|
|
return 0;
|
|
switch (this->local_sym_index_)
|
|
{
|
|
case INVALID_CODE:
|
|
gold_unreachable();
|
|
|
|
case GSYM_CODE:
|
|
if (this->u1_.gsym == NULL)
|
|
index = 0;
|
|
else if (dynamic)
|
|
index = this->u1_.gsym->dynsym_index();
|
|
else
|
|
index = this->u1_.gsym->symtab_index();
|
|
break;
|
|
|
|
case SECTION_CODE:
|
|
if (dynamic)
|
|
index = this->u1_.os->dynsym_index();
|
|
else
|
|
index = this->u1_.os->symtab_index();
|
|
break;
|
|
|
|
case TARGET_CODE:
|
|
index = parameters->target().reloc_symbol_index(this->u1_.arg,
|
|
this->type_);
|
|
break;
|
|
|
|
case 0:
|
|
// Relocations without symbols use a symbol index of 0.
|
|
index = 0;
|
|
break;
|
|
|
|
default:
|
|
{
|
|
const unsigned int lsi = this->local_sym_index_;
|
|
Sized_relobj_file<size, big_endian>* relobj =
|
|
this->u1_.relobj->sized_relobj();
|
|
gold_assert(relobj != NULL);
|
|
if (!this->is_section_symbol_)
|
|
{
|
|
if (dynamic)
|
|
index = relobj->dynsym_index(lsi);
|
|
else
|
|
index = relobj->symtab_index(lsi);
|
|
}
|
|
else
|
|
{
|
|
Output_section* os = relobj->output_section(lsi);
|
|
gold_assert(os != NULL);
|
|
if (dynamic)
|
|
index = os->dynsym_index();
|
|
else
|
|
index = os->symtab_index();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
gold_assert(index != -1U);
|
|
return index;
|
|
}
|
|
|
|
// For a local section symbol, get the address of the offset ADDEND
|
|
// within the input section.
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
typename elfcpp::Elf_types<size>::Elf_Addr
|
|
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::
|
|
local_section_offset(Addend addend) const
|
|
{
|
|
gold_assert(this->local_sym_index_ != GSYM_CODE
|
|
&& this->local_sym_index_ != SECTION_CODE
|
|
&& this->local_sym_index_ != TARGET_CODE
|
|
&& this->local_sym_index_ != INVALID_CODE
|
|
&& this->local_sym_index_ != 0
|
|
&& this->is_section_symbol_);
|
|
const unsigned int lsi = this->local_sym_index_;
|
|
Output_section* os = this->u1_.relobj->output_section(lsi);
|
|
gold_assert(os != NULL);
|
|
Address offset = this->u1_.relobj->get_output_section_offset(lsi);
|
|
if (offset != invalid_address)
|
|
return offset + addend;
|
|
// This is a merge section.
|
|
Sized_relobj_file<size, big_endian>* relobj =
|
|
this->u1_.relobj->sized_relobj();
|
|
gold_assert(relobj != NULL);
|
|
offset = os->output_address(relobj, lsi, addend);
|
|
gold_assert(offset != invalid_address);
|
|
return offset;
|
|
}
|
|
|
|
// Get the output address of a relocation.
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
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)
|
|
{
|
|
Output_section* os = this->u2_.relobj->output_section(this->shndx_);
|
|
gold_assert(os != NULL);
|
|
Address off = this->u2_.relobj->get_output_section_offset(this->shndx_);
|
|
if (off != invalid_address)
|
|
address += os->address() + off;
|
|
else
|
|
{
|
|
Sized_relobj_file<size, big_endian>* relobj =
|
|
this->u2_.relobj->sized_relobj();
|
|
gold_assert(relobj != NULL);
|
|
address = os->output_address(relobj, this->shndx_, address);
|
|
gold_assert(address != invalid_address);
|
|
}
|
|
}
|
|
else if (this->u2_.od != NULL)
|
|
address += this->u2_.od->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->get_symbol_index();
|
|
wr->put_r_info(elfcpp::elf_r_info<size>(sym_index, this->type_));
|
|
}
|
|
|
|
// Write out a Rel relocation.
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
void
|
|
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::write(
|
|
unsigned char* pov) const
|
|
{
|
|
elfcpp::Rel_write<size, big_endian> orel(pov);
|
|
this->write_rel(&orel);
|
|
}
|
|
|
|
// Get the value of the symbol referred to by a Rel relocation.
|
|
|
|
template<bool dynamic, int size, bool big_endian>
|
|
typename elfcpp::Elf_types<size>::Elf_Addr
|
|
Output_reloc<elfcpp::SHT_REL, dynamic, size, big_endian>::symbol_value(
|
|
Addend addend) const
|
|
{
|
|
if (this->local_sym_index_ == GSYM_CODE)
|
|
{
|
|
const Sized_symbol<size>* sym;
|
|
sym = static_cast<const Sized_symbol<size>*>(this->u1_.gsym);
|
|
if (this->use_plt_offset_ && sym->has_plt_offset())
|
|
return parameters->target().plt_address_for_global(sym);
|
|
else
|
|
return sym->value() + addend;
|
|
}
|
|
if (this->local_sym_index_ == SECTION_CODE)
|
|
{
|
|
gold_assert(!this->use_plt_offset_);
|
|
return this->u1_.os->address() + addend;
|
|
}
|
|
gold_assert(this->local_sym_index_ != TARGET_CODE
|
|
&& this->local_sym_index_ != INVALID_CODE
|
|
&& this->local_sym_index_ != 0
|
|
&& !this->is_section_symbol_);
|
|
const unsigned int lsi = this->local_sym_index_;
|
|
Sized_relobj_file<size, big_endian>* relobj =
|
|
this->u1_.relobj->sized_relobj();
|
|
gold_assert(relobj != NULL);
|
|
if (this->use_plt_offset_)
|
|
return parameters->target().plt_address_for_local(relobj, lsi);
|
|
const Symbol_value<size>* symval = relobj->local_symbol(lsi);
|
|
return symval->value(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>
|
|
void
|
|
Output_reloc<elfcpp::SHT_RELA, dynamic, size, big_endian>::write(
|
|
unsigned char* pov) const
|
|
{
|
|
elfcpp::Rela_write<size, big_endian> orel(pov);
|
|
this->rel_.write_rel(&orel);
|
|
Addend addend = this->addend_;
|
|
if (this->rel_.is_target_specific())
|
|
addend = parameters->target().reloc_addend(this->rel_.target_arg(),
|
|
this->rel_.type(), addend);
|
|
else if (this->rel_.is_symbolless())
|
|
addend = this->rel_.symbol_value(addend);
|
|
else if (this->rel_.is_local_section_symbol())
|
|
addend = this->rel_.local_section_offset(addend);
|
|
orel.put_r_addend(addend);
|
|
}
|
|
|
|
// Output_data_reloc_base methods.
|
|
|
|
// Adjust the output section.
|
|
|
|
template<int sh_type, bool dynamic, int size, bool big_endian>
|
|
void
|
|
Output_data_reloc_base<sh_type, dynamic, size, big_endian>
|
|
::do_adjust_output_section(Output_section* os)
|
|
{
|
|
if (sh_type == elfcpp::SHT_REL)
|
|
os->set_entsize(elfcpp::Elf_sizes<size>::rel_size);
|
|
else if (sh_type == elfcpp::SHT_RELA)
|
|
os->set_entsize(elfcpp::Elf_sizes<size>::rela_size);
|
|
else
|
|
gold_unreachable();
|
|
|
|
// A STT_GNU_IFUNC symbol may require a IRELATIVE reloc when doing a
|
|
// static link. The backends will generate a dynamic reloc section
|
|
// to hold this. In that case we don't want to link to the dynsym
|
|
// section, because there isn't one.
|
|
if (!dynamic)
|
|
os->set_should_link_to_symtab();
|
|
else if (parameters->doing_static_link())
|
|
;
|
|
else
|
|
os->set_should_link_to_dynsym();
|
|
}
|
|
|
|
// Standard relocation writer, which just calls Output_reloc::write().
|
|
|
|
template<int sh_type, bool dynamic, int size, bool big_endian>
|
|
struct Output_reloc_writer
|
|
{
|
|
typedef Output_reloc<sh_type, dynamic, size, big_endian> Output_reloc_type;
|
|
typedef std::vector<Output_reloc_type> Relocs;
|
|
|
|
static void
|
|
write(typename Relocs::const_iterator p, unsigned char* pov)
|
|
{ p->write(pov); }
|
|
};
|
|
|
|
// Write out relocation data.
|
|
|
|
template<int sh_type, bool dynamic, int size, bool big_endian>
|
|
void
|
|
Output_data_reloc_base<sh_type, dynamic, size, big_endian>::do_write(
|
|
Output_file* of)
|
|
{
|
|
typedef Output_reloc_writer<sh_type, dynamic, size, big_endian> Writer;
|
|
this->do_write_generic<Writer>(of);
|
|
}
|
|
|
|
// Class Output_relocatable_relocs.
|
|
|
|
template<int sh_type, int size, bool big_endian>
|
|
void
|
|
Output_relocatable_relocs<sh_type, size, big_endian>::set_final_data_size()
|
|
{
|
|
this->set_data_size(this->rr_->output_reloc_count()
|
|
* Reloc_types<sh_type, size, big_endian>::reloc_size);
|
|
}
|
|
|
|
// class Output_data_group.
|
|
|
|
template<int size, bool big_endian>
|
|
Output_data_group<size, big_endian>::Output_data_group(
|
|
Sized_relobj_file<size, big_endian>* relobj,
|
|
section_size_type entry_count,
|
|
elfcpp::Elf_Word flags,
|
|
std::vector<unsigned int>* input_shndxes)
|
|
: Output_section_data(entry_count * 4, 4, false),
|
|
relobj_(relobj),
|
|
flags_(flags)
|
|
{
|
|
this->input_shndxes_.swap(*input_shndxes);
|
|
}
|
|
|
|
// Write out the section group, which means translating the section
|
|
// indexes to apply to the output file.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Output_data_group<size, big_endian>::do_write(Output_file* of)
|
|
{
|
|
const off_t off = this->offset();
|
|
const section_size_type oview_size =
|
|
convert_to_section_size_type(this->data_size());
|
|
unsigned char* const oview = of->get_output_view(off, oview_size);
|
|
|
|
elfcpp::Elf_Word* contents = reinterpret_cast<elfcpp::Elf_Word*>(oview);
|
|
elfcpp::Swap<32, big_endian>::writeval(contents, this->flags_);
|
|
++contents;
|
|
|
|
for (std::vector<unsigned int>::const_iterator p =
|
|
this->input_shndxes_.begin();
|
|
p != this->input_shndxes_.end();
|
|
++p, ++contents)
|
|
{
|
|
Output_section* os = this->relobj_->output_section(*p);
|
|
|
|
unsigned int output_shndx;
|
|
if (os != NULL)
|
|
output_shndx = os->out_shndx();
|
|
else
|
|
{
|
|
this->relobj_->error(_("section group retained but "
|
|
"group element discarded"));
|
|
output_shndx = 0;
|
|
}
|
|
|
|
elfcpp::Swap<32, big_endian>::writeval(contents, output_shndx);
|
|
}
|
|
|
|
size_t wrote = reinterpret_cast<unsigned char*>(contents) - oview;
|
|
gold_assert(wrote == oview_size);
|
|
|
|
of->write_output_view(off, oview_size, oview);
|
|
|
|
// We no longer need this information.
|
|
this->input_shndxes_.clear();
|
|
}
|
|
|
|
// Output_data_got::Got_entry methods.
|
|
|
|
// Write out the entry.
|
|
|
|
template<int got_size, bool big_endian>
|
|
void
|
|
Output_data_got<got_size, big_endian>::Got_entry::write(
|
|
unsigned int got_indx,
|
|
unsigned char* pov) const
|
|
{
|
|
Valtype val = 0;
|
|
|
|
switch (this->local_sym_index_)
|
|
{
|
|
case GSYM_CODE:
|
|
{
|
|
// If the symbol is resolved locally, we need to write out the
|
|
// link-time value, which will be relocated dynamically by a
|
|
// RELATIVE relocation.
|
|
Symbol* gsym = this->u_.gsym;
|
|
if (this->use_plt_or_tls_offset_ && gsym->has_plt_offset())
|
|
val = parameters->target().plt_address_for_global(gsym);
|
|
else
|
|
{
|
|
switch (parameters->size_and_endianness())
|
|
{
|
|
#if defined(HAVE_TARGET_32_LITTLE) || defined(HAVE_TARGET_32_BIG)
|
|
case Parameters::TARGET_32_LITTLE:
|
|
case Parameters::TARGET_32_BIG:
|
|
{
|
|
// This cast is ugly. We don't want to put a
|
|
// virtual method in Symbol, because we want Symbol
|
|
// to be as small as possible.
|
|
Sized_symbol<32>::Value_type v;
|
|
v = static_cast<Sized_symbol<32>*>(gsym)->value();
|
|
val = convert_types<Valtype, Sized_symbol<32>::Value_type>(v);
|
|
}
|
|
break;
|
|
#endif
|
|
#if defined(HAVE_TARGET_64_LITTLE) || defined(HAVE_TARGET_64_BIG)
|
|
case Parameters::TARGET_64_LITTLE:
|
|
case Parameters::TARGET_64_BIG:
|
|
{
|
|
Sized_symbol<64>::Value_type v;
|
|
v = static_cast<Sized_symbol<64>*>(gsym)->value();
|
|
val = convert_types<Valtype, Sized_symbol<64>::Value_type>(v);
|
|
}
|
|
break;
|
|
#endif
|
|
default:
|
|
gold_unreachable();
|
|
}
|
|
if (this->use_plt_or_tls_offset_
|
|
&& gsym->type() == elfcpp::STT_TLS)
|
|
val += parameters->target().tls_offset_for_global(gsym,
|
|
got_indx);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case CONSTANT_CODE:
|
|
val = this->u_.constant;
|
|
break;
|
|
|
|
case RESERVED_CODE:
|
|
// If we're doing an incremental update, don't touch this GOT entry.
|
|
if (parameters->incremental_update())
|
|
return;
|
|
val = this->u_.constant;
|
|
break;
|
|
|
|
default:
|
|
{
|
|
const Relobj* object = this->u_.object;
|
|
const unsigned int lsi = this->local_sym_index_;
|
|
bool is_tls = object->local_is_tls(lsi);
|
|
if (this->use_plt_or_tls_offset_ && !is_tls)
|
|
val = parameters->target().plt_address_for_local(object, lsi);
|
|
else
|
|
{
|
|
uint64_t lval = object->local_symbol_value(lsi, this->addend_);
|
|
val = convert_types<Valtype, uint64_t>(lval);
|
|
if (this->use_plt_or_tls_offset_ && is_tls)
|
|
val += parameters->target().tls_offset_for_local(object, lsi,
|
|
got_indx);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
elfcpp::Swap<got_size, big_endian>::writeval(pov, val);
|
|
}
|
|
|
|
// Output_data_got methods.
|
|
|
|
// Add an entry for a global symbol to the GOT. This returns true if
|
|
// this is a new GOT entry, false if the symbol already had a GOT
|
|
// entry.
|
|
|
|
template<int got_size, bool big_endian>
|
|
bool
|
|
Output_data_got<got_size, big_endian>::add_global(
|
|
Symbol* gsym,
|
|
unsigned int got_type)
|
|
{
|
|
if (gsym->has_got_offset(got_type))
|
|
return false;
|
|
|
|
unsigned int got_offset = this->add_got_entry(Got_entry(gsym, false));
|
|
gsym->set_got_offset(got_type, got_offset);
|
|
return true;
|
|
}
|
|
|
|
// Like add_global, but use the PLT offset.
|
|
|
|
template<int got_size, bool big_endian>
|
|
bool
|
|
Output_data_got<got_size, big_endian>::add_global_plt(Symbol* gsym,
|
|
unsigned int got_type)
|
|
{
|
|
if (gsym->has_got_offset(got_type))
|
|
return false;
|
|
|
|
unsigned int got_offset = this->add_got_entry(Got_entry(gsym, true));
|
|
gsym->set_got_offset(got_type, got_offset);
|
|
return true;
|
|
}
|
|
|
|
// Add an entry for a global symbol to the GOT, and add a dynamic
|
|
// relocation of type R_TYPE for the GOT entry.
|
|
|
|
template<int got_size, bool big_endian>
|
|
void
|
|
Output_data_got<got_size, big_endian>::add_global_with_rel(
|
|
Symbol* gsym,
|
|
unsigned int got_type,
|
|
Output_data_reloc_generic* rel_dyn,
|
|
unsigned int r_type)
|
|
{
|
|
if (gsym->has_got_offset(got_type))
|
|
return;
|
|
|
|
unsigned int got_offset = this->add_got_entry(Got_entry());
|
|
gsym->set_got_offset(got_type, got_offset);
|
|
rel_dyn->add_global_generic(gsym, r_type, this, got_offset, 0);
|
|
}
|
|
|
|
// Add a pair of entries for a global symbol to the GOT, and add
|
|
// dynamic relocations of type R_TYPE_1 and R_TYPE_2, respectively.
|
|
// If R_TYPE_2 == 0, add the second entry with no relocation.
|
|
template<int got_size, bool big_endian>
|
|
void
|
|
Output_data_got<got_size, big_endian>::add_global_pair_with_rel(
|
|
Symbol* gsym,
|
|
unsigned int got_type,
|
|
Output_data_reloc_generic* rel_dyn,
|
|
unsigned int r_type_1,
|
|
unsigned int r_type_2)
|
|
{
|
|
if (gsym->has_got_offset(got_type))
|
|
return;
|
|
|
|
unsigned int got_offset = this->add_got_entry_pair(Got_entry(), Got_entry());
|
|
gsym->set_got_offset(got_type, got_offset);
|
|
rel_dyn->add_global_generic(gsym, r_type_1, this, got_offset, 0);
|
|
|
|
if (r_type_2 != 0)
|
|
rel_dyn->add_global_generic(gsym, r_type_2, this,
|
|
got_offset + got_size / 8, 0);
|
|
}
|
|
|
|
// Add an entry for a local symbol to the GOT. This returns true if
|
|
// this is a new GOT entry, false if the symbol already has a GOT
|
|
// entry.
|
|
|
|
template<int got_size, bool big_endian>
|
|
bool
|
|
Output_data_got<got_size, big_endian>::add_local(
|
|
Relobj* object,
|
|
unsigned int symndx,
|
|
unsigned int got_type)
|
|
{
|
|
if (object->local_has_got_offset(symndx, got_type))
|
|
return false;
|
|
|
|
unsigned int got_offset = this->add_got_entry(Got_entry(object, symndx,
|
|
false));
|
|
object->set_local_got_offset(symndx, got_type, got_offset);
|
|
return true;
|
|
}
|
|
|
|
// Add an entry for a local symbol plus ADDEND to the GOT. This returns
|
|
// true if this is a new GOT entry, false if the symbol already has a GOT
|
|
// entry.
|
|
|
|
template<int got_size, bool big_endian>
|
|
bool
|
|
Output_data_got<got_size, big_endian>::add_local(
|
|
Relobj* object,
|
|
unsigned int symndx,
|
|
unsigned int got_type,
|
|
uint64_t addend)
|
|
{
|
|
if (object->local_has_got_offset(symndx, got_type, addend))
|
|
return false;
|
|
|
|
unsigned int got_offset = this->add_got_entry(Got_entry(object, symndx,
|
|
false, addend));
|
|
object->set_local_got_offset(symndx, got_type, got_offset, addend);
|
|
return true;
|
|
}
|
|
|
|
// Like add_local, but use the PLT offset.
|
|
|
|
template<int got_size, bool big_endian>
|
|
bool
|
|
Output_data_got<got_size, big_endian>::add_local_plt(
|
|
Relobj* object,
|
|
unsigned int symndx,
|
|
unsigned int got_type)
|
|
{
|
|
if (object->local_has_got_offset(symndx, got_type))
|
|
return false;
|
|
|
|
unsigned int got_offset = this->add_got_entry(Got_entry(object, symndx,
|
|
true));
|
|
object->set_local_got_offset(symndx, got_type, got_offset);
|
|
return true;
|
|
}
|
|
|
|
// Add an entry for a local symbol to the GOT, and add a dynamic
|
|
// relocation of type R_TYPE for the GOT entry.
|
|
|
|
template<int got_size, bool big_endian>
|
|
void
|
|
Output_data_got<got_size, big_endian>::add_local_with_rel(
|
|
Relobj* object,
|
|
unsigned int symndx,
|
|
unsigned int got_type,
|
|
Output_data_reloc_generic* rel_dyn,
|
|
unsigned int r_type)
|
|
{
|
|
if (object->local_has_got_offset(symndx, got_type))
|
|
return;
|
|
|
|
unsigned int got_offset = this->add_got_entry(Got_entry());
|
|
object->set_local_got_offset(symndx, got_type, got_offset);
|
|
rel_dyn->add_local_generic(object, symndx, r_type, this, got_offset, 0);
|
|
}
|
|
|
|
// Add an entry for a local symbol plus ADDEND to the GOT, and add a dynamic
|
|
// relocation of type R_TYPE for the GOT entry.
|
|
|
|
template<int got_size, bool big_endian>
|
|
void
|
|
Output_data_got<got_size, big_endian>::add_local_with_rel(
|
|
Relobj* object,
|
|
unsigned int symndx,
|
|
unsigned int got_type,
|
|
Output_data_reloc_generic* rel_dyn,
|
|
unsigned int r_type, uint64_t addend)
|
|
{
|
|
if (object->local_has_got_offset(symndx, got_type, addend))
|
|
return;
|
|
|
|
unsigned int got_offset = this->add_got_entry(Got_entry());
|
|
object->set_local_got_offset(symndx, got_type, got_offset, addend);
|
|
rel_dyn->add_local_generic(object, symndx, r_type, this, got_offset,
|
|
addend);
|
|
}
|
|
|
|
// Add a pair of entries for a local symbol to the GOT, and add
|
|
// a dynamic relocation of type R_TYPE using the section symbol of
|
|
// the output section to which input section SHNDX maps, on the first.
|
|
// The first got entry will have a value of zero, the second the
|
|
// value of the local symbol.
|
|
template<int got_size, bool big_endian>
|
|
void
|
|
Output_data_got<got_size, big_endian>::add_local_pair_with_rel(
|
|
Relobj* object,
|
|
unsigned int symndx,
|
|
unsigned int shndx,
|
|
unsigned int got_type,
|
|
Output_data_reloc_generic* rel_dyn,
|
|
unsigned int r_type)
|
|
{
|
|
if (object->local_has_got_offset(symndx, got_type))
|
|
return;
|
|
|
|
unsigned int got_offset =
|
|
this->add_got_entry_pair(Got_entry(),
|
|
Got_entry(object, symndx, false));
|
|
object->set_local_got_offset(symndx, got_type, got_offset);
|
|
Output_section* os = object->output_section(shndx);
|
|
rel_dyn->add_output_section_generic(os, r_type, this, got_offset, 0);
|
|
}
|
|
|
|
// Add a pair of entries for a local symbol plus ADDEND to the GOT, and add
|
|
// a dynamic relocation of type R_TYPE using the section symbol of
|
|
// the output section to which input section SHNDX maps, on the first.
|
|
// The first got entry will have a value of zero, the second the
|
|
// value of the local symbol.
|
|
template<int got_size, bool big_endian>
|
|
void
|
|
Output_data_got<got_size, big_endian>::add_local_pair_with_rel(
|
|
Relobj* object,
|
|
unsigned int symndx,
|
|
unsigned int shndx,
|
|
unsigned int got_type,
|
|
Output_data_reloc_generic* rel_dyn,
|
|
unsigned int r_type, uint64_t addend)
|
|
{
|
|
if (object->local_has_got_offset(symndx, got_type, addend))
|
|
return;
|
|
|
|
unsigned int got_offset =
|
|
this->add_got_entry_pair(Got_entry(),
|
|
Got_entry(object, symndx, false, addend));
|
|
object->set_local_got_offset(symndx, got_type, got_offset, addend);
|
|
Output_section* os = object->output_section(shndx);
|
|
rel_dyn->add_output_section_generic(os, r_type, this, got_offset, addend);
|
|
}
|
|
|
|
// Add a pair of entries for a local symbol to the GOT, and add
|
|
// a dynamic relocation of type R_TYPE using STN_UNDEF on the first.
|
|
// The first got entry will have a value of zero, the second the
|
|
// value of the local symbol offset by Target::tls_offset_for_local.
|
|
template<int got_size, bool big_endian>
|
|
void
|
|
Output_data_got<got_size, big_endian>::add_local_tls_pair(
|
|
Relobj* object,
|
|
unsigned int symndx,
|
|
unsigned int got_type,
|
|
Output_data_reloc_generic* rel_dyn,
|
|
unsigned int r_type)
|
|
{
|
|
if (object->local_has_got_offset(symndx, got_type))
|
|
return;
|
|
|
|
unsigned int got_offset
|
|
= this->add_got_entry_pair(Got_entry(),
|
|
Got_entry(object, symndx, true));
|
|
object->set_local_got_offset(symndx, got_type, got_offset);
|
|
rel_dyn->add_local_generic(object, 0, r_type, this, got_offset, 0);
|
|
}
|
|
|
|
// Reserve a slot in the GOT for a local symbol or the second slot of a pair.
|
|
|
|
template<int got_size, bool big_endian>
|
|
void
|
|
Output_data_got<got_size, big_endian>::reserve_local(
|
|
unsigned int i,
|
|
Relobj* object,
|
|
unsigned int sym_index,
|
|
unsigned int got_type)
|
|
{
|
|
this->do_reserve_slot(i);
|
|
object->set_local_got_offset(sym_index, got_type, this->got_offset(i));
|
|
}
|
|
|
|
// Reserve a slot in the GOT for a global symbol.
|
|
|
|
template<int got_size, bool big_endian>
|
|
void
|
|
Output_data_got<got_size, big_endian>::reserve_global(
|
|
unsigned int i,
|
|
Symbol* gsym,
|
|
unsigned int got_type)
|
|
{
|
|
this->do_reserve_slot(i);
|
|
gsym->set_got_offset(got_type, this->got_offset(i));
|
|
}
|
|
|
|
// Write out the GOT.
|
|
|
|
template<int got_size, bool big_endian>
|
|
void
|
|
Output_data_got<got_size, big_endian>::do_write(Output_file* of)
|
|
{
|
|
const int add = got_size / 8;
|
|
|
|
const off_t off = this->offset();
|
|
const off_t oview_size = this->data_size();
|
|
unsigned char* const oview = of->get_output_view(off, oview_size);
|
|
|
|
unsigned char* pov = oview;
|
|
for (unsigned int i = 0; i < this->entries_.size(); ++i)
|
|
{
|
|
this->entries_[i].write(i, pov);
|
|
pov += add;
|
|
}
|
|
|
|
gold_assert(pov - oview == oview_size);
|
|
|
|
of->write_output_view(off, oview_size, oview);
|
|
|
|
// We no longer need the GOT entries.
|
|
this->entries_.clear();
|
|
}
|
|
|
|
// Create a new GOT entry and return its offset.
|
|
|
|
template<int got_size, bool big_endian>
|
|
unsigned int
|
|
Output_data_got<got_size, big_endian>::add_got_entry(Got_entry got_entry)
|
|
{
|
|
if (!this->is_data_size_valid())
|
|
{
|
|
this->entries_.push_back(got_entry);
|
|
this->set_got_size();
|
|
return this->last_got_offset();
|
|
}
|
|
else
|
|
{
|
|
// For an incremental update, find an available slot.
|
|
off_t got_offset = this->free_list_.allocate(got_size / 8,
|
|
got_size / 8, 0);
|
|
if (got_offset == -1)
|
|
gold_fallback(_("out of patch space (GOT);"
|
|
" relink with --incremental-full"));
|
|
unsigned int got_index = got_offset / (got_size / 8);
|
|
gold_assert(got_index < this->entries_.size());
|
|
this->entries_[got_index] = got_entry;
|
|
return static_cast<unsigned int>(got_offset);
|
|
}
|
|
}
|
|
|
|
// Create a pair of new GOT entries and return the offset of the first.
|
|
|
|
template<int got_size, bool big_endian>
|
|
unsigned int
|
|
Output_data_got<got_size, big_endian>::add_got_entry_pair(
|
|
Got_entry got_entry_1,
|
|
Got_entry got_entry_2)
|
|
{
|
|
if (!this->is_data_size_valid())
|
|
{
|
|
unsigned int got_offset;
|
|
this->entries_.push_back(got_entry_1);
|
|
got_offset = this->last_got_offset();
|
|
this->entries_.push_back(got_entry_2);
|
|
this->set_got_size();
|
|
return got_offset;
|
|
}
|
|
else
|
|
{
|
|
// For an incremental update, find an available pair of slots.
|
|
off_t got_offset = this->free_list_.allocate(2 * got_size / 8,
|
|
got_size / 8, 0);
|
|
if (got_offset == -1)
|
|
gold_fallback(_("out of patch space (GOT);"
|
|
" relink with --incremental-full"));
|
|
unsigned int got_index = got_offset / (got_size / 8);
|
|
gold_assert(got_index < this->entries_.size());
|
|
this->entries_[got_index] = got_entry_1;
|
|
this->entries_[got_index + 1] = got_entry_2;
|
|
return static_cast<unsigned int>(got_offset);
|
|
}
|
|
}
|
|
|
|
// Replace GOT entry I with a new value.
|
|
|
|
template<int got_size, bool big_endian>
|
|
void
|
|
Output_data_got<got_size, big_endian>::replace_got_entry(
|
|
unsigned int i,
|
|
Got_entry got_entry)
|
|
{
|
|
gold_assert(i < this->entries_.size());
|
|
this->entries_[i] = got_entry;
|
|
}
|
|
|
|
// Output_data_dynamic::Dynamic_entry methods.
|
|
|
|
// Write out the entry.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Output_data_dynamic::Dynamic_entry::write(
|
|
unsigned char* pov,
|
|
const Stringpool* pool) const
|
|
{
|
|
typename elfcpp::Elf_types<size>::Elf_WXword val;
|
|
switch (this->offset_)
|
|
{
|
|
case DYNAMIC_NUMBER:
|
|
val = this->u_.val;
|
|
break;
|
|
|
|
case DYNAMIC_SECTION_SIZE:
|
|
val = this->u_.od->data_size();
|
|
if (this->od2 != NULL)
|
|
val += this->od2->data_size();
|
|
break;
|
|
|
|
case DYNAMIC_SYMBOL:
|
|
{
|
|
const Sized_symbol<size>* s =
|
|
static_cast<const Sized_symbol<size>*>(this->u_.sym);
|
|
val = s->value();
|
|
}
|
|
break;
|
|
|
|
case DYNAMIC_STRING:
|
|
val = pool->get_offset(this->u_.str);
|
|
break;
|
|
|
|
case DYNAMIC_CUSTOM:
|
|
val = parameters->target().dynamic_tag_custom_value(this->tag_);
|
|
break;
|
|
|
|
default:
|
|
val = this->u_.od->address() + this->offset_;
|
|
break;
|
|
}
|
|
|
|
elfcpp::Dyn_write<size, big_endian> dw(pov);
|
|
dw.put_d_tag(this->tag_);
|
|
dw.put_d_val(val);
|
|
}
|
|
|
|
// Output_data_dynamic methods.
|
|
|
|
// Adjust the output section to set the entry size.
|
|
|
|
void
|
|
Output_data_dynamic::do_adjust_output_section(Output_section* os)
|
|
{
|
|
if (parameters->target().get_size() == 32)
|
|
os->set_entsize(elfcpp::Elf_sizes<32>::dyn_size);
|
|
else if (parameters->target().get_size() == 64)
|
|
os->set_entsize(elfcpp::Elf_sizes<64>::dyn_size);
|
|
else
|
|
gold_unreachable();
|
|
}
|
|
|
|
// Get a dynamic entry offset.
|
|
|
|
unsigned int
|
|
Output_data_dynamic::get_entry_offset(elfcpp::DT tag) const
|
|
{
|
|
int dyn_size;
|
|
|
|
if (parameters->target().get_size() == 32)
|
|
dyn_size = elfcpp::Elf_sizes<32>::dyn_size;
|
|
else if (parameters->target().get_size() == 64)
|
|
dyn_size = elfcpp::Elf_sizes<64>::dyn_size;
|
|
else
|
|
gold_unreachable();
|
|
|
|
for (size_t i = 0; i < entries_.size(); ++i)
|
|
if (entries_[i].tag() == tag)
|
|
return i * dyn_size;
|
|
|
|
return -1U;
|
|
}
|
|
|
|
// Set the final data size.
|
|
|
|
void
|
|
Output_data_dynamic::set_final_data_size()
|
|
{
|
|
// Add the terminating entry if it hasn't been added.
|
|
// Because of relaxation, we can run this multiple times.
|
|
if (this->entries_.empty() || this->entries_.back().tag() != elfcpp::DT_NULL)
|
|
{
|
|
int extra = parameters->options().spare_dynamic_tags();
|
|
for (int i = 0; i < extra; ++i)
|
|
this->add_constant(elfcpp::DT_NULL, 0);
|
|
this->add_constant(elfcpp::DT_NULL, 0);
|
|
}
|
|
|
|
int dyn_size;
|
|
if (parameters->target().get_size() == 32)
|
|
dyn_size = elfcpp::Elf_sizes<32>::dyn_size;
|
|
else if (parameters->target().get_size() == 64)
|
|
dyn_size = elfcpp::Elf_sizes<64>::dyn_size;
|
|
else
|
|
gold_unreachable();
|
|
this->set_data_size(this->entries_.size() * dyn_size);
|
|
}
|
|
|
|
// Write out the dynamic entries.
|
|
|
|
void
|
|
Output_data_dynamic::do_write(Output_file* of)
|
|
{
|
|
switch (parameters->size_and_endianness())
|
|
{
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
case Parameters::TARGET_32_LITTLE:
|
|
this->sized_write<32, false>(of);
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
case Parameters::TARGET_32_BIG:
|
|
this->sized_write<32, true>(of);
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
case Parameters::TARGET_64_LITTLE:
|
|
this->sized_write<64, false>(of);
|
|
break;
|
|
#endif
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
case Parameters::TARGET_64_BIG:
|
|
this->sized_write<64, true>(of);
|
|
break;
|
|
#endif
|
|
default:
|
|
gold_unreachable();
|
|
}
|
|
}
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Output_data_dynamic::sized_write(Output_file* of)
|
|
{
|
|
const int dyn_size = elfcpp::Elf_sizes<size>::dyn_size;
|
|
|
|
const off_t offset = this->offset();
|
|
const off_t oview_size = this->data_size();
|
|
unsigned char* const oview = of->get_output_view(offset, oview_size);
|
|
|
|
unsigned char* pov = oview;
|
|
for (typename Dynamic_entries::const_iterator p = this->entries_.begin();
|
|
p != this->entries_.end();
|
|
++p)
|
|
{
|
|
p->write<size, big_endian>(pov, this->pool_);
|
|
pov += dyn_size;
|
|
}
|
|
|
|
gold_assert(pov - oview == oview_size);
|
|
|
|
of->write_output_view(offset, oview_size, oview);
|
|
|
|
// We no longer need the dynamic entries.
|
|
this->entries_.clear();
|
|
}
|
|
|
|
// Class Output_symtab_xindex.
|
|
|
|
void
|
|
Output_symtab_xindex::do_write(Output_file* of)
|
|
{
|
|
const off_t offset = this->offset();
|
|
const off_t oview_size = this->data_size();
|
|
unsigned char* const oview = of->get_output_view(offset, oview_size);
|
|
|
|
memset(oview, 0, oview_size);
|
|
|
|
if (parameters->target().is_big_endian())
|
|
this->endian_do_write<true>(oview);
|
|
else
|
|
this->endian_do_write<false>(oview);
|
|
|
|
of->write_output_view(offset, oview_size, oview);
|
|
|
|
// We no longer need the data.
|
|
this->entries_.clear();
|
|
}
|
|
|
|
template<bool big_endian>
|
|
void
|
|
Output_symtab_xindex::endian_do_write(unsigned char* const oview)
|
|
{
|
|
for (Xindex_entries::const_iterator p = this->entries_.begin();
|
|
p != this->entries_.end();
|
|
++p)
|
|
{
|
|
unsigned int symndx = p->first;
|
|
gold_assert(static_cast<off_t>(symndx) * 4 < this->data_size());
|
|
elfcpp::Swap<32, big_endian>::writeval(oview + symndx * 4, p->second);
|
|
}
|
|
}
|
|
|
|
// Output_fill_debug_info methods.
|
|
|
|
// Return the minimum size needed for a dummy compilation unit header.
|
|
|
|
size_t
|
|
Output_fill_debug_info::do_minimum_hole_size() const
|
|
{
|
|
// Compile unit header fields: unit_length, version, debug_abbrev_offset,
|
|
// address_size.
|
|
const size_t len = 4 + 2 + 4 + 1;
|
|
// For type units, add type_signature, type_offset.
|
|
if (this->is_debug_types_)
|
|
return len + 8 + 4;
|
|
return len;
|
|
}
|
|
|
|
// Write a dummy compilation unit header to fill a hole in the
|
|
// .debug_info or .debug_types section.
|
|
|
|
void
|
|
Output_fill_debug_info::do_write(Output_file* of, off_t off, size_t len) const
|
|
{
|
|
gold_debug(DEBUG_INCREMENTAL, "fill_debug_info(%08lx, %08lx)",
|
|
static_cast<long>(off), static_cast<long>(len));
|
|
|
|
gold_assert(len >= this->do_minimum_hole_size());
|
|
|
|
unsigned char* const oview = of->get_output_view(off, len);
|
|
unsigned char* pov = oview;
|
|
|
|
// Write header fields: unit_length, version, debug_abbrev_offset,
|
|
// address_size.
|
|
if (this->is_big_endian())
|
|
{
|
|
elfcpp::Swap_unaligned<32, true>::writeval(pov, len - 4);
|
|
elfcpp::Swap_unaligned<16, true>::writeval(pov + 4, this->version);
|
|
elfcpp::Swap_unaligned<32, true>::writeval(pov + 6, 0);
|
|
}
|
|
else
|
|
{
|
|
elfcpp::Swap_unaligned<32, false>::writeval(pov, len - 4);
|
|
elfcpp::Swap_unaligned<16, false>::writeval(pov + 4, this->version);
|
|
elfcpp::Swap_unaligned<32, false>::writeval(pov + 6, 0);
|
|
}
|
|
pov += 4 + 2 + 4;
|
|
*pov++ = 4;
|
|
|
|
// For type units, the additional header fields -- type_signature,
|
|
// type_offset -- can be filled with zeroes.
|
|
|
|
// Fill the remainder of the free space with zeroes. The first
|
|
// zero should tell the consumer there are no DIEs to read in this
|
|
// compilation unit.
|
|
if (pov < oview + len)
|
|
memset(pov, 0, oview + len - pov);
|
|
|
|
of->write_output_view(off, len, oview);
|
|
}
|
|
|
|
// Output_fill_debug_line methods.
|
|
|
|
// Return the minimum size needed for a dummy line number program header.
|
|
|
|
size_t
|
|
Output_fill_debug_line::do_minimum_hole_size() const
|
|
{
|
|
// Line number program header fields: unit_length, version, header_length,
|
|
// minimum_instruction_length, default_is_stmt, line_base, line_range,
|
|
// opcode_base, standard_opcode_lengths[], include_directories, filenames.
|
|
const size_t len = 4 + 2 + 4 + this->header_length;
|
|
return len;
|
|
}
|
|
|
|
// Write a dummy line number program header to fill a hole in the
|
|
// .debug_line section.
|
|
|
|
void
|
|
Output_fill_debug_line::do_write(Output_file* of, off_t off, size_t len) const
|
|
{
|
|
gold_debug(DEBUG_INCREMENTAL, "fill_debug_line(%08lx, %08lx)",
|
|
static_cast<long>(off), static_cast<long>(len));
|
|
|
|
gold_assert(len >= this->do_minimum_hole_size());
|
|
|
|
unsigned char* const oview = of->get_output_view(off, len);
|
|
unsigned char* pov = oview;
|
|
|
|
// Write header fields: unit_length, version, header_length,
|
|
// minimum_instruction_length, default_is_stmt, line_base, line_range,
|
|
// opcode_base, standard_opcode_lengths[], include_directories, filenames.
|
|
// We set the header_length field to cover the entire hole, so the
|
|
// line number program is empty.
|
|
if (this->is_big_endian())
|
|
{
|
|
elfcpp::Swap_unaligned<32, true>::writeval(pov, len - 4);
|
|
elfcpp::Swap_unaligned<16, true>::writeval(pov + 4, this->version);
|
|
elfcpp::Swap_unaligned<32, true>::writeval(pov + 6, len - (4 + 2 + 4));
|
|
}
|
|
else
|
|
{
|
|
elfcpp::Swap_unaligned<32, false>::writeval(pov, len - 4);
|
|
elfcpp::Swap_unaligned<16, false>::writeval(pov + 4, this->version);
|
|
elfcpp::Swap_unaligned<32, false>::writeval(pov + 6, len - (4 + 2 + 4));
|
|
}
|
|
pov += 4 + 2 + 4;
|
|
*pov++ = 1; // minimum_instruction_length
|
|
*pov++ = 0; // default_is_stmt
|
|
*pov++ = 0; // line_base
|
|
*pov++ = 5; // line_range
|
|
*pov++ = 13; // opcode_base
|
|
*pov++ = 0; // standard_opcode_lengths[1]
|
|
*pov++ = 1; // standard_opcode_lengths[2]
|
|
*pov++ = 1; // standard_opcode_lengths[3]
|
|
*pov++ = 1; // standard_opcode_lengths[4]
|
|
*pov++ = 1; // standard_opcode_lengths[5]
|
|
*pov++ = 0; // standard_opcode_lengths[6]
|
|
*pov++ = 0; // standard_opcode_lengths[7]
|
|
*pov++ = 0; // standard_opcode_lengths[8]
|
|
*pov++ = 1; // standard_opcode_lengths[9]
|
|
*pov++ = 0; // standard_opcode_lengths[10]
|
|
*pov++ = 0; // standard_opcode_lengths[11]
|
|
*pov++ = 1; // standard_opcode_lengths[12]
|
|
*pov++ = 0; // include_directories (empty)
|
|
*pov++ = 0; // filenames (empty)
|
|
|
|
// Some consumers don't check the header_length field, and simply
|
|
// start reading the line number program immediately following the
|
|
// header. For those consumers, we fill the remainder of the free
|
|
// space with DW_LNS_set_basic_block opcodes. These are effectively
|
|
// no-ops: the resulting line table program will not create any rows.
|
|
if (pov < oview + len)
|
|
memset(pov, elfcpp::DW_LNS_set_basic_block, oview + len - pov);
|
|
|
|
of->write_output_view(off, len, oview);
|
|
}
|
|
|
|
// Output_section::Input_section methods.
|
|
|
|
// Return the current data size. For an input section we store the size here.
|
|
// For an Output_section_data, we have to ask it for the size.
|
|
|
|
off_t
|
|
Output_section::Input_section::current_data_size() const
|
|
{
|
|
if (this->is_input_section())
|
|
return this->u1_.data_size;
|
|
else
|
|
{
|
|
this->u2_.posd->pre_finalize_data_size();
|
|
return this->u2_.posd->current_data_size();
|
|
}
|
|
}
|
|
|
|
// Return the data size. For an input section we store the size here.
|
|
// For an Output_section_data, we have to ask it for the size.
|
|
|
|
off_t
|
|
Output_section::Input_section::data_size() const
|
|
{
|
|
if (this->is_input_section())
|
|
return this->u1_.data_size;
|
|
else
|
|
return this->u2_.posd->data_size();
|
|
}
|
|
|
|
// Return the object for an input section.
|
|
|
|
Relobj*
|
|
Output_section::Input_section::relobj() const
|
|
{
|
|
if (this->is_input_section())
|
|
return this->u2_.object;
|
|
else if (this->is_merge_section())
|
|
{
|
|
gold_assert(this->u2_.pomb->first_relobj() != NULL);
|
|
return this->u2_.pomb->first_relobj();
|
|
}
|
|
else if (this->is_relaxed_input_section())
|
|
return this->u2_.poris->relobj();
|
|
else
|
|
gold_unreachable();
|
|
}
|
|
|
|
// Return the input section index for an input section.
|
|
|
|
unsigned int
|
|
Output_section::Input_section::shndx() const
|
|
{
|
|
if (this->is_input_section())
|
|
return this->shndx_;
|
|
else if (this->is_merge_section())
|
|
{
|
|
gold_assert(this->u2_.pomb->first_relobj() != NULL);
|
|
return this->u2_.pomb->first_shndx();
|
|
}
|
|
else if (this->is_relaxed_input_section())
|
|
return this->u2_.poris->shndx();
|
|
else
|
|
gold_unreachable();
|
|
}
|
|
|
|
// Set the address and file offset.
|
|
|
|
void
|
|
Output_section::Input_section::set_address_and_file_offset(
|
|
uint64_t address,
|
|
off_t file_offset,
|
|
off_t section_file_offset)
|
|
{
|
|
if (this->is_input_section())
|
|
this->u2_.object->set_section_offset(this->shndx_,
|
|
file_offset - section_file_offset);
|
|
else
|
|
this->u2_.posd->set_address_and_file_offset(address, file_offset);
|
|
}
|
|
|
|
// Reset the address and file offset.
|
|
|
|
void
|
|
Output_section::Input_section::reset_address_and_file_offset()
|
|
{
|
|
if (!this->is_input_section())
|
|
this->u2_.posd->reset_address_and_file_offset();
|
|
}
|
|
|
|
// Finalize the data size.
|
|
|
|
void
|
|
Output_section::Input_section::finalize_data_size()
|
|
{
|
|
if (!this->is_input_section())
|
|
this->u2_.posd->finalize_data_size();
|
|
}
|
|
|
|
// Try to turn an input offset into an output offset. We want to
|
|
// return the output offset relative to the start of this
|
|
// Input_section in the output section.
|
|
|
|
inline bool
|
|
Output_section::Input_section::output_offset(
|
|
const Relobj* object,
|
|
unsigned int shndx,
|
|
section_offset_type offset,
|
|
section_offset_type* poutput) const
|
|
{
|
|
if (!this->is_input_section())
|
|
return this->u2_.posd->output_offset(object, shndx, offset, poutput);
|
|
else
|
|
{
|
|
if (this->shndx_ != shndx || this->u2_.object != object)
|
|
return false;
|
|
*poutput = offset;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// Write out the data. We don't have to do anything for an input
|
|
// section--they are handled via Object::relocate--but this is where
|
|
// we write out the data for an Output_section_data.
|
|
|
|
void
|
|
Output_section::Input_section::write(Output_file* of)
|
|
{
|
|
if (!this->is_input_section())
|
|
this->u2_.posd->write(of);
|
|
}
|
|
|
|
// Write the data to a buffer. As for write(), we don't have to do
|
|
// anything for an input section.
|
|
|
|
void
|
|
Output_section::Input_section::write_to_buffer(unsigned char* buffer)
|
|
{
|
|
if (!this->is_input_section())
|
|
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;
|
|
|
|
case RELAXED_INPUT_SECTION_CODE:
|
|
{
|
|
Output_relaxed_input_section* relaxed_section =
|
|
this->relaxed_input_section();
|
|
mapfile->print_input_section(relaxed_section->relobj(),
|
|
relaxed_section->shndx());
|
|
}
|
|
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.
|
|
|
|
Output_section::Output_section(const char* name, elfcpp::Elf_Word type,
|
|
elfcpp::Elf_Xword flags)
|
|
: name_(name),
|
|
addralign_(0),
|
|
entsize_(0),
|
|
load_address_(0),
|
|
link_section_(NULL),
|
|
link_(0),
|
|
info_section_(NULL),
|
|
info_symndx_(NULL),
|
|
info_(0),
|
|
type_(type),
|
|
flags_(flags),
|
|
order_(ORDER_INVALID),
|
|
out_shndx_(-1U),
|
|
symtab_index_(0),
|
|
dynsym_index_(0),
|
|
input_sections_(),
|
|
first_input_offset_(0),
|
|
fills_(),
|
|
postprocessing_buffer_(NULL),
|
|
needs_symtab_index_(false),
|
|
needs_dynsym_index_(false),
|
|
should_link_to_symtab_(false),
|
|
should_link_to_dynsym_(false),
|
|
after_input_sections_(false),
|
|
requires_postprocessing_(false),
|
|
found_in_sections_clause_(false),
|
|
has_load_address_(false),
|
|
info_uses_section_index_(false),
|
|
input_section_order_specified_(false),
|
|
may_sort_attached_input_sections_(false),
|
|
must_sort_attached_input_sections_(false),
|
|
attached_input_sections_are_sorted_(false),
|
|
is_relro_(false),
|
|
is_small_section_(false),
|
|
is_large_section_(false),
|
|
generate_code_fills_at_write_(false),
|
|
is_entsize_zero_(false),
|
|
section_offsets_need_adjustment_(false),
|
|
is_noload_(false),
|
|
always_keeps_input_sections_(false),
|
|
has_fixed_layout_(false),
|
|
is_patch_space_allowed_(false),
|
|
is_unique_segment_(false),
|
|
tls_offset_(0),
|
|
extra_segment_flags_(0),
|
|
segment_alignment_(0),
|
|
checkpoint_(NULL),
|
|
lookup_maps_(new Output_section_lookup_maps),
|
|
free_list_(),
|
|
free_space_fill_(NULL),
|
|
patch_space_(0),
|
|
reloc_section_(NULL)
|
|
{
|
|
// An unallocated section has no address. Forcing this means that
|
|
// we don't need special treatment for symbols defined in debug
|
|
// sections.
|
|
if ((flags & elfcpp::SHF_ALLOC) == 0)
|
|
this->set_address(0);
|
|
}
|
|
|
|
Output_section::~Output_section()
|
|
{
|
|
delete this->checkpoint_;
|
|
}
|
|
|
|
// Set the entry size.
|
|
|
|
void
|
|
Output_section::set_entsize(uint64_t v)
|
|
{
|
|
if (this->is_entsize_zero_)
|
|
;
|
|
else if (this->entsize_ == 0)
|
|
this->entsize_ = v;
|
|
else if (this->entsize_ != v)
|
|
{
|
|
this->entsize_ = 0;
|
|
this->is_entsize_zero_ = 1;
|
|
}
|
|
}
|
|
|
|
// Add the input section SHNDX, with header SHDR, named SECNAME, in
|
|
// OBJECT, to the Output_section. RELOC_SHNDX is the index of a
|
|
// relocation section which applies to this section, or 0 if none, or
|
|
// -1U if more than one. Return the offset of the input section
|
|
// within the output section. Return -1 if the input section will
|
|
// receive special handling. In the normal case we don't always keep
|
|
// track of input sections for an Output_section. Instead, each
|
|
// Object keeps track of the Output_section for each of its input
|
|
// sections. However, if HAVE_SECTIONS_SCRIPT is true, we do keep
|
|
// track of input sections here; this is used when SECTIONS appears in
|
|
// a linker script.
|
|
|
|
template<int size, bool big_endian>
|
|
off_t
|
|
Output_section::add_input_section(Layout* layout,
|
|
Sized_relobj_file<size, big_endian>* object,
|
|
unsigned int shndx,
|
|
const char* secname,
|
|
const elfcpp::Shdr<size, big_endian>& shdr,
|
|
unsigned int reloc_shndx,
|
|
bool have_sections_script)
|
|
{
|
|
elfcpp::Elf_Xword addralign = shdr.get_sh_addralign();
|
|
if ((addralign & (addralign - 1)) != 0)
|
|
{
|
|
object->error(_("invalid alignment %lu for section \"%s\""),
|
|
static_cast<unsigned long>(addralign), secname);
|
|
addralign = 1;
|
|
}
|
|
|
|
if (addralign > this->addralign_)
|
|
this->addralign_ = addralign;
|
|
|
|
typename elfcpp::Elf_types<size>::Elf_WXword sh_flags = shdr.get_sh_flags();
|
|
uint64_t entsize = shdr.get_sh_entsize();
|
|
|
|
// .debug_str is a mergeable string section, but is not always so
|
|
// marked by compilers. Mark manually here so we can optimize.
|
|
if (strcmp(secname, ".debug_str") == 0)
|
|
{
|
|
sh_flags |= (elfcpp::SHF_MERGE | elfcpp::SHF_STRINGS);
|
|
entsize = 1;
|
|
}
|
|
|
|
this->update_flags_for_input_section(sh_flags);
|
|
this->set_entsize(entsize);
|
|
|
|
// 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. We don't try to handle empty merge sections--they
|
|
// mess up the mappings, and are useless anyhow.
|
|
// FIXME: Need to handle merge sections during incremental update.
|
|
if ((sh_flags & elfcpp::SHF_MERGE) != 0
|
|
&& reloc_shndx == 0
|
|
&& shdr.get_sh_size() > 0
|
|
&& !parameters->incremental())
|
|
{
|
|
// Keep information about merged input sections for rebuilding fast
|
|
// lookup maps if we have sections-script or we do relaxation.
|
|
bool keeps_input_sections = (this->always_keeps_input_sections_
|
|
|| have_sections_script
|
|
|| parameters->target().may_relax());
|
|
|
|
if (this->add_merge_input_section(object, shndx, sh_flags, entsize,
|
|
addralign, keeps_input_sections))
|
|
{
|
|
// Tell the relocation routines that they need to call the
|
|
// output_offset method to determine the final address.
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
section_size_type input_section_size = shdr.get_sh_size();
|
|
section_size_type uncompressed_size;
|
|
if (object->section_is_compressed(shndx, &uncompressed_size))
|
|
input_section_size = uncompressed_size;
|
|
|
|
off_t offset_in_section;
|
|
|
|
if (this->has_fixed_layout())
|
|
{
|
|
// For incremental updates, find a chunk of unused space in the section.
|
|
offset_in_section = this->free_list_.allocate(input_section_size,
|
|
addralign, 0);
|
|
if (offset_in_section == -1)
|
|
gold_fallback(_("out of patch space in section %s; "
|
|
"relink with --incremental-full"),
|
|
this->name());
|
|
return offset_in_section;
|
|
}
|
|
|
|
offset_in_section = this->current_data_size_for_child();
|
|
off_t aligned_offset_in_section = align_address(offset_in_section,
|
|
addralign);
|
|
this->set_current_data_size_for_child(aligned_offset_in_section
|
|
+ input_section_size);
|
|
|
|
// Determine if we want to delay code-fill generation until the output
|
|
// section is written. When the target is relaxing, we want to delay fill
|
|
// generating to avoid adjusting them during relaxation. Also, if we are
|
|
// sorting input sections we must delay fill generation.
|
|
if (!this->generate_code_fills_at_write_
|
|
&& !have_sections_script
|
|
&& (sh_flags & elfcpp::SHF_EXECINSTR) != 0
|
|
&& parameters->target().has_code_fill()
|
|
&& (parameters->target().may_relax()
|
|
|| layout->is_section_ordering_specified()))
|
|
{
|
|
gold_assert(this->fills_.empty());
|
|
this->generate_code_fills_at_write_ = true;
|
|
}
|
|
|
|
if (aligned_offset_in_section > offset_in_section
|
|
&& !this->generate_code_fills_at_write_
|
|
&& !have_sections_script
|
|
&& (sh_flags & elfcpp::SHF_EXECINSTR) != 0
|
|
&& parameters->target().has_code_fill())
|
|
{
|
|
// We need to add some fill data. Using fill_list_ when
|
|
// possible is an optimization, since we will often have fill
|
|
// sections without input sections.
|
|
off_t fill_len = aligned_offset_in_section - offset_in_section;
|
|
if (this->input_sections_.empty())
|
|
this->fills_.push_back(Fill(offset_in_section, fill_len));
|
|
else
|
|
{
|
|
std::string fill_data(parameters->target().code_fill(fill_len));
|
|
Output_data_const* odc = new Output_data_const(fill_data, 1);
|
|
this->input_sections_.push_back(Input_section(odc));
|
|
}
|
|
}
|
|
|
|
// We need to keep track of this section if we are already keeping
|
|
// track of sections, or if we are relaxing. Also, if this is a
|
|
// section which requires sorting, or which may require sorting in
|
|
// the future, we keep track of the sections. If the
|
|
// --section-ordering-file option is used to specify the order of
|
|
// sections, we need to keep track of sections.
|
|
if (this->always_keeps_input_sections_
|
|
|| have_sections_script
|
|
|| !this->input_sections_.empty()
|
|
|| this->may_sort_attached_input_sections()
|
|
|| this->must_sort_attached_input_sections()
|
|
|| parameters->options().user_set_Map()
|
|
|| parameters->target().may_relax()
|
|
|| layout->is_section_ordering_specified())
|
|
{
|
|
Input_section isecn(object, shndx, input_section_size, addralign);
|
|
/* If section ordering is requested by specifying a ordering file,
|
|
using --section-ordering-file, match the section name with
|
|
a pattern. */
|
|
if (parameters->options().section_ordering_file())
|
|
{
|
|
unsigned int section_order_index =
|
|
layout->find_section_order_index(std::string(secname));
|
|
if (section_order_index != 0)
|
|
{
|
|
isecn.set_section_order_index(section_order_index);
|
|
this->set_input_section_order_specified();
|
|
}
|
|
}
|
|
this->input_sections_.push_back(isecn);
|
|
}
|
|
|
|
return aligned_offset_in_section;
|
|
}
|
|
|
|
// Add arbitrary data to an output section.
|
|
|
|
void
|
|
Output_section::add_output_section_data(Output_section_data* posd)
|
|
{
|
|
Input_section inp(posd);
|
|
this->add_output_section_data(&inp);
|
|
|
|
if (posd->is_data_size_valid())
|
|
{
|
|
off_t offset_in_section;
|
|
if (this->has_fixed_layout())
|
|
{
|
|
// For incremental updates, find a chunk of unused space.
|
|
offset_in_section = this->free_list_.allocate(posd->data_size(),
|
|
posd->addralign(), 0);
|
|
if (offset_in_section == -1)
|
|
gold_fallback(_("out of patch space in section %s; "
|
|
"relink with --incremental-full"),
|
|
this->name());
|
|
// Finalize the address and offset now.
|
|
uint64_t addr = this->address();
|
|
off_t offset = this->offset();
|
|
posd->set_address_and_file_offset(addr + offset_in_section,
|
|
offset + offset_in_section);
|
|
}
|
|
else
|
|
{
|
|
offset_in_section = this->current_data_size_for_child();
|
|
off_t aligned_offset_in_section = align_address(offset_in_section,
|
|
posd->addralign());
|
|
this->set_current_data_size_for_child(aligned_offset_in_section
|
|
+ posd->data_size());
|
|
}
|
|
}
|
|
else if (this->has_fixed_layout())
|
|
{
|
|
// For incremental updates, arrange for the data to have a fixed layout.
|
|
// This will mean that additions to the data must be allocated from
|
|
// free space within the containing output section.
|
|
uint64_t addr = this->address();
|
|
posd->set_address(addr);
|
|
posd->set_file_offset(0);
|
|
// FIXME: This should eventually be unreachable.
|
|
// gold_unreachable();
|
|
}
|
|
}
|
|
|
|
// Add a relaxed input section.
|
|
|
|
void
|
|
Output_section::add_relaxed_input_section(Layout* layout,
|
|
Output_relaxed_input_section* poris,
|
|
const std::string& name)
|
|
{
|
|
Input_section inp(poris);
|
|
|
|
// If the --section-ordering-file option is used to specify the order of
|
|
// sections, we need to keep track of sections.
|
|
if (layout->is_section_ordering_specified())
|
|
{
|
|
unsigned int section_order_index =
|
|
layout->find_section_order_index(name);
|
|
if (section_order_index != 0)
|
|
{
|
|
inp.set_section_order_index(section_order_index);
|
|
this->set_input_section_order_specified();
|
|
}
|
|
}
|
|
|
|
this->add_output_section_data(&inp);
|
|
if (this->lookup_maps_->is_valid())
|
|
this->lookup_maps_->add_relaxed_input_section(poris->relobj(),
|
|
poris->shndx(), poris);
|
|
|
|
// For a relaxed section, we use the current data size. Linker scripts
|
|
// get all the input sections, including relaxed one from an output
|
|
// section and add them back to the same output section to compute the
|
|
// output section size. If we do not account for sizes of relaxed input
|
|
// sections, an output section would be incorrectly sized.
|
|
off_t offset_in_section = this->current_data_size_for_child();
|
|
off_t aligned_offset_in_section = align_address(offset_in_section,
|
|
poris->addralign());
|
|
this->set_current_data_size_for_child(aligned_offset_in_section
|
|
+ poris->current_data_size());
|
|
}
|
|
|
|
// Add arbitrary data to an output section by Input_section.
|
|
|
|
void
|
|
Output_section::add_output_section_data(Input_section* inp)
|
|
{
|
|
if (this->input_sections_.empty())
|
|
this->first_input_offset_ = this->current_data_size_for_child();
|
|
|
|
this->input_sections_.push_back(*inp);
|
|
|
|
uint64_t addralign = inp->addralign();
|
|
if (addralign > this->addralign_)
|
|
this->addralign_ = addralign;
|
|
|
|
inp->set_output_section(this);
|
|
}
|
|
|
|
// Add a merge section to an output section.
|
|
|
|
void
|
|
Output_section::add_output_merge_section(Output_section_data* posd,
|
|
bool is_string, uint64_t entsize)
|
|
{
|
|
Input_section inp(posd, is_string, entsize);
|
|
this->add_output_section_data(&inp);
|
|
}
|
|
|
|
// Add an input section to a SHF_MERGE section.
|
|
|
|
bool
|
|
Output_section::add_merge_input_section(Relobj* object, unsigned int shndx,
|
|
uint64_t flags, uint64_t entsize,
|
|
uint64_t addralign,
|
|
bool keeps_input_sections)
|
|
{
|
|
// We cannot merge sections with entsize == 0.
|
|
if (entsize == 0)
|
|
return false;
|
|
|
|
bool is_string = (flags & elfcpp::SHF_STRINGS) != 0;
|
|
|
|
// We cannot restore merged input section states.
|
|
gold_assert(this->checkpoint_ == NULL);
|
|
|
|
// Look up merge sections by required properties.
|
|
// Currently, we only invalidate the lookup maps in script processing
|
|
// and relaxation. We should not have done either when we reach here.
|
|
// So we assume that the lookup maps are valid to simply code.
|
|
gold_assert(this->lookup_maps_->is_valid());
|
|
Merge_section_properties msp(is_string, entsize, addralign);
|
|
Output_merge_base* pomb = this->lookup_maps_->find_merge_section(msp);
|
|
bool is_new = false;
|
|
if (pomb != NULL)
|
|
{
|
|
gold_assert(pomb->is_string() == is_string
|
|
&& pomb->entsize() == entsize
|
|
&& pomb->addralign() == addralign);
|
|
}
|
|
else
|
|
{
|
|
// Create a new Output_merge_data or Output_merge_string_data.
|
|
if (!is_string)
|
|
pomb = new Output_merge_data(entsize, addralign);
|
|
else
|
|
{
|
|
switch (entsize)
|
|
{
|
|
case 1:
|
|
pomb = new Output_merge_string<char>(addralign);
|
|
break;
|
|
case 2:
|
|
pomb = new Output_merge_string<uint16_t>(addralign);
|
|
break;
|
|
case 4:
|
|
pomb = new Output_merge_string<uint32_t>(addralign);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
// If we need to do script processing or relaxation, we need to keep
|
|
// the original input sections to rebuild the fast lookup maps.
|
|
if (keeps_input_sections)
|
|
pomb->set_keeps_input_sections();
|
|
is_new = true;
|
|
}
|
|
|
|
if (pomb->add_input_section(object, shndx))
|
|
{
|
|
// Add new merge section to this output section and link merge
|
|
// section properties to new merge section in map.
|
|
if (is_new)
|
|
{
|
|
this->add_output_merge_section(pomb, is_string, entsize);
|
|
this->lookup_maps_->add_merge_section(msp, pomb);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// If add_input_section failed, delete new merge section to avoid
|
|
// exporting empty merge sections in Output_section::get_input_section.
|
|
if (is_new)
|
|
delete pomb;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Build a relaxation map to speed up relaxation of existing input sections.
|
|
// Look up to the first LIMIT elements in INPUT_SECTIONS.
|
|
|
|
void
|
|
Output_section::build_relaxation_map(
|
|
const Input_section_list& input_sections,
|
|
size_t limit,
|
|
Relaxation_map* relaxation_map) const
|
|
{
|
|
for (size_t i = 0; i < limit; ++i)
|
|
{
|
|
const Input_section& is(input_sections[i]);
|
|
if (is.is_input_section() || is.is_relaxed_input_section())
|
|
{
|
|
Section_id sid(is.relobj(), is.shndx());
|
|
(*relaxation_map)[sid] = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Convert regular input sections in INPUT_SECTIONS into relaxed input
|
|
// sections in RELAXED_SECTIONS. MAP is a prebuilt map from section id
|
|
// indices of INPUT_SECTIONS.
|
|
|
|
void
|
|
Output_section::convert_input_sections_in_list_to_relaxed_sections(
|
|
const std::vector<Output_relaxed_input_section*>& relaxed_sections,
|
|
const Relaxation_map& map,
|
|
Input_section_list* input_sections)
|
|
{
|
|
for (size_t i = 0; i < relaxed_sections.size(); ++i)
|
|
{
|
|
Output_relaxed_input_section* poris = relaxed_sections[i];
|
|
Section_id sid(poris->relobj(), poris->shndx());
|
|
Relaxation_map::const_iterator p = map.find(sid);
|
|
gold_assert(p != map.end());
|
|
gold_assert((*input_sections)[p->second].is_input_section());
|
|
|
|
// Remember section order index of original input section
|
|
// if it is set. Copy it to the relaxed input section.
|
|
unsigned int soi =
|
|
(*input_sections)[p->second].section_order_index();
|
|
(*input_sections)[p->second] = Input_section(poris);
|
|
(*input_sections)[p->second].set_section_order_index(soi);
|
|
}
|
|
}
|
|
|
|
// Convert regular input sections into relaxed input sections. RELAXED_SECTIONS
|
|
// is a vector of pointers to Output_relaxed_input_section or its derived
|
|
// classes. The relaxed sections must correspond to existing input sections.
|
|
|
|
void
|
|
Output_section::convert_input_sections_to_relaxed_sections(
|
|
const std::vector<Output_relaxed_input_section*>& relaxed_sections)
|
|
{
|
|
gold_assert(parameters->target().may_relax());
|
|
|
|
// We want to make sure that restore_states does not undo the effect of
|
|
// this. If there is no checkpoint active, just search the current
|
|
// input section list and replace the sections there. If there is
|
|
// a checkpoint, also replace the sections there.
|
|
|
|
// By default, we look at the whole list.
|
|
size_t limit = this->input_sections_.size();
|
|
|
|
if (this->checkpoint_ != NULL)
|
|
{
|
|
// Replace input sections with relaxed input section in the saved
|
|
// copy of the input section list.
|
|
if (this->checkpoint_->input_sections_saved())
|
|
{
|
|
Relaxation_map map;
|
|
this->build_relaxation_map(
|
|
*(this->checkpoint_->input_sections()),
|
|
this->checkpoint_->input_sections()->size(),
|
|
&map);
|
|
this->convert_input_sections_in_list_to_relaxed_sections(
|
|
relaxed_sections,
|
|
map,
|
|
this->checkpoint_->input_sections());
|
|
}
|
|
else
|
|
{
|
|
// We have not copied the input section list yet. Instead, just
|
|
// look at the portion that would be saved.
|
|
limit = this->checkpoint_->input_sections_size();
|
|
}
|
|
}
|
|
|
|
// Convert input sections in input_section_list.
|
|
Relaxation_map map;
|
|
this->build_relaxation_map(this->input_sections_, limit, &map);
|
|
this->convert_input_sections_in_list_to_relaxed_sections(
|
|
relaxed_sections,
|
|
map,
|
|
&this->input_sections_);
|
|
|
|
// Update fast look-up map.
|
|
if (this->lookup_maps_->is_valid())
|
|
for (size_t i = 0; i < relaxed_sections.size(); ++i)
|
|
{
|
|
Output_relaxed_input_section* poris = relaxed_sections[i];
|
|
this->lookup_maps_->add_relaxed_input_section(poris->relobj(),
|
|
poris->shndx(), poris);
|
|
}
|
|
}
|
|
|
|
// Update the output section flags based on input section flags.
|
|
|
|
void
|
|
Output_section::update_flags_for_input_section(elfcpp::Elf_Xword flags)
|
|
{
|
|
// If we created the section with SHF_ALLOC clear, we set the
|
|
// address. If we are now setting the SHF_ALLOC flag, we need to
|
|
// undo that.
|
|
if ((this->flags_ & elfcpp::SHF_ALLOC) == 0
|
|
&& (flags & elfcpp::SHF_ALLOC) != 0)
|
|
this->mark_address_invalid();
|
|
|
|
this->flags_ |= (flags
|
|
& (elfcpp::SHF_WRITE
|
|
| elfcpp::SHF_ALLOC
|
|
| elfcpp::SHF_EXECINSTR));
|
|
|
|
if ((flags & elfcpp::SHF_MERGE) == 0)
|
|
this->flags_ &=~ elfcpp::SHF_MERGE;
|
|
else
|
|
{
|
|
if (this->current_data_size_for_child() == 0)
|
|
this->flags_ |= elfcpp::SHF_MERGE;
|
|
}
|
|
|
|
if ((flags & elfcpp::SHF_STRINGS) == 0)
|
|
this->flags_ &=~ elfcpp::SHF_STRINGS;
|
|
else
|
|
{
|
|
if (this->current_data_size_for_child() == 0)
|
|
this->flags_ |= elfcpp::SHF_STRINGS;
|
|
}
|
|
}
|
|
|
|
// Find the merge section into which an input section with index SHNDX in
|
|
// OBJECT has been added. Return NULL if none found.
|
|
|
|
const Output_section_data*
|
|
Output_section::find_merge_section(const Relobj* object,
|
|
unsigned int shndx) const
|
|
{
|
|
return object->find_merge_section(shndx);
|
|
}
|
|
|
|
// Build the lookup maps for relaxed sections. This needs
|
|
// to be declared as a const method so that it is callable with a const
|
|
// Output_section pointer. The method only updates states of the maps.
|
|
|
|
void
|
|
Output_section::build_lookup_maps() const
|
|
{
|
|
this->lookup_maps_->clear();
|
|
for (Input_section_list::const_iterator p = this->input_sections_.begin();
|
|
p != this->input_sections_.end();
|
|
++p)
|
|
{
|
|
if (p->is_relaxed_input_section())
|
|
{
|
|
Output_relaxed_input_section* poris = p->relaxed_input_section();
|
|
this->lookup_maps_->add_relaxed_input_section(poris->relobj(),
|
|
poris->shndx(), poris);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Find an relaxed input section corresponding to an input section
|
|
// in OBJECT with index SHNDX.
|
|
|
|
const Output_relaxed_input_section*
|
|
Output_section::find_relaxed_input_section(const Relobj* object,
|
|
unsigned int shndx) const
|
|
{
|
|
if (!this->lookup_maps_->is_valid())
|
|
this->build_lookup_maps();
|
|
return this->lookup_maps_->find_relaxed_input_section(object, shndx);
|
|
}
|
|
|
|
// Given an address OFFSET relative to the start of input section
|
|
// SHNDX in OBJECT, return whether this address is being included in
|
|
// the final link. This should only be called if SHNDX in OBJECT has
|
|
// a special mapping.
|
|
|
|
bool
|
|
Output_section::is_input_address_mapped(const Relobj* object,
|
|
unsigned int shndx,
|
|
off_t offset) const
|
|
{
|
|
// Look at the Output_section_data_maps first.
|
|
const Output_section_data* posd = this->find_merge_section(object, shndx);
|
|
if (posd == NULL)
|
|
posd = this->find_relaxed_input_section(object, shndx);
|
|
|
|
if (posd != NULL)
|
|
{
|
|
section_offset_type output_offset;
|
|
bool found = posd->output_offset(object, shndx, offset, &output_offset);
|
|
// By default we assume that the address is mapped. See comment at the
|
|
// end.
|
|
if (!found)
|
|
return true;
|
|
return output_offset != -1;
|
|
}
|
|
|
|
// Fall back to the slow look-up.
|
|
for (Input_section_list::const_iterator p = this->input_sections_.begin();
|
|
p != this->input_sections_.end();
|
|
++p)
|
|
{
|
|
section_offset_type output_offset;
|
|
if (p->output_offset(object, shndx, offset, &output_offset))
|
|
return output_offset != -1;
|
|
}
|
|
|
|
// By default we assume that the address is mapped. This should
|
|
// only be called after we have passed all sections to Layout. At
|
|
// that point we should know what we are discarding.
|
|
return true;
|
|
}
|
|
|
|
// Given an address OFFSET relative to the start of input section
|
|
// SHNDX in object OBJECT, return the output offset relative to the
|
|
// start of the input section in the output section. This should only
|
|
// be called if SHNDX in OBJECT has a special mapping.
|
|
|
|
section_offset_type
|
|
Output_section::output_offset(const Relobj* object, unsigned int shndx,
|
|
section_offset_type offset) const
|
|
{
|
|
// This can only be called meaningfully when we know the data size
|
|
// of this.
|
|
gold_assert(this->is_data_size_valid());
|
|
|
|
// Look at the Output_section_data_maps first.
|
|
const Output_section_data* posd = this->find_merge_section(object, shndx);
|
|
if (posd == NULL)
|
|
posd = this->find_relaxed_input_section(object, shndx);
|
|
if (posd != NULL)
|
|
{
|
|
section_offset_type output_offset;
|
|
bool found = posd->output_offset(object, shndx, offset, &output_offset);
|
|
gold_assert(found);
|
|
return output_offset;
|
|
}
|
|
|
|
// Fall back to the slow look-up.
|
|
for (Input_section_list::const_iterator p = this->input_sections_.begin();
|
|
p != this->input_sections_.end();
|
|
++p)
|
|
{
|
|
section_offset_type output_offset;
|
|
if (p->output_offset(object, shndx, offset, &output_offset))
|
|
return output_offset;
|
|
}
|
|
gold_unreachable();
|
|
}
|
|
|
|
// Return the output virtual address of OFFSET relative to the start
|
|
// of input section SHNDX in object OBJECT.
|
|
|
|
uint64_t
|
|
Output_section::output_address(const Relobj* object, unsigned int shndx,
|
|
off_t offset) const
|
|
{
|
|
uint64_t addr = this->address() + this->first_input_offset_;
|
|
|
|
// Look at the Output_section_data_maps first.
|
|
const Output_section_data* posd = this->find_merge_section(object, shndx);
|
|
if (posd == NULL)
|
|
posd = this->find_relaxed_input_section(object, shndx);
|
|
if (posd != NULL && posd->is_address_valid())
|
|
{
|
|
section_offset_type output_offset;
|
|
bool found = posd->output_offset(object, shndx, offset, &output_offset);
|
|
gold_assert(found);
|
|
return posd->address() + output_offset;
|
|
}
|
|
|
|
// Fall back to the slow look-up.
|
|
for (Input_section_list::const_iterator p = this->input_sections_.begin();
|
|
p != this->input_sections_.end();
|
|
++p)
|
|
{
|
|
addr = align_address(addr, p->addralign());
|
|
section_offset_type output_offset;
|
|
if (p->output_offset(object, shndx, offset, &output_offset))
|
|
{
|
|
if (output_offset == -1)
|
|
return -1ULL;
|
|
return addr + output_offset;
|
|
}
|
|
addr += p->data_size();
|
|
}
|
|
|
|
// If we get here, it means that we don't know the mapping for this
|
|
// input section. This might happen in principle if
|
|
// add_input_section were called before add_output_section_data.
|
|
// But it should never actually happen.
|
|
|
|
gold_unreachable();
|
|
}
|
|
|
|
// Find the output address of the start of the merged section for
|
|
// input section SHNDX in object OBJECT.
|
|
|
|
bool
|
|
Output_section::find_starting_output_address(const Relobj* object,
|
|
unsigned int shndx,
|
|
uint64_t* paddr) const
|
|
{
|
|
const Output_section_data* data = this->find_merge_section(object, shndx);
|
|
if (data == NULL)
|
|
return false;
|
|
|
|
// FIXME: This becomes a bottle-neck if we have many relaxed sections.
|
|
// Looking up the merge section map does not always work as we sometimes
|
|
// find a merge section without its address set.
|
|
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();
|
|
++p)
|
|
{
|
|
addr = align_address(addr, p->addralign());
|
|
|
|
// It would be nice if we could use the existing output_offset
|
|
// method to get the output offset of input offset 0.
|
|
// Unfortunately we don't know for sure that input offset 0 is
|
|
// mapped at all.
|
|
if (!p->is_input_section() && p->output_section_data() == data)
|
|
{
|
|
*paddr = addr;
|
|
return true;
|
|
}
|
|
|
|
addr += p->data_size();
|
|
}
|
|
|
|
// We couldn't find a merge output section for this input section.
|
|
return false;
|
|
}
|
|
|
|
// Update the data size of an Output_section.
|
|
|
|
void
|
|
Output_section::update_data_size()
|
|
{
|
|
if (this->input_sections_.empty())
|
|
return;
|
|
|
|
if (this->must_sort_attached_input_sections()
|
|
|| this->input_section_order_specified())
|
|
this->sort_attached_input_sections();
|
|
|
|
off_t off = this->first_input_offset_;
|
|
for (Input_section_list::iterator p = this->input_sections_.begin();
|
|
p != this->input_sections_.end();
|
|
++p)
|
|
{
|
|
off = align_address(off, p->addralign());
|
|
off += p->current_data_size();
|
|
}
|
|
|
|
this->set_current_data_size_for_child(off);
|
|
}
|
|
|
|
// Set the data size of an Output_section. This is where we handle
|
|
// setting the addresses of any Output_section_data objects.
|
|
|
|
void
|
|
Output_section::set_final_data_size()
|
|
{
|
|
off_t data_size;
|
|
|
|
if (this->input_sections_.empty())
|
|
data_size = this->current_data_size_for_child();
|
|
else
|
|
{
|
|
if (this->must_sort_attached_input_sections()
|
|
|| this->input_section_order_specified())
|
|
this->sort_attached_input_sections();
|
|
|
|
uint64_t address = this->address();
|
|
off_t startoff = this->offset();
|
|
off_t off = this->first_input_offset_;
|
|
for (Input_section_list::iterator p = this->input_sections_.begin();
|
|
p != this->input_sections_.end();
|
|
++p)
|
|
{
|
|
off = align_address(off, p->addralign());
|
|
p->set_address_and_file_offset(address + off, startoff + off,
|
|
startoff);
|
|
off += p->data_size();
|
|
}
|
|
data_size = off;
|
|
}
|
|
|
|
// For full incremental links, we want to allocate some patch space
|
|
// in most sections for subsequent incremental updates.
|
|
if (this->is_patch_space_allowed_ && parameters->incremental_full())
|
|
{
|
|
double pct = parameters->options().incremental_patch();
|
|
size_t extra = static_cast<size_t>(data_size * pct);
|
|
if (this->free_space_fill_ != NULL
|
|
&& this->free_space_fill_->minimum_hole_size() > extra)
|
|
extra = this->free_space_fill_->minimum_hole_size();
|
|
off_t new_size = align_address(data_size + extra, this->addralign());
|
|
this->patch_space_ = new_size - data_size;
|
|
gold_debug(DEBUG_INCREMENTAL,
|
|
"set_final_data_size: %08lx + %08lx: section %s",
|
|
static_cast<long>(data_size),
|
|
static_cast<long>(this->patch_space_),
|
|
this->name());
|
|
data_size = new_size;
|
|
}
|
|
|
|
this->set_data_size(data_size);
|
|
}
|
|
|
|
// Reset the address and file offset.
|
|
|
|
void
|
|
Output_section::do_reset_address_and_file_offset()
|
|
{
|
|
// An unallocated section has no address. Forcing this means that
|
|
// we don't need special treatment for symbols defined in debug
|
|
// sections. We do the same in the constructor. This does not
|
|
// apply to NOLOAD sections though.
|
|
if (((this->flags_ & elfcpp::SHF_ALLOC) == 0) && !this->is_noload_)
|
|
this->set_address(0);
|
|
|
|
for (Input_section_list::iterator p = this->input_sections_.begin();
|
|
p != this->input_sections_.end();
|
|
++p)
|
|
p->reset_address_and_file_offset();
|
|
|
|
// Remove any patch space that was added in set_final_data_size.
|
|
if (this->patch_space_ > 0)
|
|
{
|
|
this->set_current_data_size_for_child(this->current_data_size_for_child()
|
|
- this->patch_space_);
|
|
this->patch_space_ = 0;
|
|
}
|
|
}
|
|
|
|
// Return true if address and file offset have the values after reset.
|
|
|
|
bool
|
|
Output_section::do_address_and_file_offset_have_reset_values() const
|
|
{
|
|
if (this->is_offset_valid())
|
|
return false;
|
|
|
|
// An unallocated section has address 0 after its construction or a reset.
|
|
if ((this->flags_ & elfcpp::SHF_ALLOC) == 0)
|
|
return this->is_address_valid() && this->address() == 0;
|
|
else
|
|
return !this->is_address_valid();
|
|
}
|
|
|
|
// Set the TLS offset. Called only for SHT_TLS sections.
|
|
|
|
void
|
|
Output_section::do_set_tls_offset(uint64_t tls_base)
|
|
{
|
|
this->tls_offset_ = this->address() - tls_base;
|
|
}
|
|
|
|
// In a few cases we need to sort the input sections attached to an
|
|
// output section. This is used to implement the type of constructor
|
|
// priority ordering implemented by the GNU linker, in which the
|
|
// priority becomes part of the section name and the sections are
|
|
// sorted by name. We only do this for an output section if we see an
|
|
// attached input section matching ".ctors.*", ".dtors.*",
|
|
// ".init_array.*" or ".fini_array.*".
|
|
|
|
class Output_section::Input_section_sort_entry
|
|
{
|
|
public:
|
|
Input_section_sort_entry()
|
|
: input_section_(), index_(-1U), section_name_()
|
|
{ }
|
|
|
|
Input_section_sort_entry(const Input_section& input_section,
|
|
unsigned int index,
|
|
bool must_sort_attached_input_sections,
|
|
const char* output_section_name)
|
|
: input_section_(input_section), index_(index), section_name_()
|
|
{
|
|
if ((input_section.is_input_section()
|
|
|| input_section.is_relaxed_input_section())
|
|
&& must_sort_attached_input_sections)
|
|
{
|
|
// This is only called single-threaded from Layout::finalize,
|
|
// so it is OK to lock. Unfortunately we have no way to pass
|
|
// in a Task token.
|
|
const Task* dummy_task = reinterpret_cast<const Task*>(-1);
|
|
Object* obj = (input_section.is_input_section()
|
|
? input_section.relobj()
|
|
: input_section.relaxed_input_section()->relobj());
|
|
Task_lock_obj<Object> tl(dummy_task, obj);
|
|
|
|
// This is a slow operation, which should be cached in
|
|
// Layout::layout if this becomes a speed problem.
|
|
this->section_name_ = obj->section_name(input_section.shndx());
|
|
}
|
|
else if (input_section.is_output_section_data()
|
|
&& must_sort_attached_input_sections)
|
|
{
|
|
// For linker-generated sections, use the output section name.
|
|
this->section_name_.assign(output_section_name);
|
|
}
|
|
}
|
|
|
|
// Return the Input_section.
|
|
const Input_section&
|
|
input_section() const
|
|
{
|
|
gold_assert(this->index_ != -1U);
|
|
return this->input_section_;
|
|
}
|
|
|
|
// The index of this entry in the original list. This is used to
|
|
// make the sort stable.
|
|
unsigned int
|
|
index() const
|
|
{
|
|
gold_assert(this->index_ != -1U);
|
|
return this->index_;
|
|
}
|
|
|
|
// The section name.
|
|
const std::string&
|
|
section_name() const
|
|
{
|
|
return this->section_name_;
|
|
}
|
|
|
|
// Return true if the section name has a priority. This is assumed
|
|
// to be true if it has a dot after the initial dot.
|
|
bool
|
|
has_priority() const
|
|
{
|
|
return this->section_name_.find('.', 1) != std::string::npos;
|
|
}
|
|
|
|
// Return the priority. Believe it or not, gcc encodes the priority
|
|
// differently for .ctors/.dtors and .init_array/.fini_array
|
|
// sections.
|
|
unsigned int
|
|
get_priority() const
|
|
{
|
|
bool is_ctors;
|
|
if (is_prefix_of(".ctors.", this->section_name_.c_str())
|
|
|| is_prefix_of(".dtors.", this->section_name_.c_str()))
|
|
is_ctors = true;
|
|
else if (is_prefix_of(".init_array.", this->section_name_.c_str())
|
|
|| is_prefix_of(".fini_array.", this->section_name_.c_str()))
|
|
is_ctors = false;
|
|
else
|
|
return 0;
|
|
char* end;
|
|
unsigned long prio = strtoul((this->section_name_.c_str()
|
|
+ (is_ctors ? 7 : 12)),
|
|
&end, 10);
|
|
if (*end != '\0')
|
|
return 0;
|
|
else if (is_ctors)
|
|
return 65535 - prio;
|
|
else
|
|
return prio;
|
|
}
|
|
|
|
// Return true if this an input file whose base name matches
|
|
// FILE_NAME. The base name must have an extension of ".o", and
|
|
// must be exactly FILE_NAME.o or FILE_NAME, one character, ".o".
|
|
// This is to match crtbegin.o as well as crtbeginS.o without
|
|
// getting confused by other possibilities. Overall matching the
|
|
// file name this way is a dreadful hack, but the GNU linker does it
|
|
// in order to better support gcc, and we need to be compatible.
|
|
bool
|
|
match_file_name(const char* file_name) const
|
|
{
|
|
if (this->input_section_.is_output_section_data())
|
|
return false;
|
|
return Layout::match_file_name(this->input_section_.relobj(), file_name);
|
|
}
|
|
|
|
// Returns 1 if THIS should appear before S in section order, -1 if S
|
|
// appears before THIS and 0 if they are not comparable.
|
|
int
|
|
compare_section_ordering(const Input_section_sort_entry& s) const
|
|
{
|
|
unsigned int this_secn_index = this->input_section_.section_order_index();
|
|
unsigned int s_secn_index = s.input_section().section_order_index();
|
|
if (this_secn_index > 0 && s_secn_index > 0)
|
|
{
|
|
if (this_secn_index < s_secn_index)
|
|
return 1;
|
|
else if (this_secn_index > s_secn_index)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
private:
|
|
// The Input_section we are sorting.
|
|
Input_section input_section_;
|
|
// The index of this Input_section in the original list.
|
|
unsigned int index_;
|
|
// The section name if there is one.
|
|
std::string section_name_;
|
|
};
|
|
|
|
// Return true if S1 should come before S2 in the output section.
|
|
|
|
bool
|
|
Output_section::Input_section_sort_compare::operator()(
|
|
const Output_section::Input_section_sort_entry& s1,
|
|
const Output_section::Input_section_sort_entry& s2) const
|
|
{
|
|
// crtbegin.o must come first.
|
|
bool s1_begin = s1.match_file_name("crtbegin");
|
|
bool s2_begin = s2.match_file_name("crtbegin");
|
|
if (s1_begin || s2_begin)
|
|
{
|
|
if (!s1_begin)
|
|
return false;
|
|
if (!s2_begin)
|
|
return true;
|
|
return s1.index() < s2.index();
|
|
}
|
|
|
|
// crtend.o must come last.
|
|
bool s1_end = s1.match_file_name("crtend");
|
|
bool s2_end = s2.match_file_name("crtend");
|
|
if (s1_end || s2_end)
|
|
{
|
|
if (!s1_end)
|
|
return true;
|
|
if (!s2_end)
|
|
return false;
|
|
return s1.index() < s2.index();
|
|
}
|
|
|
|
// A section with a priority follows a section without a priority.
|
|
bool s1_has_priority = s1.has_priority();
|
|
bool s2_has_priority = s2.has_priority();
|
|
if (s1_has_priority && !s2_has_priority)
|
|
return false;
|
|
if (!s1_has_priority && s2_has_priority)
|
|
return true;
|
|
|
|
// Check if a section order exists for these sections through a section
|
|
// ordering file. If sequence_num is 0, an order does not exist.
|
|
int sequence_num = s1.compare_section_ordering(s2);
|
|
if (sequence_num != 0)
|
|
return sequence_num == 1;
|
|
|
|
// Otherwise we sort by name.
|
|
int compare = s1.section_name().compare(s2.section_name());
|
|
if (compare != 0)
|
|
return compare < 0;
|
|
|
|
// Otherwise we keep the input order.
|
|
return s1.index() < s2.index();
|
|
}
|
|
|
|
// Return true if S1 should come before S2 in an .init_array or .fini_array
|
|
// output section.
|
|
|
|
bool
|
|
Output_section::Input_section_sort_init_fini_compare::operator()(
|
|
const Output_section::Input_section_sort_entry& s1,
|
|
const Output_section::Input_section_sort_entry& s2) const
|
|
{
|
|
// A section without a priority follows a section with a priority.
|
|
// This is the reverse of .ctors and .dtors sections.
|
|
bool s1_has_priority = s1.has_priority();
|
|
bool s2_has_priority = s2.has_priority();
|
|
if (s1_has_priority && !s2_has_priority)
|
|
return true;
|
|
if (!s1_has_priority && s2_has_priority)
|
|
return false;
|
|
|
|
// .ctors and .dtors sections without priority come after
|
|
// .init_array and .fini_array sections without priority.
|
|
if (!s1_has_priority
|
|
&& (s1.section_name() == ".ctors" || s1.section_name() == ".dtors")
|
|
&& s1.section_name() != s2.section_name())
|
|
return false;
|
|
if (!s2_has_priority
|
|
&& (s2.section_name() == ".ctors" || s2.section_name() == ".dtors")
|
|
&& s2.section_name() != s1.section_name())
|
|
return true;
|
|
|
|
// Sort by priority if we can.
|
|
if (s1_has_priority)
|
|
{
|
|
unsigned int s1_prio = s1.get_priority();
|
|
unsigned int s2_prio = s2.get_priority();
|
|
if (s1_prio < s2_prio)
|
|
return true;
|
|
else if (s1_prio > s2_prio)
|
|
return false;
|
|
}
|
|
|
|
// Check if a section order exists for these sections through a section
|
|
// ordering file. If sequence_num is 0, an order does not exist.
|
|
int sequence_num = s1.compare_section_ordering(s2);
|
|
if (sequence_num != 0)
|
|
return sequence_num == 1;
|
|
|
|
// Otherwise we sort by name.
|
|
int compare = s1.section_name().compare(s2.section_name());
|
|
if (compare != 0)
|
|
return compare < 0;
|
|
|
|
// Otherwise we keep the input order.
|
|
return s1.index() < s2.index();
|
|
}
|
|
|
|
// Return true if S1 should come before S2. Sections that do not match
|
|
// any pattern in the section ordering file are placed ahead of the sections
|
|
// that match some pattern.
|
|
|
|
bool
|
|
Output_section::Input_section_sort_section_order_index_compare::operator()(
|
|
const Output_section::Input_section_sort_entry& s1,
|
|
const Output_section::Input_section_sort_entry& s2) const
|
|
{
|
|
unsigned int s1_secn_index = s1.input_section().section_order_index();
|
|
unsigned int s2_secn_index = s2.input_section().section_order_index();
|
|
|
|
// Keep input order if section ordering cannot determine order.
|
|
if (s1_secn_index == s2_secn_index)
|
|
return s1.index() < s2.index();
|
|
|
|
return s1_secn_index < s2_secn_index;
|
|
}
|
|
|
|
// Return true if S1 should come before S2. This is the sort comparison
|
|
// function for .text to sort sections with prefixes
|
|
// .text.{unlikely,exit,startup,hot} before other sections.
|
|
|
|
bool
|
|
Output_section::Input_section_sort_section_prefix_special_ordering_compare
|
|
::operator()(
|
|
const Output_section::Input_section_sort_entry& s1,
|
|
const Output_section::Input_section_sort_entry& s2) const
|
|
{
|
|
// Some input section names have special ordering requirements.
|
|
int o1 = Layout::special_ordering_of_input_section(s1.section_name().c_str());
|
|
int o2 = Layout::special_ordering_of_input_section(s2.section_name().c_str());
|
|
if (o1 != o2)
|
|
{
|
|
if (o1 < 0)
|
|
return false;
|
|
else if (o2 < 0)
|
|
return true;
|
|
else
|
|
return o1 < o2;
|
|
}
|
|
|
|
// Keep input order otherwise.
|
|
return s1.index() < s2.index();
|
|
}
|
|
|
|
// Return true if S1 should come before S2. This is the sort comparison
|
|
// function for sections to sort them by name.
|
|
|
|
bool
|
|
Output_section::Input_section_sort_section_name_compare
|
|
::operator()(
|
|
const Output_section::Input_section_sort_entry& s1,
|
|
const Output_section::Input_section_sort_entry& s2) const
|
|
{
|
|
// We sort by name.
|
|
int compare = s1.section_name().compare(s2.section_name());
|
|
if (compare != 0)
|
|
return compare < 0;
|
|
|
|
// Keep input order otherwise.
|
|
return s1.index() < s2.index();
|
|
}
|
|
|
|
// This updates the section order index of input sections according to the
|
|
// the order specified in the mapping from Section id to order index.
|
|
|
|
void
|
|
Output_section::update_section_layout(
|
|
const Section_layout_order* order_map)
|
|
{
|
|
for (Input_section_list::iterator p = this->input_sections_.begin();
|
|
p != this->input_sections_.end();
|
|
++p)
|
|
{
|
|
if (p->is_input_section()
|
|
|| p->is_relaxed_input_section())
|
|
{
|
|
Relobj* obj = (p->is_input_section()
|
|
? p->relobj()
|
|
: p->relaxed_input_section()->relobj());
|
|
unsigned int shndx = p->shndx();
|
|
Section_layout_order::const_iterator it
|
|
= order_map->find(Section_id(obj, shndx));
|
|
if (it == order_map->end())
|
|
continue;
|
|
unsigned int section_order_index = it->second;
|
|
if (section_order_index != 0)
|
|
{
|
|
p->set_section_order_index(section_order_index);
|
|
this->set_input_section_order_specified();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort the input sections attached to an output section.
|
|
|
|
void
|
|
Output_section::sort_attached_input_sections()
|
|
{
|
|
if (this->attached_input_sections_are_sorted_)
|
|
return;
|
|
|
|
if (this->checkpoint_ != NULL
|
|
&& !this->checkpoint_->input_sections_saved())
|
|
this->checkpoint_->save_input_sections();
|
|
|
|
// The only thing we know about an input section is the object and
|
|
// the section index. We need the section name. Recomputing this
|
|
// is slow but this is an unusual case. If this becomes a speed
|
|
// problem we can cache the names as required in Layout::layout.
|
|
|
|
// We start by building a larger vector holding a copy of each
|
|
// Input_section, plus its current index in the list and its name.
|
|
std::vector<Input_section_sort_entry> sort_list;
|
|
|
|
unsigned int i = 0;
|
|
for (Input_section_list::iterator p = this->input_sections_.begin();
|
|
p != this->input_sections_.end();
|
|
++p, ++i)
|
|
sort_list.push_back(Input_section_sort_entry(*p, i,
|
|
this->must_sort_attached_input_sections(),
|
|
this->name()));
|
|
|
|
// Sort the input sections.
|
|
if (this->must_sort_attached_input_sections())
|
|
{
|
|
if (this->type() == elfcpp::SHT_PREINIT_ARRAY
|
|
|| this->type() == elfcpp::SHT_INIT_ARRAY
|
|
|| this->type() == elfcpp::SHT_FINI_ARRAY)
|
|
std::sort(sort_list.begin(), sort_list.end(),
|
|
Input_section_sort_init_fini_compare());
|
|
else if (strcmp(parameters->options().sort_section(), "name") == 0)
|
|
std::sort(sort_list.begin(), sort_list.end(),
|
|
Input_section_sort_section_name_compare());
|
|
else if (strcmp(this->name(), ".text") == 0)
|
|
std::sort(sort_list.begin(), sort_list.end(),
|
|
Input_section_sort_section_prefix_special_ordering_compare());
|
|
else
|
|
std::sort(sort_list.begin(), sort_list.end(),
|
|
Input_section_sort_compare());
|
|
}
|
|
else
|
|
{
|
|
gold_assert(this->input_section_order_specified());
|
|
std::sort(sort_list.begin(), sort_list.end(),
|
|
Input_section_sort_section_order_index_compare());
|
|
}
|
|
|
|
// Copy the sorted input sections back to our list.
|
|
this->input_sections_.clear();
|
|
for (std::vector<Input_section_sort_entry>::iterator p = sort_list.begin();
|
|
p != sort_list.end();
|
|
++p)
|
|
this->input_sections_.push_back(p->input_section());
|
|
sort_list.clear();
|
|
|
|
// Remember that we sorted the input sections, since we might get
|
|
// called again.
|
|
this->attached_input_sections_are_sorted_ = true;
|
|
}
|
|
|
|
// Write the section header to *OSHDR.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Output_section::write_header(const Layout* layout,
|
|
const Stringpool* secnamepool,
|
|
elfcpp::Shdr_write<size, big_endian>* oshdr) const
|
|
{
|
|
oshdr->put_sh_name(secnamepool->get_offset(this->name_));
|
|
oshdr->put_sh_type(this->type_);
|
|
|
|
elfcpp::Elf_Xword flags = this->flags_;
|
|
if (this->info_section_ != NULL && this->info_uses_section_index_)
|
|
flags |= elfcpp::SHF_INFO_LINK;
|
|
oshdr->put_sh_flags(flags);
|
|
|
|
oshdr->put_sh_addr(this->address());
|
|
oshdr->put_sh_offset(this->offset());
|
|
oshdr->put_sh_size(this->data_size());
|
|
if (this->link_section_ != NULL)
|
|
oshdr->put_sh_link(this->link_section_->out_shndx());
|
|
else if (this->should_link_to_symtab_)
|
|
oshdr->put_sh_link(layout->symtab_section_shndx());
|
|
else if (this->should_link_to_dynsym_)
|
|
oshdr->put_sh_link(layout->dynsym_section()->out_shndx());
|
|
else
|
|
oshdr->put_sh_link(this->link_);
|
|
|
|
elfcpp::Elf_Word info;
|
|
if (this->info_section_ != NULL)
|
|
{
|
|
if (this->info_uses_section_index_)
|
|
info = this->info_section_->out_shndx();
|
|
else
|
|
info = this->info_section_->symtab_index();
|
|
}
|
|
else if (this->info_symndx_ != NULL)
|
|
info = this->info_symndx_->symtab_index();
|
|
else
|
|
info = this->info_;
|
|
oshdr->put_sh_info(info);
|
|
|
|
oshdr->put_sh_addralign(this->addralign_);
|
|
oshdr->put_sh_entsize(this->entsize_);
|
|
}
|
|
|
|
// Write out the data. For input sections the data is written out by
|
|
// Object::relocate, but we have to handle Output_section_data objects
|
|
// here.
|
|
|
|
void
|
|
Output_section::do_write(Output_file* of)
|
|
{
|
|
gold_assert(!this->requires_postprocessing());
|
|
|
|
// If the target performs relaxation, we delay filler generation until now.
|
|
gold_assert(!this->generate_code_fills_at_write_ || this->fills_.empty());
|
|
|
|
off_t output_section_file_offset = this->offset();
|
|
for (Fill_list::iterator p = this->fills_.begin();
|
|
p != this->fills_.end();
|
|
++p)
|
|
{
|
|
std::string fill_data(parameters->target().code_fill(p->length()));
|
|
of->write(output_section_file_offset + p->section_offset(),
|
|
fill_data.data(), fill_data.size());
|
|
}
|
|
|
|
off_t off = this->offset() + this->first_input_offset_;
|
|
for (Input_section_list::iterator p = this->input_sections_.begin();
|
|
p != this->input_sections_.end();
|
|
++p)
|
|
{
|
|
off_t aligned_off = align_address(off, p->addralign());
|
|
if (this->generate_code_fills_at_write_ && (off != aligned_off))
|
|
{
|
|
size_t fill_len = aligned_off - off;
|
|
std::string fill_data(parameters->target().code_fill(fill_len));
|
|
of->write(off, fill_data.data(), fill_data.size());
|
|
}
|
|
|
|
p->write(of);
|
|
off = aligned_off + p->data_size();
|
|
}
|
|
|
|
// For incremental links, fill in unused chunks in debug sections
|
|
// with dummy compilation unit headers.
|
|
if (this->free_space_fill_ != NULL)
|
|
{
|
|
for (Free_list::Const_iterator p = this->free_list_.begin();
|
|
p != this->free_list_.end();
|
|
++p)
|
|
{
|
|
off_t off = p->start_;
|
|
size_t len = p->end_ - off;
|
|
this->free_space_fill_->write(of, this->offset() + off, len);
|
|
}
|
|
if (this->patch_space_ > 0)
|
|
{
|
|
off_t off = this->current_data_size_for_child() - this->patch_space_;
|
|
this->free_space_fill_->write(of, this->offset() + off,
|
|
this->patch_space_);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If a section requires postprocessing, create the buffer to use.
|
|
|
|
void
|
|
Output_section::create_postprocessing_buffer()
|
|
{
|
|
gold_assert(this->requires_postprocessing());
|
|
|
|
if (this->postprocessing_buffer_ != NULL)
|
|
return;
|
|
|
|
if (!this->input_sections_.empty())
|
|
{
|
|
off_t off = this->first_input_offset_;
|
|
for (Input_section_list::iterator p = this->input_sections_.begin();
|
|
p != this->input_sections_.end();
|
|
++p)
|
|
{
|
|
off = align_address(off, p->addralign());
|
|
p->finalize_data_size();
|
|
off += p->data_size();
|
|
}
|
|
this->set_current_data_size_for_child(off);
|
|
}
|
|
|
|
off_t buffer_size = this->current_data_size_for_child();
|
|
this->postprocessing_buffer_ = new unsigned char[buffer_size];
|
|
}
|
|
|
|
// Write all the data of an Output_section into the postprocessing
|
|
// buffer. This is used for sections which require postprocessing,
|
|
// such as compression. Input sections are handled by
|
|
// Object::Relocate.
|
|
|
|
void
|
|
Output_section::write_to_postprocessing_buffer()
|
|
{
|
|
gold_assert(this->requires_postprocessing());
|
|
|
|
// If the target performs relaxation, we delay filler generation until now.
|
|
gold_assert(!this->generate_code_fills_at_write_ || this->fills_.empty());
|
|
|
|
unsigned char* buffer = this->postprocessing_buffer();
|
|
for (Fill_list::iterator p = this->fills_.begin();
|
|
p != this->fills_.end();
|
|
++p)
|
|
{
|
|
std::string fill_data(parameters->target().code_fill(p->length()));
|
|
memcpy(buffer + p->section_offset(), fill_data.data(),
|
|
fill_data.size());
|
|
}
|
|
|
|
off_t off = this->first_input_offset_;
|
|
for (Input_section_list::iterator p = this->input_sections_.begin();
|
|
p != this->input_sections_.end();
|
|
++p)
|
|
{
|
|
off_t aligned_off = align_address(off, p->addralign());
|
|
if (this->generate_code_fills_at_write_ && (off != aligned_off))
|
|
{
|
|
size_t fill_len = aligned_off - off;
|
|
std::string fill_data(parameters->target().code_fill(fill_len));
|
|
memcpy(buffer + off, fill_data.data(), fill_data.size());
|
|
}
|
|
|
|
p->write_to_buffer(buffer + aligned_off);
|
|
off = aligned_off + p->data_size();
|
|
}
|
|
}
|
|
|
|
// Get the input sections for linker script processing. We leave
|
|
// behind the Output_section_data entries. Note that this may be
|
|
// slightly incorrect for merge sections. We will leave them behind,
|
|
// but it is possible that the script says that they should follow
|
|
// some other input sections, as in:
|
|
// .rodata { *(.rodata) *(.rodata.cst*) }
|
|
// For that matter, we don't handle this correctly:
|
|
// .rodata { foo.o(.rodata.cst*) *(.rodata.cst*) }
|
|
// With luck this will never matter.
|
|
|
|
uint64_t
|
|
Output_section::get_input_sections(
|
|
uint64_t address,
|
|
const std::string& fill,
|
|
std::list<Input_section>* input_sections)
|
|
{
|
|
if (this->checkpoint_ != NULL
|
|
&& !this->checkpoint_->input_sections_saved())
|
|
this->checkpoint_->save_input_sections();
|
|
|
|
// Invalidate fast look-up maps.
|
|
this->lookup_maps_->invalidate();
|
|
|
|
uint64_t orig_address = address;
|
|
|
|
address = align_address(address, this->addralign());
|
|
|
|
Input_section_list remaining;
|
|
for (Input_section_list::iterator p = this->input_sections_.begin();
|
|
p != this->input_sections_.end();
|
|
++p)
|
|
{
|
|
if (p->is_input_section()
|
|
|| p->is_relaxed_input_section()
|
|
|| p->is_merge_section())
|
|
input_sections->push_back(*p);
|
|
else
|
|
{
|
|
uint64_t aligned_address = align_address(address, p->addralign());
|
|
if (aligned_address != address && !fill.empty())
|
|
{
|
|
section_size_type length =
|
|
convert_to_section_size_type(aligned_address - address);
|
|
std::string this_fill;
|
|
this_fill.reserve(length);
|
|
while (this_fill.length() + fill.length() <= length)
|
|
this_fill += fill;
|
|
if (this_fill.length() < length)
|
|
this_fill.append(fill, 0, length - this_fill.length());
|
|
|
|
Output_section_data* posd = new Output_data_const(this_fill, 0);
|
|
remaining.push_back(Input_section(posd));
|
|
}
|
|
address = aligned_address;
|
|
|
|
remaining.push_back(*p);
|
|
|
|
p->finalize_data_size();
|
|
address += p->data_size();
|
|
}
|
|
}
|
|
|
|
this->input_sections_.swap(remaining);
|
|
this->first_input_offset_ = 0;
|
|
|
|
uint64_t data_size = address - orig_address;
|
|
this->set_current_data_size_for_child(data_size);
|
|
return data_size;
|
|
}
|
|
|
|
// Add a script input section. SIS is an Output_section::Input_section,
|
|
// which can be either a plain input section or a special input section like
|
|
// a relaxed input section. For a special input section, its size must be
|
|
// finalized.
|
|
|
|
void
|
|
Output_section::add_script_input_section(const Input_section& sis)
|
|
{
|
|
uint64_t data_size = sis.data_size();
|
|
uint64_t addralign = sis.addralign();
|
|
if (addralign > this->addralign_)
|
|
this->addralign_ = addralign;
|
|
|
|
off_t offset_in_section = this->current_data_size_for_child();
|
|
off_t aligned_offset_in_section = align_address(offset_in_section,
|
|
addralign);
|
|
|
|
this->set_current_data_size_for_child(aligned_offset_in_section
|
|
+ data_size);
|
|
|
|
this->input_sections_.push_back(sis);
|
|
|
|
// Update fast lookup maps if necessary.
|
|
if (this->lookup_maps_->is_valid())
|
|
{
|
|
if (sis.is_relaxed_input_section())
|
|
{
|
|
Output_relaxed_input_section* poris = sis.relaxed_input_section();
|
|
this->lookup_maps_->add_relaxed_input_section(poris->relobj(),
|
|
poris->shndx(), poris);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Save states for relaxation.
|
|
|
|
void
|
|
Output_section::save_states()
|
|
{
|
|
gold_assert(this->checkpoint_ == NULL);
|
|
Checkpoint_output_section* checkpoint =
|
|
new Checkpoint_output_section(this->addralign_, this->flags_,
|
|
this->input_sections_,
|
|
this->first_input_offset_,
|
|
this->attached_input_sections_are_sorted_);
|
|
this->checkpoint_ = checkpoint;
|
|
gold_assert(this->fills_.empty());
|
|
}
|
|
|
|
void
|
|
Output_section::discard_states()
|
|
{
|
|
gold_assert(this->checkpoint_ != NULL);
|
|
delete this->checkpoint_;
|
|
this->checkpoint_ = NULL;
|
|
gold_assert(this->fills_.empty());
|
|
|
|
// Simply invalidate the fast lookup maps since we do not keep
|
|
// track of them.
|
|
this->lookup_maps_->invalidate();
|
|
}
|
|
|
|
void
|
|
Output_section::restore_states()
|
|
{
|
|
gold_assert(this->checkpoint_ != NULL);
|
|
Checkpoint_output_section* checkpoint = this->checkpoint_;
|
|
|
|
this->addralign_ = checkpoint->addralign();
|
|
this->flags_ = checkpoint->flags();
|
|
this->first_input_offset_ = checkpoint->first_input_offset();
|
|
|
|
if (!checkpoint->input_sections_saved())
|
|
{
|
|
// If we have not copied the input sections, just resize it.
|
|
size_t old_size = checkpoint->input_sections_size();
|
|
gold_assert(this->input_sections_.size() >= old_size);
|
|
this->input_sections_.resize(old_size);
|
|
}
|
|
else
|
|
{
|
|
// We need to copy the whole list. This is not efficient for
|
|
// extremely large output with hundreads of thousands of input
|
|
// objects. We may need to re-think how we should pass sections
|
|
// to scripts.
|
|
this->input_sections_ = *checkpoint->input_sections();
|
|
}
|
|
|
|
this->attached_input_sections_are_sorted_ =
|
|
checkpoint->attached_input_sections_are_sorted();
|
|
|
|
// Simply invalidate the fast lookup maps since we do not keep
|
|
// track of them.
|
|
this->lookup_maps_->invalidate();
|
|
}
|
|
|
|
// Update the section offsets of input sections in this. This is required if
|
|
// relaxation causes some input sections to change sizes.
|
|
|
|
void
|
|
Output_section::adjust_section_offsets()
|
|
{
|
|
if (!this->section_offsets_need_adjustment_)
|
|
return;
|
|
|
|
off_t off = 0;
|
|
for (Input_section_list::iterator p = this->input_sections_.begin();
|
|
p != this->input_sections_.end();
|
|
++p)
|
|
{
|
|
off = align_address(off, p->addralign());
|
|
if (p->is_input_section())
|
|
p->relobj()->set_section_offset(p->shndx(), off);
|
|
off += p->data_size();
|
|
}
|
|
|
|
this->section_offsets_need_adjustment_ = false;
|
|
}
|
|
|
|
// 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
|
|
Output_section::print_merge_stats()
|
|
{
|
|
Input_section_list::iterator p;
|
|
for (p = this->input_sections_.begin();
|
|
p != this->input_sections_.end();
|
|
++p)
|
|
p->print_merge_stats(this->name_);
|
|
}
|
|
|
|
// Set a fixed layout for the section. Used for incremental update links.
|
|
|
|
void
|
|
Output_section::set_fixed_layout(uint64_t sh_addr, off_t sh_offset,
|
|
off_t sh_size, uint64_t sh_addralign)
|
|
{
|
|
this->addralign_ = sh_addralign;
|
|
this->set_current_data_size(sh_size);
|
|
if ((this->flags_ & elfcpp::SHF_ALLOC) != 0)
|
|
this->set_address(sh_addr);
|
|
this->set_file_offset(sh_offset);
|
|
this->finalize_data_size();
|
|
this->free_list_.init(sh_size, false);
|
|
this->has_fixed_layout_ = true;
|
|
}
|
|
|
|
// Reserve space within the fixed layout for the section. Used for
|
|
// incremental update links.
|
|
|
|
void
|
|
Output_section::reserve(uint64_t sh_offset, uint64_t sh_size)
|
|
{
|
|
this->free_list_.remove(sh_offset, sh_offset + sh_size);
|
|
}
|
|
|
|
// Allocate space from the free list for the section. Used for
|
|
// incremental update links.
|
|
|
|
off_t
|
|
Output_section::allocate(off_t len, uint64_t addralign)
|
|
{
|
|
return this->free_list_.allocate(len, addralign, 0);
|
|
}
|
|
|
|
// Output segment methods.
|
|
|
|
Output_segment::Output_segment(elfcpp::Elf_Word type, elfcpp::Elf_Word flags)
|
|
: vaddr_(0),
|
|
paddr_(0),
|
|
memsz_(0),
|
|
max_align_(0),
|
|
min_p_align_(0),
|
|
offset_(0),
|
|
filesz_(0),
|
|
type_(type),
|
|
flags_(flags),
|
|
is_max_align_known_(false),
|
|
are_addresses_set_(false),
|
|
is_large_data_segment_(false),
|
|
is_unique_segment_(false)
|
|
{
|
|
// The ELF ABI specifies that a PT_TLS segment always has PF_R as
|
|
// the flags.
|
|
if (type == elfcpp::PT_TLS)
|
|
this->flags_ = elfcpp::PF_R;
|
|
}
|
|
|
|
// Add an Output_section to a PT_LOAD Output_segment.
|
|
|
|
void
|
|
Output_segment::add_output_section_to_load(Layout* layout,
|
|
Output_section* os,
|
|
elfcpp::Elf_Word seg_flags)
|
|
{
|
|
gold_assert(this->type() == elfcpp::PT_LOAD);
|
|
gold_assert((os->flags() & elfcpp::SHF_ALLOC) != 0);
|
|
gold_assert(!this->is_max_align_known_);
|
|
gold_assert(os->is_large_data_section() == this->is_large_data_segment());
|
|
|
|
this->update_flags_for_output_section(seg_flags);
|
|
|
|
// We don't want to change the ordering if we have a linker script
|
|
// with a SECTIONS clause.
|
|
Output_section_order order = os->order();
|
|
if (layout->script_options()->saw_sections_clause())
|
|
order = static_cast<Output_section_order>(0);
|
|
else
|
|
gold_assert(order != ORDER_INVALID);
|
|
|
|
this->output_lists_[order].push_back(os);
|
|
}
|
|
|
|
// Add an Output_section to a non-PT_LOAD Output_segment.
|
|
|
|
void
|
|
Output_segment::add_output_section_to_nonload(Output_section* os,
|
|
elfcpp::Elf_Word seg_flags)
|
|
{
|
|
gold_assert(this->type() != elfcpp::PT_LOAD);
|
|
gold_assert((os->flags() & elfcpp::SHF_ALLOC) != 0);
|
|
gold_assert(!this->is_max_align_known_);
|
|
|
|
this->update_flags_for_output_section(seg_flags);
|
|
|
|
this->output_lists_[0].push_back(os);
|
|
}
|
|
|
|
// Remove an Output_section from this segment. It is an error if it
|
|
// is not present.
|
|
|
|
void
|
|
Output_segment::remove_output_section(Output_section* os)
|
|
{
|
|
for (int i = 0; i < static_cast<int>(ORDER_MAX); ++i)
|
|
{
|
|
Output_data_list* pdl = &this->output_lists_[i];
|
|
for (Output_data_list::iterator p = pdl->begin(); p != pdl->end(); ++p)
|
|
{
|
|
if (*p == os)
|
|
{
|
|
pdl->erase(p);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
gold_unreachable();
|
|
}
|
|
|
|
// Add an Output_data (which need not be an Output_section) to the
|
|
// start of a segment.
|
|
|
|
void
|
|
Output_segment::add_initial_output_data(Output_data* od)
|
|
{
|
|
gold_assert(!this->is_max_align_known_);
|
|
Output_data_list::iterator p = this->output_lists_[0].begin();
|
|
this->output_lists_[0].insert(p, od);
|
|
}
|
|
|
|
// Return true if this segment has any sections which hold actual
|
|
// data, rather than being a BSS section.
|
|
|
|
bool
|
|
Output_segment::has_any_data_sections() const
|
|
{
|
|
for (int i = 0; i < static_cast<int>(ORDER_MAX); ++i)
|
|
{
|
|
const Output_data_list* pdl = &this->output_lists_[i];
|
|
for (Output_data_list::const_iterator p = pdl->begin();
|
|
p != pdl->end();
|
|
++p)
|
|
{
|
|
if (!(*p)->is_section())
|
|
return true;
|
|
if ((*p)->output_section()->type() != elfcpp::SHT_NOBITS)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Return whether the first data section (not counting TLS sections)
|
|
// is a relro section.
|
|
|
|
bool
|
|
Output_segment::is_first_section_relro() const
|
|
{
|
|
for (int i = 0; i < static_cast<int>(ORDER_MAX); ++i)
|
|
{
|
|
if (i == static_cast<int>(ORDER_TLS_BSS))
|
|
continue;
|
|
const Output_data_list* pdl = &this->output_lists_[i];
|
|
if (!pdl->empty())
|
|
{
|
|
Output_data* p = pdl->front();
|
|
return p->is_section() && p->output_section()->is_relro();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Return the maximum alignment of the Output_data in Output_segment.
|
|
|
|
uint64_t
|
|
Output_segment::maximum_alignment()
|
|
{
|
|
if (!this->is_max_align_known_)
|
|
{
|
|
for (int i = 0; i < static_cast<int>(ORDER_MAX); ++i)
|
|
{
|
|
const Output_data_list* pdl = &this->output_lists_[i];
|
|
uint64_t addralign = Output_segment::maximum_alignment_list(pdl);
|
|
if (addralign > this->max_align_)
|
|
this->max_align_ = addralign;
|
|
}
|
|
this->is_max_align_known_ = true;
|
|
}
|
|
|
|
return this->max_align_;
|
|
}
|
|
|
|
// Return the maximum alignment of a list of Output_data.
|
|
|
|
uint64_t
|
|
Output_segment::maximum_alignment_list(const Output_data_list* pdl)
|
|
{
|
|
uint64_t ret = 0;
|
|
for (Output_data_list::const_iterator p = pdl->begin();
|
|
p != pdl->end();
|
|
++p)
|
|
{
|
|
uint64_t addralign = (*p)->addralign();
|
|
if (addralign > ret)
|
|
ret = addralign;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
// Return whether this segment has any dynamic relocs.
|
|
|
|
bool
|
|
Output_segment::has_dynamic_reloc() const
|
|
{
|
|
for (int i = 0; i < static_cast<int>(ORDER_MAX); ++i)
|
|
if (this->has_dynamic_reloc_list(&this->output_lists_[i]))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// Return whether this Output_data_list has any dynamic relocs.
|
|
|
|
bool
|
|
Output_segment::has_dynamic_reloc_list(const Output_data_list* pdl) const
|
|
{
|
|
for (Output_data_list::const_iterator p = pdl->begin();
|
|
p != pdl->end();
|
|
++p)
|
|
if ((*p)->has_dynamic_reloc())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
// Set the section addresses for an Output_segment. If RESET is true,
|
|
// reset the addresses first. ADDR is the address and *POFF is the
|
|
// file offset. Set the section indexes starting with *PSHNDX.
|
|
// INCREASE_RELRO is the size of the portion of the first non-relro
|
|
// section that should be included in the PT_GNU_RELRO segment.
|
|
// If this segment has relro sections, and has been aligned for
|
|
// that purpose, set *HAS_RELRO to TRUE. Return the address of
|
|
// the immediately following segment. Update *HAS_RELRO, *POFF,
|
|
// and *PSHNDX.
|
|
|
|
uint64_t
|
|
Output_segment::set_section_addresses(const Target* target,
|
|
Layout* layout, bool reset,
|
|
uint64_t addr,
|
|
unsigned int* increase_relro,
|
|
bool* has_relro,
|
|
off_t* poff,
|
|
unsigned int* pshndx)
|
|
{
|
|
gold_assert(this->type_ == elfcpp::PT_LOAD);
|
|
|
|
uint64_t last_relro_pad = 0;
|
|
off_t orig_off = *poff;
|
|
|
|
bool in_tls = false;
|
|
|
|
// If we have relro sections, we need to pad forward now so that the
|
|
// relro sections plus INCREASE_RELRO end on an abi page boundary.
|
|
if (parameters->options().relro()
|
|
&& this->is_first_section_relro()
|
|
&& (!this->are_addresses_set_ || reset))
|
|
{
|
|
uint64_t relro_size = 0;
|
|
off_t off = *poff;
|
|
uint64_t max_align = 0;
|
|
for (int i = 0; i <= static_cast<int>(ORDER_RELRO_LAST); ++i)
|
|
{
|
|
Output_data_list* pdl = &this->output_lists_[i];
|
|
Output_data_list::iterator p;
|
|
for (p = pdl->begin(); p != pdl->end(); ++p)
|
|
{
|
|
if (!(*p)->is_section())
|
|
break;
|
|
uint64_t align = (*p)->addralign();
|
|
if (align > max_align)
|
|
max_align = align;
|
|
if ((*p)->is_section_flag_set(elfcpp::SHF_TLS))
|
|
in_tls = true;
|
|
else if (in_tls)
|
|
{
|
|
// Align the first non-TLS section to the alignment
|
|
// of the TLS segment.
|
|
align = max_align;
|
|
in_tls = false;
|
|
}
|
|
// Ignore the size of the .tbss section.
|
|
if ((*p)->is_section_flag_set(elfcpp::SHF_TLS)
|
|
&& (*p)->is_section_type(elfcpp::SHT_NOBITS))
|
|
continue;
|
|
relro_size = align_address(relro_size, align);
|
|
if ((*p)->is_address_valid())
|
|
relro_size += (*p)->data_size();
|
|
else
|
|
{
|
|
// FIXME: This could be faster.
|
|
(*p)->set_address_and_file_offset(relro_size,
|
|
relro_size);
|
|
relro_size += (*p)->data_size();
|
|
(*p)->reset_address_and_file_offset();
|
|
}
|
|
}
|
|
if (p != pdl->end())
|
|
break;
|
|
}
|
|
relro_size += *increase_relro;
|
|
// Pad the total relro size to a multiple of the maximum
|
|
// section alignment seen.
|
|
uint64_t aligned_size = align_address(relro_size, max_align);
|
|
// Note the amount of padding added after the last relro section.
|
|
last_relro_pad = aligned_size - relro_size;
|
|
*has_relro = true;
|
|
|
|
uint64_t page_align = parameters->target().abi_pagesize();
|
|
|
|
// Align to offset N such that (N + RELRO_SIZE) % PAGE_ALIGN == 0.
|
|
uint64_t desired_align = page_align - (aligned_size % page_align);
|
|
if (desired_align < off % page_align)
|
|
off += page_align;
|
|
off += desired_align - off % page_align;
|
|
addr += off - orig_off;
|
|
orig_off = off;
|
|
*poff = off;
|
|
}
|
|
|
|
if (!reset && this->are_addresses_set_)
|
|
{
|
|
gold_assert(this->paddr_ == addr);
|
|
addr = this->vaddr_;
|
|
}
|
|
else
|
|
{
|
|
this->vaddr_ = addr;
|
|
this->paddr_ = addr;
|
|
this->are_addresses_set_ = true;
|
|
}
|
|
|
|
in_tls = false;
|
|
|
|
this->offset_ = orig_off;
|
|
|
|
off_t off = 0;
|
|
off_t foff = *poff;
|
|
uint64_t ret = 0;
|
|
for (int i = 0; i < static_cast<int>(ORDER_MAX); ++i)
|
|
{
|
|
if (i == static_cast<int>(ORDER_RELRO_LAST))
|
|
{
|
|
*poff += last_relro_pad;
|
|
foff += last_relro_pad;
|
|
addr += last_relro_pad;
|
|
if (this->output_lists_[i].empty())
|
|
{
|
|
// If there is nothing in the ORDER_RELRO_LAST list,
|
|
// the padding will occur at the end of the relro
|
|
// segment, and we need to add it to *INCREASE_RELRO.
|
|
*increase_relro += last_relro_pad;
|
|
}
|
|
}
|
|
addr = this->set_section_list_addresses(layout, reset,
|
|
&this->output_lists_[i],
|
|
addr, poff, &foff, pshndx,
|
|
&in_tls);
|
|
|
|
// FOFF tracks the last offset used for the file image,
|
|
// and *POFF tracks the last offset used for the memory image.
|
|
// When not using a linker script, bss sections should all
|
|
// be processed in the ORDER_SMALL_BSS and later buckets.
|
|
gold_assert(*poff == foff
|
|
|| i == static_cast<int>(ORDER_TLS_BSS)
|
|
|| i >= static_cast<int>(ORDER_SMALL_BSS)
|
|
|| layout->script_options()->saw_sections_clause());
|
|
|
|
this->filesz_ = foff - orig_off;
|
|
off = foff;
|
|
|
|
ret = addr;
|
|
}
|
|
|
|
// 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
|
|
// segment is aligned.
|
|
if (in_tls)
|
|
{
|
|
uint64_t segment_align = layout->tls_segment()->maximum_alignment();
|
|
*poff = align_address(*poff, segment_align);
|
|
}
|
|
|
|
this->memsz_ = *poff - orig_off;
|
|
|
|
// Ignore the file offset adjustments made by the BSS Output_data
|
|
// objects.
|
|
*poff = off;
|
|
|
|
// If code segments must contain only code, and this code segment is
|
|
// page-aligned in the file, then fill it out to a whole page with
|
|
// code fill (the tail of the segment will not be within any section).
|
|
// Thus the entire code segment can be mapped from the file as whole
|
|
// pages and that mapping will contain only valid instructions.
|
|
if (target->isolate_execinstr() && (this->flags() & elfcpp::PF_X) != 0)
|
|
{
|
|
uint64_t abi_pagesize = target->abi_pagesize();
|
|
if (orig_off % abi_pagesize == 0 && off % abi_pagesize != 0)
|
|
{
|
|
size_t fill_size = abi_pagesize - (off % abi_pagesize);
|
|
|
|
std::string fill_data;
|
|
if (target->has_code_fill())
|
|
fill_data = target->code_fill(fill_size);
|
|
else
|
|
fill_data.resize(fill_size); // Zero fill.
|
|
|
|
Output_data_const* fill = new Output_data_const(fill_data, 0);
|
|
fill->set_address(this->vaddr_ + this->memsz_);
|
|
fill->set_file_offset(off);
|
|
layout->add_relax_output(fill);
|
|
|
|
off += fill_size;
|
|
gold_assert(off % abi_pagesize == 0);
|
|
ret += fill_size;
|
|
gold_assert(ret % abi_pagesize == 0);
|
|
|
|
gold_assert((uint64_t) this->filesz_ == this->memsz_);
|
|
this->memsz_ = this->filesz_ += fill_size;
|
|
|
|
*poff = off;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Set the addresses and file offsets in a list of Output_data
|
|
// structures.
|
|
|
|
uint64_t
|
|
Output_segment::set_section_list_addresses(Layout* layout, bool reset,
|
|
Output_data_list* pdl,
|
|
uint64_t addr, off_t* poff,
|
|
off_t* pfoff,
|
|
unsigned int* pshndx,
|
|
bool* in_tls)
|
|
{
|
|
off_t startoff = *poff;
|
|
// For incremental updates, we may allocate non-fixed sections from
|
|
// free space in the file. This keeps track of the high-water mark.
|
|
off_t maxoff = startoff;
|
|
|
|
off_t off = startoff;
|
|
off_t foff = *pfoff;
|
|
for (Output_data_list::iterator p = pdl->begin();
|
|
p != pdl->end();
|
|
++p)
|
|
{
|
|
bool is_bss = (*p)->is_section_type(elfcpp::SHT_NOBITS);
|
|
bool is_tls = (*p)->is_section_flag_set(elfcpp::SHF_TLS);
|
|
|
|
if (reset)
|
|
(*p)->reset_address_and_file_offset();
|
|
|
|
// When doing an incremental update or when using a linker script,
|
|
// the section will most likely already have an address.
|
|
if (!(*p)->is_address_valid())
|
|
{
|
|
uint64_t align = (*p)->addralign();
|
|
|
|
if (is_tls)
|
|
{
|
|
// Give the first TLS section the alignment of the
|
|
// entire TLS segment. Otherwise the TLS segment as a
|
|
// whole may be misaligned.
|
|
if (!*in_tls)
|
|
{
|
|
Output_segment* tls_segment = layout->tls_segment();
|
|
gold_assert(tls_segment != NULL);
|
|
uint64_t segment_align = tls_segment->maximum_alignment();
|
|
gold_assert(segment_align >= align);
|
|
align = segment_align;
|
|
|
|
*in_tls = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If this is the first section after the TLS segment,
|
|
// align it to at least the alignment of the TLS
|
|
// segment, so that the size of the overall TLS segment
|
|
// is aligned.
|
|
if (*in_tls)
|
|
{
|
|
uint64_t segment_align =
|
|
layout->tls_segment()->maximum_alignment();
|
|
if (segment_align > align)
|
|
align = segment_align;
|
|
|
|
*in_tls = false;
|
|
}
|
|
}
|
|
|
|
if (!parameters->incremental_update())
|
|
{
|
|
gold_assert(off == foff || is_bss);
|
|
off = align_address(off, align);
|
|
if (is_tls || !is_bss)
|
|
foff = off;
|
|
(*p)->set_address_and_file_offset(addr + (off - startoff), foff);
|
|
}
|
|
else
|
|
{
|
|
// Incremental update: allocate file space from free list.
|
|
(*p)->pre_finalize_data_size();
|
|
off_t current_size = (*p)->current_data_size();
|
|
off = layout->allocate(current_size, align, startoff);
|
|
foff = off;
|
|
if (off == -1)
|
|
{
|
|
gold_assert((*p)->output_section() != NULL);
|
|
gold_fallback(_("out of patch space for section %s; "
|
|
"relink with --incremental-full"),
|
|
(*p)->output_section()->name());
|
|
}
|
|
(*p)->set_address_and_file_offset(addr + (off - startoff), foff);
|
|
if ((*p)->data_size() > current_size)
|
|
{
|
|
gold_assert((*p)->output_section() != NULL);
|
|
gold_fallback(_("%s: section changed size; "
|
|
"relink with --incremental-full"),
|
|
(*p)->output_section()->name());
|
|
}
|
|
}
|
|
}
|
|
else if (parameters->incremental_update())
|
|
{
|
|
// For incremental updates, use the fixed offset for the
|
|
// high-water mark computation.
|
|
off = (*p)->offset();
|
|
foff = off;
|
|
}
|
|
else
|
|
{
|
|
// The script may have inserted a skip forward, but it
|
|
// better not have moved backward.
|
|
if ((*p)->address() >= addr + (off - startoff))
|
|
{
|
|
if (!is_bss && off > foff)
|
|
gold_warning(_("script places BSS section in the middle "
|
|
"of a LOAD segment; space will be allocated "
|
|
"in the file"));
|
|
off += (*p)->address() - (addr + (off - startoff));
|
|
if (is_tls || !is_bss)
|
|
foff = off;
|
|
}
|
|
else
|
|
{
|
|
if (!layout->script_options()->saw_sections_clause())
|
|
gold_unreachable();
|
|
else
|
|
{
|
|
Output_section* os = (*p)->output_section();
|
|
|
|
// Cast to unsigned long long to avoid format warnings.
|
|
unsigned long long previous_dot =
|
|
static_cast<unsigned long long>(addr + (off - startoff));
|
|
unsigned long long dot =
|
|
static_cast<unsigned long long>((*p)->address());
|
|
|
|
if (os == NULL)
|
|
gold_error(_("dot moves backward in linker script "
|
|
"from 0x%llx to 0x%llx"), previous_dot, dot);
|
|
else
|
|
gold_error(_("address of section '%s' moves backward "
|
|
"from 0x%llx to 0x%llx"),
|
|
os->name(), previous_dot, dot);
|
|
}
|
|
}
|
|
(*p)->set_file_offset(foff);
|
|
(*p)->finalize_data_size();
|
|
}
|
|
|
|
if (parameters->incremental_update())
|
|
gold_debug(DEBUG_INCREMENTAL,
|
|
"set_section_list_addresses: %08lx %08lx %s",
|
|
static_cast<long>(off),
|
|
static_cast<long>((*p)->data_size()),
|
|
((*p)->output_section() != NULL
|
|
? (*p)->output_section()->name() : "(special)"));
|
|
|
|
// We want to ignore the size of a SHF_TLS SHT_NOBITS
|
|
// section. Such a section does not affect the size of a
|
|
// PT_LOAD segment.
|
|
if (!is_tls || !is_bss)
|
|
off += (*p)->data_size();
|
|
|
|
// We don't allocate space in the file for SHT_NOBITS sections,
|
|
// unless a script has force-placed one in the middle of a segment.
|
|
if (!is_bss)
|
|
foff = off;
|
|
|
|
if (off > maxoff)
|
|
maxoff = off;
|
|
|
|
if ((*p)->is_section())
|
|
{
|
|
(*p)->set_out_shndx(*pshndx);
|
|
++*pshndx;
|
|
}
|
|
}
|
|
|
|
*poff = maxoff;
|
|
*pfoff = foff;
|
|
return addr + (maxoff - startoff);
|
|
}
|
|
|
|
// For a non-PT_LOAD segment, set the offset from the sections, if
|
|
// any. Add INCREASE to the file size and the memory size.
|
|
|
|
void
|
|
Output_segment::set_offset(unsigned int increase)
|
|
{
|
|
gold_assert(this->type_ != elfcpp::PT_LOAD);
|
|
|
|
gold_assert(!this->are_addresses_set_);
|
|
|
|
// A non-load section only uses output_lists_[0].
|
|
|
|
Output_data_list* pdl = &this->output_lists_[0];
|
|
|
|
if (pdl->empty())
|
|
{
|
|
gold_assert(increase == 0);
|
|
this->vaddr_ = 0;
|
|
this->paddr_ = 0;
|
|
this->are_addresses_set_ = true;
|
|
this->memsz_ = 0;
|
|
this->min_p_align_ = 0;
|
|
this->offset_ = 0;
|
|
this->filesz_ = 0;
|
|
return;
|
|
}
|
|
|
|
// Find the first and last section by address.
|
|
const Output_data* first = NULL;
|
|
const Output_data* last_data = NULL;
|
|
const Output_data* last_bss = NULL;
|
|
for (Output_data_list::const_iterator p = pdl->begin();
|
|
p != pdl->end();
|
|
++p)
|
|
{
|
|
if (first == NULL
|
|
|| (*p)->address() < first->address()
|
|
|| ((*p)->address() == first->address()
|
|
&& (*p)->data_size() < first->data_size()))
|
|
first = *p;
|
|
const Output_data** plast;
|
|
if ((*p)->is_section()
|
|
&& (*p)->output_section()->type() == elfcpp::SHT_NOBITS)
|
|
plast = &last_bss;
|
|
else
|
|
plast = &last_data;
|
|
if (*plast == NULL
|
|
|| (*p)->address() > (*plast)->address()
|
|
|| ((*p)->address() == (*plast)->address()
|
|
&& (*p)->data_size() > (*plast)->data_size()))
|
|
*plast = *p;
|
|
}
|
|
|
|
this->vaddr_ = first->address();
|
|
this->paddr_ = (first->has_load_address()
|
|
? first->load_address()
|
|
: this->vaddr_);
|
|
this->are_addresses_set_ = true;
|
|
this->offset_ = first->offset();
|
|
|
|
if (last_data == NULL)
|
|
this->filesz_ = 0;
|
|
else
|
|
this->filesz_ = (last_data->address()
|
|
+ last_data->data_size()
|
|
- this->vaddr_);
|
|
|
|
const Output_data* last = last_bss != NULL ? last_bss : last_data;
|
|
this->memsz_ = (last->address()
|
|
+ last->data_size()
|
|
- this->vaddr_);
|
|
|
|
this->filesz_ += increase;
|
|
this->memsz_ += increase;
|
|
|
|
// If this is a RELRO segment, verify that the segment ends at a
|
|
// page boundary.
|
|
if (this->type_ == elfcpp::PT_GNU_RELRO)
|
|
{
|
|
uint64_t page_align = parameters->target().abi_pagesize();
|
|
uint64_t segment_end = this->vaddr_ + this->memsz_;
|
|
if (parameters->incremental_update())
|
|
{
|
|
// The INCREASE_RELRO calculation is bypassed for an incremental
|
|
// update, so we need to adjust the segment size manually here.
|
|
segment_end = align_address(segment_end, page_align);
|
|
this->memsz_ = segment_end - this->vaddr_;
|
|
}
|
|
else
|
|
gold_assert(segment_end == align_address(segment_end, page_align));
|
|
}
|
|
|
|
// If this is a TLS segment, align the memory size. The code in
|
|
// set_section_list ensures that the section after the TLS segment
|
|
// is aligned to give us room.
|
|
if (this->type_ == elfcpp::PT_TLS)
|
|
{
|
|
uint64_t segment_align = this->maximum_alignment();
|
|
gold_assert(this->vaddr_ == align_address(this->vaddr_, segment_align));
|
|
this->memsz_ = align_address(this->memsz_, segment_align);
|
|
}
|
|
}
|
|
|
|
// Set the TLS offsets of the sections in the PT_TLS segment.
|
|
|
|
void
|
|
Output_segment::set_tls_offsets()
|
|
{
|
|
gold_assert(this->type_ == elfcpp::PT_TLS);
|
|
|
|
for (Output_data_list::iterator p = this->output_lists_[0].begin();
|
|
p != this->output_lists_[0].end();
|
|
++p)
|
|
(*p)->set_tls_offset(this->vaddr_);
|
|
}
|
|
|
|
// Return the first section.
|
|
|
|
Output_section*
|
|
Output_segment::first_section() const
|
|
{
|
|
for (int i = 0; i < static_cast<int>(ORDER_MAX); ++i)
|
|
{
|
|
const Output_data_list* pdl = &this->output_lists_[i];
|
|
for (Output_data_list::const_iterator p = pdl->begin();
|
|
p != pdl->end();
|
|
++p)
|
|
{
|
|
if ((*p)->is_section())
|
|
return (*p)->output_section();
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Return the number of Output_sections in an Output_segment.
|
|
|
|
unsigned int
|
|
Output_segment::output_section_count() const
|
|
{
|
|
unsigned int ret = 0;
|
|
for (int i = 0; i < static_cast<int>(ORDER_MAX); ++i)
|
|
ret += this->output_section_count_list(&this->output_lists_[i]);
|
|
return ret;
|
|
}
|
|
|
|
// Return the number of Output_sections in an Output_data_list.
|
|
|
|
unsigned int
|
|
Output_segment::output_section_count_list(const Output_data_list* pdl) const
|
|
{
|
|
unsigned int count = 0;
|
|
for (Output_data_list::const_iterator p = pdl->begin();
|
|
p != pdl->end();
|
|
++p)
|
|
{
|
|
if ((*p)->is_section())
|
|
++count;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
// Return the section attached to the list segment with the lowest
|
|
// load address. This is used when handling a PHDRS clause in a
|
|
// linker script.
|
|
|
|
Output_section*
|
|
Output_segment::section_with_lowest_load_address() const
|
|
{
|
|
Output_section* found = NULL;
|
|
uint64_t found_lma = 0;
|
|
for (int i = 0; i < static_cast<int>(ORDER_MAX); ++i)
|
|
this->lowest_load_address_in_list(&this->output_lists_[i], &found,
|
|
&found_lma);
|
|
return found;
|
|
}
|
|
|
|
// Look through a list for a section with a lower load address.
|
|
|
|
void
|
|
Output_segment::lowest_load_address_in_list(const Output_data_list* pdl,
|
|
Output_section** found,
|
|
uint64_t* found_lma) const
|
|
{
|
|
for (Output_data_list::const_iterator p = pdl->begin();
|
|
p != pdl->end();
|
|
++p)
|
|
{
|
|
if (!(*p)->is_section())
|
|
continue;
|
|
Output_section* os = static_cast<Output_section*>(*p);
|
|
uint64_t lma = (os->has_load_address()
|
|
? os->load_address()
|
|
: os->address());
|
|
if (*found == NULL || lma < *found_lma)
|
|
{
|
|
*found = os;
|
|
*found_lma = lma;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Write the segment data into *OPHDR.
|
|
|
|
template<int size, bool big_endian>
|
|
void
|
|
Output_segment::write_header(elfcpp::Phdr_write<size, big_endian>* ophdr)
|
|
{
|
|
ophdr->put_p_type(this->type_);
|
|
ophdr->put_p_offset(this->offset_);
|
|
ophdr->put_p_vaddr(this->vaddr_);
|
|
ophdr->put_p_paddr(this->paddr_);
|
|
ophdr->put_p_filesz(this->filesz_);
|
|
ophdr->put_p_memsz(this->memsz_);
|
|
ophdr->put_p_flags(this->flags_);
|
|
ophdr->put_p_align(std::max(this->min_p_align_, this->maximum_alignment()));
|
|
}
|
|
|
|
// Write the section headers into V.
|
|
|
|
template<int size, bool big_endian>
|
|
unsigned char*
|
|
Output_segment::write_section_headers(const Layout* layout,
|
|
const Stringpool* secnamepool,
|
|
unsigned char* v,
|
|
unsigned int* pshndx) const
|
|
{
|
|
// Every section that is attached to a segment must be attached to a
|
|
// PT_LOAD segment, so we only write out section headers for PT_LOAD
|
|
// segments.
|
|
if (this->type_ != elfcpp::PT_LOAD)
|
|
return v;
|
|
|
|
for (int i = 0; i < static_cast<int>(ORDER_MAX); ++i)
|
|
{
|
|
const Output_data_list* pdl = &this->output_lists_[i];
|
|
v = this->write_section_headers_list<size, big_endian>(layout,
|
|
secnamepool,
|
|
pdl,
|
|
v, pshndx);
|
|
}
|
|
|
|
return v;
|
|
}
|
|
|
|
template<int size, bool big_endian>
|
|
unsigned char*
|
|
Output_segment::write_section_headers_list(const Layout* layout,
|
|
const Stringpool* secnamepool,
|
|
const Output_data_list* pdl,
|
|
unsigned char* v,
|
|
unsigned int* pshndx) const
|
|
{
|
|
const int shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
|
|
for (Output_data_list::const_iterator p = pdl->begin();
|
|
p != pdl->end();
|
|
++p)
|
|
{
|
|
if ((*p)->is_section())
|
|
{
|
|
const Output_section* ps = static_cast<const Output_section*>(*p);
|
|
gold_assert(*pshndx == ps->out_shndx());
|
|
elfcpp::Shdr_write<size, big_endian> oshdr(v);
|
|
ps->write_header(layout, secnamepool, &oshdr);
|
|
v += shdr_size;
|
|
++*pshndx;
|
|
}
|
|
}
|
|
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;
|
|
for (int i = 0; i < static_cast<int>(ORDER_MAX); ++i)
|
|
this->print_section_list_to_mapfile(mapfile, &this->output_lists_[i]);
|
|
}
|
|
|
|
// 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)
|
|
: name_(name),
|
|
o_(-1),
|
|
file_size_(0),
|
|
base_(NULL),
|
|
map_is_anonymous_(false),
|
|
map_is_allocated_(false),
|
|
is_temporary_(false)
|
|
{
|
|
}
|
|
|
|
// Try to open an existing file. Returns false if the file doesn't
|
|
// exist, has a size of 0 or can't be mmapped. If BASE_NAME is not
|
|
// NULL, open that file as the base for incremental linking, and
|
|
// copy its contents to the new output file. This routine can
|
|
// be called for incremental updates, in which case WRITABLE should
|
|
// be true, or by the incremental-dump utility, in which case
|
|
// WRITABLE should be false.
|
|
|
|
bool
|
|
Output_file::open_base_file(const char* base_name, bool writable)
|
|
{
|
|
// The name "-" means "stdout".
|
|
if (strcmp(this->name_, "-") == 0)
|
|
return false;
|
|
|
|
bool use_base_file = base_name != NULL;
|
|
if (!use_base_file)
|
|
base_name = this->name_;
|
|
else if (strcmp(base_name, this->name_) == 0)
|
|
gold_fatal(_("%s: incremental base and output file name are the same"),
|
|
base_name);
|
|
|
|
// Don't bother opening files with a size of zero.
|
|
struct stat s;
|
|
if (::stat(base_name, &s) != 0)
|
|
{
|
|
gold_info(_("%s: stat: %s"), base_name, strerror(errno));
|
|
return false;
|
|
}
|
|
if (s.st_size == 0)
|
|
{
|
|
gold_info(_("%s: incremental base file is empty"), base_name);
|
|
return false;
|
|
}
|
|
|
|
// If we're using a base file, we want to open it read-only.
|
|
if (use_base_file)
|
|
writable = false;
|
|
|
|
int oflags = writable ? O_RDWR : O_RDONLY;
|
|
int o = open_descriptor(-1, base_name, oflags, 0);
|
|
if (o < 0)
|
|
{
|
|
gold_info(_("%s: open: %s"), base_name, strerror(errno));
|
|
return false;
|
|
}
|
|
|
|
// If the base file and the output file are different, open a
|
|
// new output file and read the contents from the base file into
|
|
// the newly-mapped region.
|
|
if (use_base_file)
|
|
{
|
|
this->open(s.st_size);
|
|
ssize_t bytes_to_read = s.st_size;
|
|
unsigned char* p = this->base_;
|
|
while (bytes_to_read > 0)
|
|
{
|
|
ssize_t len = ::read(o, p, bytes_to_read);
|
|
if (len < 0)
|
|
{
|
|
gold_info(_("%s: read failed: %s"), base_name, strerror(errno));
|
|
return false;
|
|
}
|
|
if (len == 0)
|
|
{
|
|
gold_info(_("%s: file too short: read only %lld of %lld bytes"),
|
|
base_name,
|
|
static_cast<long long>(s.st_size - bytes_to_read),
|
|
static_cast<long long>(s.st_size));
|
|
return false;
|
|
}
|
|
p += len;
|
|
bytes_to_read -= len;
|
|
}
|
|
::close(o);
|
|
return true;
|
|
}
|
|
|
|
this->o_ = o;
|
|
this->file_size_ = s.st_size;
|
|
|
|
if (!this->map_no_anonymous(writable))
|
|
{
|
|
release_descriptor(o, true);
|
|
this->o_ = -1;
|
|
this->file_size_ = 0;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// Open the output file.
|
|
|
|
void
|
|
Output_file::open(off_t file_size)
|
|
{
|
|
this->file_size_ = file_size;
|
|
|
|
// Unlink the file first; otherwise the open() may fail if the file
|
|
// is busy (e.g. it's an executable that's currently being executed).
|
|
//
|
|
// However, the linker may be part of a system where a zero-length
|
|
// file is created for it to write to, with tight permissions (gcc
|
|
// 2.95 did something like this). Unlinking the file would work
|
|
// around those permission controls, so we only unlink if the file
|
|
// has a non-zero size. We also unlink only regular files to avoid
|
|
// trouble with directories/etc.
|
|
//
|
|
// If we fail, continue; this command is merely a best-effort attempt
|
|
// to improve the odds for open().
|
|
|
|
// We let the name "-" mean "stdout"
|
|
if (!this->is_temporary_)
|
|
{
|
|
if (strcmp(this->name_, "-") == 0)
|
|
this->o_ = STDOUT_FILENO;
|
|
else
|
|
{
|
|
struct stat s;
|
|
if (::stat(this->name_, &s) == 0
|
|
&& (S_ISREG (s.st_mode) || S_ISLNK (s.st_mode)))
|
|
{
|
|
if (s.st_size != 0)
|
|
::unlink(this->name_);
|
|
else if (!parameters->options().relocatable())
|
|
{
|
|
// If we don't unlink the existing file, add execute
|
|
// permission where read permissions already exist
|
|
// and where the umask permits.
|
|
int mask = ::umask(0);
|
|
::umask(mask);
|
|
s.st_mode |= (s.st_mode & 0444) >> 2;
|
|
::chmod(this->name_, s.st_mode & ~mask);
|
|
}
|
|
}
|
|
|
|
int mode = parameters->options().relocatable() ? 0666 : 0777;
|
|
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;
|
|
}
|
|
}
|
|
|
|
this->map();
|
|
}
|
|
|
|
// Resize the output file.
|
|
|
|
void
|
|
Output_file::resize(off_t file_size)
|
|
{
|
|
// If the mmap is mapping an anonymous memory buffer, this is easy:
|
|
// just mremap to the new size. If it's mapping to a file, we want
|
|
// to unmap to flush to the file, then remap after growing the file.
|
|
if (this->map_is_anonymous_)
|
|
{
|
|
void* base;
|
|
if (!this->map_is_allocated_)
|
|
{
|
|
base = ::mremap(this->base_, this->file_size_, file_size,
|
|
MREMAP_MAYMOVE);
|
|
if (base == MAP_FAILED)
|
|
gold_fatal(_("%s: mremap: %s"), this->name_, strerror(errno));
|
|
}
|
|
else
|
|
{
|
|
base = realloc(this->base_, file_size);
|
|
if (base == NULL)
|
|
gold_nomem();
|
|
if (file_size > this->file_size_)
|
|
memset(static_cast<char*>(base) + this->file_size_, 0,
|
|
file_size - this->file_size_);
|
|
}
|
|
this->base_ = static_cast<unsigned char*>(base);
|
|
this->file_size_ = file_size;
|
|
}
|
|
else
|
|
{
|
|
this->unmap();
|
|
this->file_size_ = file_size;
|
|
if (!this->map_no_anonymous(true))
|
|
gold_fatal(_("%s: mmap: %s"), this->name_, strerror(errno));
|
|
}
|
|
}
|
|
|
|
// Map an anonymous block of memory which will later be written to the
|
|
// file. Return whether the map succeeded.
|
|
|
|
bool
|
|
Output_file::map_anonymous()
|
|
{
|
|
void* base = ::mmap(NULL, this->file_size_, PROT_READ | PROT_WRITE,
|
|
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
|
if (base == MAP_FAILED)
|
|
{
|
|
base = malloc(this->file_size_);
|
|
if (base == NULL)
|
|
return false;
|
|
memset(base, 0, this->file_size_);
|
|
this->map_is_allocated_ = true;
|
|
}
|
|
this->base_ = static_cast<unsigned char*>(base);
|
|
this->map_is_anonymous_ = true;
|
|
return true;
|
|
}
|
|
|
|
// Map the file into memory. Return whether the mapping succeeded.
|
|
// If WRITABLE is true, map with write access.
|
|
|
|
bool
|
|
Output_file::map_no_anonymous(bool writable)
|
|
{
|
|
const int o = this->o_;
|
|
|
|
// If the output file is not a regular file, don't try to mmap it;
|
|
// instead, we'll mmap a block of memory (an anonymous buffer), and
|
|
// then later write the buffer to the file.
|
|
void* base;
|
|
struct stat statbuf;
|
|
if (o == STDOUT_FILENO || o == STDERR_FILENO
|
|
|| ::fstat(o, &statbuf) != 0
|
|
|| !S_ISREG(statbuf.st_mode)
|
|
|| this->is_temporary_)
|
|
return false;
|
|
|
|
// 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 (writable)
|
|
{
|
|
int err = gold_fallocate(o, 0, this->file_size_);
|
|
if (err != 0)
|
|
gold_fatal(_("%s: %s"), this->name_, strerror(err));
|
|
}
|
|
|
|
// Map the file into memory.
|
|
int prot = PROT_READ;
|
|
if (writable)
|
|
prot |= PROT_WRITE;
|
|
base = ::mmap(NULL, this->file_size_, prot, 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.
|
|
if (base == MAP_FAILED)
|
|
return false;
|
|
|
|
this->map_is_anonymous_ = false;
|
|
this->base_ = static_cast<unsigned char*>(base);
|
|
return true;
|
|
}
|
|
|
|
// Map the file into memory.
|
|
|
|
void
|
|
Output_file::map()
|
|
{
|
|
if (parameters->options().mmap_output_file()
|
|
&& this->map_no_anonymous(true))
|
|
return;
|
|
|
|
// 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 and we
|
|
// don't care about file contents, try for an anonymous map.
|
|
if (this->map_anonymous())
|
|
return;
|
|
|
|
gold_fatal(_("%s: mmap: failed to allocate %lu bytes for output file: %s"),
|
|
this->name_, static_cast<unsigned long>(this->file_size_),
|
|
strerror(errno));
|
|
}
|
|
|
|
// Unmap the file from memory.
|
|
|
|
void
|
|
Output_file::unmap()
|
|
{
|
|
if (this->map_is_anonymous_)
|
|
{
|
|
// We've already written out the data, so there is no reason to
|
|
// waste time unmapping or freeing the memory.
|
|
}
|
|
else
|
|
{
|
|
if (::munmap(this->base_, this->file_size_) < 0)
|
|
gold_error(_("%s: munmap: %s"), this->name_, strerror(errno));
|
|
}
|
|
this->base_ = NULL;
|
|
}
|
|
|
|
// Close the output file.
|
|
|
|
void
|
|
Output_file::close()
|
|
{
|
|
// If the map isn't file-backed, we need to write it now.
|
|
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_ + 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;
|
|
offset += bytes_written;
|
|
}
|
|
}
|
|
}
|
|
this->unmap();
|
|
|
|
// We don't close stdout or stderr
|
|
if (this->o_ != STDOUT_FILENO
|
|
&& this->o_ != STDERR_FILENO
|
|
&& !this->is_temporary_)
|
|
if (::close(this->o_) < 0)
|
|
gold_error(_("%s: close: %s"), this->name_, strerror(errno));
|
|
this->o_ = -1;
|
|
}
|
|
|
|
// Instantiate the templates we need. We could use the configure
|
|
// script to restrict this to only the ones for implemented targets.
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
off_t
|
|
Output_section::add_input_section<32, false>(
|
|
Layout* layout,
|
|
Sized_relobj_file<32, false>* object,
|
|
unsigned int shndx,
|
|
const char* secname,
|
|
const elfcpp::Shdr<32, false>& shdr,
|
|
unsigned int reloc_shndx,
|
|
bool have_sections_script);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
off_t
|
|
Output_section::add_input_section<32, true>(
|
|
Layout* layout,
|
|
Sized_relobj_file<32, true>* object,
|
|
unsigned int shndx,
|
|
const char* secname,
|
|
const elfcpp::Shdr<32, true>& shdr,
|
|
unsigned int reloc_shndx,
|
|
bool have_sections_script);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
off_t
|
|
Output_section::add_input_section<64, false>(
|
|
Layout* layout,
|
|
Sized_relobj_file<64, false>* object,
|
|
unsigned int shndx,
|
|
const char* secname,
|
|
const elfcpp::Shdr<64, false>& shdr,
|
|
unsigned int reloc_shndx,
|
|
bool have_sections_script);
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
off_t
|
|
Output_section::add_input_section<64, true>(
|
|
Layout* layout,
|
|
Sized_relobj_file<64, true>* object,
|
|
unsigned int shndx,
|
|
const char* secname,
|
|
const elfcpp::Shdr<64, true>& shdr,
|
|
unsigned int reloc_shndx,
|
|
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>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
class Output_data_reloc<elfcpp::SHT_REL, false, 32, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
class Output_data_reloc<elfcpp::SHT_REL, false, 64, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
class Output_data_reloc<elfcpp::SHT_REL, false, 64, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
class Output_data_reloc<elfcpp::SHT_REL, true, 32, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
class Output_data_reloc<elfcpp::SHT_REL, true, 32, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
class Output_data_reloc<elfcpp::SHT_REL, true, 64, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
class Output_data_reloc<elfcpp::SHT_REL, true, 64, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
class Output_data_reloc<elfcpp::SHT_RELA, false, 32, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
class Output_data_reloc<elfcpp::SHT_RELA, false, 32, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
class Output_data_reloc<elfcpp::SHT_RELA, false, 64, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
class Output_data_reloc<elfcpp::SHT_RELA, false, 64, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
class Output_data_reloc<elfcpp::SHT_RELA, true, 32, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
class Output_data_reloc<elfcpp::SHT_RELA, true, 32, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
class Output_data_reloc<elfcpp::SHT_RELA, true, 64, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
class Output_data_reloc<elfcpp::SHT_RELA, true, 64, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
class Output_relocatable_relocs<elfcpp::SHT_REL, 32, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
class Output_relocatable_relocs<elfcpp::SHT_REL, 32, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
class Output_relocatable_relocs<elfcpp::SHT_REL, 64, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
class Output_relocatable_relocs<elfcpp::SHT_REL, 64, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
class Output_relocatable_relocs<elfcpp::SHT_RELA, 32, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
class Output_relocatable_relocs<elfcpp::SHT_RELA, 32, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
class Output_relocatable_relocs<elfcpp::SHT_RELA, 64, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
class Output_relocatable_relocs<elfcpp::SHT_RELA, 64, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_LITTLE
|
|
template
|
|
class Output_data_group<32, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_32_BIG
|
|
template
|
|
class Output_data_group<32, true>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_LITTLE
|
|
template
|
|
class Output_data_group<64, false>;
|
|
#endif
|
|
|
|
#ifdef HAVE_TARGET_64_BIG
|
|
template
|
|
class Output_data_group<64, true>;
|
|
#endif
|
|
|
|
template
|
|
class Output_data_got<32, false>;
|
|
|
|
template
|
|
class Output_data_got<32, true>;
|
|
|
|
template
|
|
class Output_data_got<64, false>;
|
|
|
|
template
|
|
class Output_data_got<64, true>;
|
|
|
|
} // End namespace gold.
|