// mips.cc -- mips target support for gold.
-// Copyright (C) 2011-2017 Free Software Foundation, Inc.
+// Copyright (C) 2011-2019 Free Software Foundation, Inc.
// Written by Sasa Stankovic <sasa.stankovic@imgtec.com>
// and Aleksandar Simeonov <aleksandar.simeonov@rt-rk.com>.
// This file contains borrowed and adapted code from bfd/elfxx-mips.c.
template<int size, bool big_endian>
class Mips_output_section_reginfo;
+template<int size, bool big_endian>
+class Mips_output_section_options;
+
template<int size, bool big_endian>
class Mips_output_data_la25_stub;
? this->d.object->name().c_str()
: this->d.sym->name());
size_t addend = this->addend_;
- return name_hash_value ^ this->symndx_ ^ addend;
+ return name_hash_value ^ this->symndx_ ^ (addend << 16);
}
// Return whether this entry is equal to OTHER.
bool
equals(Mips_got_entry<size, big_endian>* other) const
{
+ if (this->symndx_ != other->symndx_
+ || this->tls_type_ != other->tls_type_)
+ return false;
+
if (this->tls_type_ == GOT_TLS_LDM)
return true;
- return ((this->tls_type_ == other->tls_type_)
- && (this->symndx_ == other->symndx_)
- && ((this->symndx_ != -1U)
- ? (this->d.object == other->d.object)
- : (this->d.sym == other->d.sym))
- && (this->addend_ == other->addend_));
+ return (((this->symndx_ != -1U)
+ ? (this->d.object == other->d.object)
+ : (this->d.sym == other->d.sym))
+ && (this->addend_ == other->addend_));
}
// Return input object that needs this GOT entry.
struct Got_page_entry
{
Got_page_entry()
- : object(NULL), symndx(-1U), ranges(NULL), num_pages(0)
+ : object(NULL), symndx(-1U), ranges(NULL)
{ }
Got_page_entry(Object* object_, unsigned int symndx_)
- : object(object_), symndx(symndx_), ranges(NULL), num_pages(0)
+ : object(object_), symndx(symndx_), ranges(NULL)
{ }
// The input object that needs the GOT page entry.
unsigned int symndx;
// The ranges for this page entry.
Got_page_range* ranges;
- // The maximum number of page entries needed for RANGES.
- unsigned int num_pages;
};
// Hash for Got_page_entry.
// Add FROM's GOT page entries.
void
- add_got_page_entries(Mips_got_info<size, big_endian>* from);
+ add_got_page_count(Mips_got_info<size, big_endian>* from);
// Return GOT size.
unsigned int
Global_got_entry_set global_got_symbols_;
// A hash table holding GOT entries.
Got_entry_set got_entries_;
- // A hash table of GOT page entries.
+ // A hash table of GOT page entries (only used in master GOT).
Got_page_entry_set got_page_entries_;
// The offset of first GOT page entry for this GOT.
unsigned int got_page_offset_start_;
local_symbol_is_micromips_(), mips16_stub_sections_(),
local_non_16bit_calls_(), local_16bit_calls_(), local_mips16_fn_stubs_(),
local_mips16_call_stubs_(), gp_(0), has_reginfo_section_(false),
- got_info_(NULL), section_is_mips16_fn_stub_(),
- section_is_mips16_call_stub_(), section_is_mips16_call_fp_stub_(),
- pdr_shndx_(-1U), attributes_section_data_(NULL), abiflags_(NULL),
- gprmask_(0), cprmask1_(0), cprmask2_(0), cprmask3_(0), cprmask4_(0)
+ merge_processor_specific_data_(true), got_info_(NULL),
+ section_is_mips16_fn_stub_(), section_is_mips16_call_stub_(),
+ section_is_mips16_call_fp_stub_(), pdr_shndx_(-1U),
+ attributes_section_data_(NULL), abiflags_(NULL), gprmask_(0),
+ cprmask1_(0), cprmask2_(0), cprmask3_(0), cprmask4_(0)
{
this->is_pic_ = (ehdr.get_e_flags() & elfcpp::EF_MIPS_PIC) != 0;
this->is_n32_ = elfcpp::abi_n32(ehdr.get_e_flags());
has_reginfo_section() const
{ return this->has_reginfo_section_; }
+ // Return whether we want to merge processor-specific data.
+ bool
+ merge_processor_specific_data() const
+ { return this->merge_processor_specific_data_; }
+
// Return gprmask from the .reginfo section of this object.
Valtype
gprmask() const
bool is_n32_ : 1;
// Whether the object contains a .reginfo section.
bool has_reginfo_section_ : 1;
+ // Whether we merge processor-specific data of this object to output.
+ bool merge_processor_specific_data_ : 1;
// The Mips_got_info for this object.
Mips_got_info<size, big_endian>* got_info_;
Valtype cprmask4_;
};
+// This class handles .MIPS.options output section.
+
+template<int size, bool big_endian>
+class Mips_output_section_options : public Output_section
+{
+ public:
+ Mips_output_section_options(const char* name, elfcpp::Elf_Word type,
+ elfcpp::Elf_Xword flags,
+ Target_mips<size, big_endian>* target)
+ : Output_section(name, type, flags), target_(target)
+ {
+ // After the input sections are written, we only need to update
+ // ri_gp_value field of ODK_REGINFO entries.
+ this->set_after_input_sections();
+ }
+
+ protected:
+ // Write out option section.
+ void
+ do_write(Output_file* of);
+
+ private:
+ Target_mips<size, big_endian>* target_;
+};
+
// This class handles .MIPS.abiflags output section.
template<int size, bool big_endian>
// If we don't have a definition in a regular file, then we can't
// resolve locally. The sym is either undefined or dynamic.
- if (sym->source() != Symbol::FROM_OBJECT || sym->object()->is_dynamic()
- || sym->is_undefined())
+ if (sym->is_from_dynobj() || sym->is_undefined())
return false;
// Forced local symbols resolve locally.
const elfcpp::Ehdr<size, !big_endian>&)
{ gold_unreachable(); }
+ // Make an output section.
+ Output_section*
+ do_make_output_section(const char* name, elfcpp::Elf_Word type,
+ elfcpp::Elf_Xword flags)
+ {
+ if (type == elfcpp::SHT_MIPS_OPTIONS)
+ return new Mips_output_section_options<size, big_endian>(name, type,
+ flags, this);
+ else
+ return new Output_section(name, type, flags);
+ }
+
// Adjust ELF file header.
void
do_adjust_elf_header(unsigned char* view, int len);
{
public:
Relocate()
+ : calculated_value_(0), calculate_only_(false)
{ }
~Relocate()
Target_mips*, Output_section*, size_t, const unsigned char*,
const Sized_symbol<size>*, const Symbol_value<size>*,
unsigned char*, Mips_address, section_size_type);
+
+ private:
+ // Result of the relocation.
+ Valtype calculated_value_;
+ // Whether we have to calculate relocation instead of applying it.
+ bool calculate_only_;
};
// This POD class holds the dynamic relocations that should be emitted instead
mach_mips5 = 5,
mach_mips_loongson_2e = 3001,
mach_mips_loongson_2f = 3002,
- mach_mips_loongson_3a = 3003,
+ mach_mips_gs464 = 3003,
+ mach_mips_gs464e = 3004,
+ mach_mips_gs264e = 3005,
mach_mips_sb1 = 12310201, // octal 'SB', 01
mach_mips_octeon = 6501,
mach_mips_octeonp = 6601,
this->add_extension(mach_mips_octeon2, mach_mips_octeonp);
this->add_extension(mach_mips_octeonp, mach_mips_octeon);
this->add_extension(mach_mips_octeon, mach_mipsisa64r2);
- this->add_extension(mach_mips_loongson_3a, mach_mipsisa64r2);
+ this->add_extension(mach_mips_gs264e, mach_mips_gs464e);
+ this->add_extension(mach_mips_gs464e, mach_mips_gs464);
+ this->add_extension(mach_mips_gs464, mach_mipsisa64r2);
// MIPS64 extensions.
this->add_extension(mach_mipsisa64r2, mach_mipsisa64);
Valtype32 val = elfcpp::Swap<32, big_endian>::readval(wv);
// Try converting J(AL)R to B(AL), if the target is in range.
- if (!parameters->options().relocatable()
- && r_type == elfcpp::R_MIPS_JALR
+ if (r_type == elfcpp::R_MIPS_JALR
&& !cross_mode_jump
&& ((jalr_to_bal && val == 0x0320f809) // jalr t9
|| (jr_to_b && val == 0x03200008))) // jr t9
}
x = psymval->value(object, x) >> shift;
- if (!calculate_only && !local && !gsym->is_weak_undefined())
- {
- if ((x >> 26) != ((address + 4) >> (26 + shift)))
- {
- gold_error(_("relocation truncated to fit: %u against '%s'"),
- r_type, gsym->name());
- return This::STATUS_OVERFLOW;
- }
- }
+ if (!calculate_only && !local && !gsym->is_weak_undefined()
+ && ((x >> 26) != ((address + 4) >> (26 + shift))))
+ return This::STATUS_OVERFLOW;
val = Bits<32>::bit_select32(val, x, 0x03ffffff);
: addend_a);
Valtype x = psymval->value(object, addend);
- x = ((x + (uint64_t) 0x800080008000) >> 48) & 0xffff;
+ x = ((x + (uint64_t) 0x800080008000llu) >> 48) & 0xffff;
val = Bits<32>::bit_select32(val, x, 0xffff);
if (calculate_only)
else
this->got_page_entries_.insert(entry);
- // Add the same entry to the OBJECT's GOT.
- Got_page_entry* entry2 = NULL;
+ // Get the object's GOT, but we don't need to insert an entry here.
Mips_got_info<size, big_endian>* g2 = object->get_or_create_got_info();
- if (g2->got_page_entries_.find(entry) == g2->got_page_entries_.end())
- {
- entry2 = new Got_page_entry(*entry);
- g2->got_page_entries_.insert(entry2);
- }
// Skip over ranges whose maximum extent cannot share a page entry
// with ADDEND.
range->max_addend = addend;
*range_ptr = range;
- ++entry->num_pages;
- if (entry2 != NULL)
- ++entry2->num_pages;
++this->page_gotno_;
++g2->page_gotno_;
return;
new_pages = range->get_max_pages();
if (old_pages != new_pages)
{
- entry->num_pages += new_pages - old_pages;
- if (entry2 != NULL)
- entry2->num_pages += new_pages - old_pages;
this->page_gotno_ += new_pages - old_pages;
g2->page_gotno_ += new_pages - old_pages;
}
template<int size, bool big_endian>
void
-Mips_got_info<size, big_endian>::add_got_page_entries(
+Mips_got_info<size, big_endian>::add_got_page_count(
Mips_got_info<size, big_endian>* from)
{
- for (typename Got_page_entry_set::iterator
- p = from->got_page_entries_.begin();
- p != from->got_page_entries_.end();
- ++p)
- {
- Got_page_entry* entry = *p;
- if (this->got_page_entries_.find(entry) == this->got_page_entries_.end())
- {
- Got_page_entry* entry2 = new Got_page_entry(*entry);
- this->got_page_entries_.insert(entry2);
- this->page_gotno_ += entry->num_pages;
- }
- }
+ this->page_gotno_ += from->page_gotno_;
}
// Mips_output_data_got methods.
// Transfer the object's GOT information from FROM to TO.
to->add_got_entries(from);
- to->add_got_page_entries(from);
+ to->add_got_page_count(from);
// Record that OBJECT should use output GOT TO.
object->set_got_info(to);
// Call parent class to read symbol information.
this->base_read_symbols(sd);
+ // If this input file is a binary file, it has no processor
+ // specific data.
+ Input_file::Format format = this->input_file()->format();
+ if (format != Input_file::FORMAT_ELF)
+ {
+ gold_assert(format == Input_file::FORMAT_BINARY);
+ this->merge_processor_specific_data_ = false;
+ return;
+ }
+
// Read processor-specific flags in ELF file header.
const unsigned char* pehdr = this->get_view(elfcpp::file_header_offset,
elfcpp::Elf_sizes<size>::ehdr_size,
const size_t shdr_size = elfcpp::Elf_sizes<size>::shdr_size;
const unsigned char* pshdrs = sd->section_headers->data();
const unsigned char* ps = pshdrs + shdr_size;
+ bool must_merge_processor_specific_data = false;
for (unsigned int i = 1; i < this->shnum(); ++i, ps += shdr_size)
{
elfcpp::Shdr<size, big_endian> shdr(ps);
+ // Sometimes an object has no contents except the section name string
+ // table and an empty symbol table with the undefined symbol. We
+ // don't want to merge processor-specific data from such an object.
+ if (shdr.get_sh_type() == elfcpp::SHT_SYMTAB)
+ {
+ // Symbol table is not empty.
+ const typename elfcpp::Elf_types<size>::Elf_WXword sym_size =
+ elfcpp::Elf_sizes<size>::sym_size;
+ if (shdr.get_sh_size() > sym_size)
+ must_merge_processor_specific_data = true;
+ }
+ else if (shdr.get_sh_type() != elfcpp::SHT_STRTAB)
+ // If this is neither an empty symbol table nor a string table,
+ // be conservative.
+ must_merge_processor_specific_data = true;
+
if (shdr.get_sh_type() == elfcpp::SHT_MIPS_REGINFO)
{
this->has_reginfo_section_ = true;
this->pdr_shndx_ = i;
}
}
+
+ // This is rare.
+ if (!must_merge_processor_specific_data)
+ this->merge_processor_specific_data_ = false;
}
// Discard MIPS16 stub secions that are not needed.
of->write_output_view(offset, data_size, view);
}
+// Mips_output_section_options methods.
+
+template<int size, bool big_endian>
+void
+Mips_output_section_options<size, big_endian>::do_write(Output_file* of)
+{
+ off_t offset = this->offset();
+ const section_size_type oview_size =
+ convert_to_section_size_type(this->data_size());
+ unsigned char* view = of->get_output_view(offset, oview_size);
+ const unsigned char* end = view + oview_size;
+
+ while (view + 8 <= end)
+ {
+ unsigned char kind = elfcpp::Swap<8, big_endian>::readval(view);
+ unsigned char sz = elfcpp::Swap<8, big_endian>::readval(view + 1);
+ if (sz < 8)
+ {
+ gold_error(_("Warning: bad `%s' option size %u smaller "
+ "than its header in output section"),
+ this->name(), sz);
+ break;
+ }
+
+ // Only update ri_gp_value (GP register value) field of ODK_REGINFO entry.
+ if (this->target_->is_output_n64() && kind == elfcpp::ODK_REGINFO)
+ elfcpp::Swap<size, big_endian>::writeval(view + 32,
+ this->target_->gp_value());
+ else if (kind == elfcpp::ODK_REGINFO)
+ elfcpp::Swap<size, big_endian>::writeval(view + 28,
+ this->target_->gp_value());
+
+ view += sz;
+ }
+
+ of->write_output_view(offset, oview_size, view);
+}
+
// Mips_output_section_abiflags methods.
template<int size, bool big_endian>
this->got_,
0, 0, elfcpp::STT_OBJECT,
elfcpp::STB_GLOBAL,
- elfcpp::STV_DEFAULT, 0,
+ elfcpp::STV_HIDDEN, 0,
false, false);
}
void
Target_mips<size, big_endian>::set_gp(Layout* layout, Symbol_table* symtab)
{
- if (this->gp_ != NULL)
- return;
+ gold_assert(this->gp_ == NULL);
- Output_data* section = layout->find_output_section(".got");
- if (section == NULL)
+ Sized_symbol<size>* gp =
+ static_cast<Sized_symbol<size>*>(symtab->lookup("_gp"));
+
+ // Set _gp symbol if the linker script hasn't created it.
+ if (gp == NULL || gp->source() != Symbol::IS_CONSTANT)
{
// If there is no .got section, gp should be based on .sdata.
- // TODO(sasa): This is probably not needed. This was needed for older
- // MIPS architectures which accessed both GOT and .sdata section using
- // gp-relative addressing. Modern Mips Linux ELF architectures don't
- // access .sdata using gp-relative addressing.
- for (Layout::Section_list::const_iterator
- p = layout->section_list().begin();
- p != layout->section_list().end();
- ++p)
- {
- if (strcmp((*p)->name(), ".sdata") == 0)
- {
- section = *p;
- break;
- }
- }
- }
+ Output_data* gp_section = (this->got_ != NULL
+ ? this->got_->output_section()
+ : layout->find_output_section(".sdata"));
- Sized_symbol<size>* gp =
- static_cast<Sized_symbol<size>*>(symtab->lookup("_gp"));
- if (gp != NULL)
- {
- if (gp->source() != Symbol::IS_CONSTANT && section != NULL)
- gp->init_output_data(gp->name(), NULL, section, MIPS_GP_OFFSET, 0,
- elfcpp::STT_OBJECT,
- elfcpp::STB_GLOBAL,
- elfcpp::STV_DEFAULT, 0,
- false, false);
- this->gp_ = gp;
- }
- else if (section != NULL)
- {
- gp = static_cast<Sized_symbol<size>*>(symtab->define_in_output_data(
- "_gp", NULL, Symbol_table::PREDEFINED,
- section, MIPS_GP_OFFSET, 0,
- elfcpp::STT_OBJECT,
- elfcpp::STB_GLOBAL,
- elfcpp::STV_DEFAULT,
- 0, false, false));
- this->gp_ = gp;
+ if (gp_section != NULL)
+ gp = static_cast<Sized_symbol<size>*>(symtab->define_in_output_data(
+ "_gp", NULL, Symbol_table::PREDEFINED,
+ gp_section, MIPS_GP_OFFSET, 0,
+ elfcpp::STT_NOTYPE,
+ elfcpp::STB_LOCAL,
+ elfcpp::STV_DEFAULT,
+ 0, false, false));
}
+
+ this->gp_ = gp;
}
// Set the dynamic symbol indexes. INDEX is the index of the first
case elfcpp::E_MIPS_MACH_LS2F:
return mach_mips_loongson_2f;
- case elfcpp::E_MIPS_MACH_LS3A:
- return mach_mips_loongson_3a;
+ case elfcpp::E_MIPS_MACH_GS464:
+ return mach_mips_gs464;
+
+ case elfcpp::E_MIPS_MACH_GS464E:
+ return mach_mips_gs464e;
+
+ case elfcpp::E_MIPS_MACH_GS264E:
+ return mach_mips_gs264e;
case elfcpp::E_MIPS_MACH_OCTEON3:
return mach_mips_octeon3;
case elfcpp::AFL_EXT_LOONGSON_2F:
return mach_mips_loongson_2f;
- case elfcpp::AFL_EXT_LOONGSON_3A:
- return mach_mips_loongson_3a;
-
case elfcpp::AFL_EXT_SB1:
return mach_mips_sb1;
case mach_mips_loongson_2f:
return elfcpp::AFL_EXT_LOONGSON_2F;
- case mach_mips_loongson_3a:
- return elfcpp::AFL_EXT_LOONGSON_3A;
-
case mach_mips_sb1:
return elfcpp::AFL_EXT_SB1;
&& abiflags->fp_abi != elfcpp::Val_GNU_MIPS_ABI_FP_SOFT
&& abiflags->fp_abi != elfcpp::Val_GNU_MIPS_ABI_FP_64A
&& abiflags->isa_level >= 32
- && abiflags->isa_ext != elfcpp::AFL_EXT_LOONGSON_3A)
+ && abiflags->ases != elfcpp::AFL_ASE_LOONGSON_EXT)
abiflags->flags1 |= elfcpp::AFL_FLAGS1_ODDSPREG;
}
const Input_objects* input_objects,
Symbol_table* symtab)
{
+ const bool relocatable = parameters->options().relocatable();
+
// Add +1 to MIPS16 and microMIPS init_ and _fini symbols so that DT_INIT and
// DT_FINI have correct values.
Mips_symbol<size>* init = static_cast<Mips_symbol<size>*>(
if (this->got16_addends_.size() > 0)
gold_error("Can't find matching LO16 reloc");
- // Set _gp value.
- this->set_gp(layout, symtab);
-
- // Check for any mips16 stub sections that we can discard.
- if (!parameters->options().relocatable())
- {
- for (Input_objects::Relobj_iterator p = input_objects->relobj_begin();
- p != input_objects->relobj_end();
- ++p)
- {
- Mips_relobj<size, big_endian>* object =
- Mips_relobj<size, big_endian>::as_mips_relobj(*p);
- object->discard_mips16_stub_sections(symtab);
- }
- }
-
Valtype gprmask = 0;
Valtype cprmask1 = 0;
Valtype cprmask2 = 0;
Mips_relobj<size, big_endian>* relobj =
Mips_relobj<size, big_endian>::as_mips_relobj(*p);
+ // Check for any mips16 stub sections that we can discard.
+ if (!relocatable)
+ relobj->discard_mips16_stub_sections(symtab);
+
+ if (!relobj->merge_processor_specific_data())
+ continue;
+
// Merge .reginfo contents of input objects.
if (relobj->has_reginfo_section())
{
cprmask4 |= relobj->cprmask4();
}
- Input_file::Format format = relobj->input_file()->format();
- if (format != Input_file::FORMAT_ELF)
- continue;
-
- // If all input sections will be discarded, don't use this object
- // file for merging processor specific flags.
- bool should_merge_processor_specific_flags = false;
-
- for (unsigned int i = 1; i < relobj->shnum(); ++i)
- if (relobj->output_section(i) != NULL)
- {
- should_merge_processor_specific_flags = true;
- break;
- }
-
- if (!should_merge_processor_specific_flags)
- continue;
-
// Merge processor specific flags.
Mips_abiflags<big_endian> in_abiflags;
elfcpp::SHF_ALLOC,
abiflags_section, ORDER_INVALID, false);
- if (!parameters->options().relocatable() && os != NULL)
+ if (!relocatable && os != NULL)
{
Output_segment* abiflags_segment =
layout->make_output_segment(elfcpp::PT_MIPS_ABIFLAGS, elfcpp::PF_R);
elfcpp::SHF_ALLOC, reginfo_section,
ORDER_INVALID, false);
- if (!parameters->options().relocatable() && os != NULL)
+ if (!relocatable && os != NULL)
{
Output_segment* reginfo_segment =
layout->make_output_segment(elfcpp::PT_MIPS_REGINFO,
false, false);
}
- if (!parameters->options().relocatable() && !parameters->doing_static_link())
+ if (!relocatable && !parameters->doing_static_link())
// In case there is no .got section, create one.
this->got_section(symtab, layout);
this->copy_relocs_.emit_mips(this->rel_dyn_section(layout), symtab, layout,
this);
+ // Set _gp value.
+ this->set_gp(layout, symtab);
+
// Emit dynamic relocs.
for (typename std::vector<Dyn_reloc>::iterator p = this->dyn_relocs_.begin();
p != this->dyn_relocs_.end();
symtab));
// Add NULL segment.
- if (!parameters->options().relocatable())
+ if (!relocatable)
layout->make_output_segment(elfcpp::PT_NULL, 0);
// Fill in some more dynamic tags.
Output_data_dynamic* const odyn = layout->dynamic_data();
if (odyn != NULL
- && !parameters->options().relocatable()
+ && !relocatable
&& !parameters->doing_static_link())
{
unsigned int d_val;
break;
case Reloc_funcs::STATUS_OVERFLOW:
gold_error_at_location(relinfo, relnum, reloc.get_r_offset(),
- _("relocation overflow"));
+ _("relocation overflow: "
+ "%u against local symbol %u in %s"),
+ r_type, r_sym, object->name().c_str());
break;
case Reloc_funcs::STATUS_BAD_RELOC:
gold_error_at_location(relinfo, relnum, reloc.get_r_offset(),
// looking for relocs that would need to refer to MIPS16 stubs.
mips_sym->set_need_fn_stub();
- // A reference to _GLOBAL_OFFSET_TABLE_ implies that we need a got
- // section. We check here to avoid creating a dynamic reloc against
- // _GLOBAL_OFFSET_TABLE_.
- if (!target->has_got_section()
- && strcmp(gsym->name(), "_GLOBAL_OFFSET_TABLE_") == 0)
- target->got_section(symtab, layout);
-
// We need PLT entries if there are static-only relocations against
// an externally-defined function. This can technically occur for
// shared libraries if there are branches to the symbol, although it
unsigned int r_type3;
unsigned char r_ssym;
typename elfcpp::Elf_types<size>::Elf_Swxword r_addend;
+ // r_offset and r_type of the next relocation is needed for resolving multiple
+ // consecutive relocations with the same offset.
+ Mips_address next_r_offset = static_cast<Mips_address>(0) - 1;
+ unsigned int next_r_type = elfcpp::R_MIPS_NONE;
+
+ elfcpp::Shdr<size, big_endian> shdr(relinfo->reloc_shdr);
+ size_t reloc_count = shdr.get_sh_size() / shdr.get_sh_entsize();
if (rel_type == elfcpp::SHT_RELA)
{
r_ssym = Mips_classify_reloc<elfcpp::SHT_RELA, size, big_endian>::
get_r_ssym(&rela);
r_addend = rela.get_r_addend();
+ // If this is not last relocation, get r_offset and r_type of the next
+ // relocation.
+ if (relnum + 1 < reloc_count)
+ {
+ const int reloc_size = elfcpp::Elf_sizes<size>::rela_size;
+ const Relatype next_rela(preloc + reloc_size);
+ next_r_offset = next_rela.get_r_offset();
+ next_r_type =
+ Mips_classify_reloc<elfcpp::SHT_RELA, size, big_endian>::
+ get_r_type(&next_rela);
+ }
}
else
{
r_type = Mips_classify_reloc<elfcpp::SHT_REL, size, big_endian>::
get_r_type(&rel);
r_ssym = 0;
- r_type2 = 0;
- r_type3 = 0;
+ r_type2 = elfcpp::R_MIPS_NONE;
+ r_type3 = elfcpp::R_MIPS_NONE;
r_addend = 0;
+ // If this is not last relocation, get r_offset and r_type of the next
+ // relocation.
+ if (relnum + 1 < reloc_count)
+ {
+ const int reloc_size = elfcpp::Elf_sizes<size>::rel_size;
+ const Reltype next_rel(preloc + reloc_size);
+ next_r_offset = next_rel.get_r_offset();
+ next_r_type = Mips_classify_reloc<elfcpp::SHT_REL, size, big_endian>::
+ get_r_type(&next_rel);
+ }
}
typedef Mips_relocate_functions<size, big_endian> Reloc_funcs;
//
// (c) the section allows direct references to MIPS16 functions.
if (r_type != elfcpp::R_MIPS16_26
- && !parameters->options().relocatable()
&& ((mips_sym != NULL
&& mips_sym->has_mips16_fn_stub()
&& (r_type != elfcpp::R_MIPS16_CALL16 || mips_sym->need_fn_stub()))
// to a standard MIPS function, we need to redirect the call to the stub.
// Note that we specifically exclude R_MIPS16_CALL16 from this behavior;
// indirect calls should use an indirect stub instead.
- else if (r_type == elfcpp::R_MIPS16_26 && !parameters->options().relocatable()
+ else if (r_type == elfcpp::R_MIPS16_26
&& ((mips_sym != NULL
&& (mips_sym->has_mips16_call_stub()
|| mips_sym->has_mips16_call_fp_stub()))
// entry is used if a standard PLT entry has also been made.
else if ((r_type == elfcpp::R_MIPS16_26
|| r_type == elfcpp::R_MICROMIPS_26_S1)
- && !parameters->options().relocatable()
&& mips_sym != NULL
&& mips_sym->has_plt_offset()
&& mips_sym->has_comp_plt_offset()
// symbol would be 16-bit code, and that direct jumps were therefore
// acceptable.
cross_mode_jump =
- (!parameters->options().relocatable()
- && !(gsym != NULL && gsym->is_weak_undefined())
+ (!(gsym != NULL && gsym->is_weak_undefined())
&& ((r_type == elfcpp::R_MIPS16_26 && !target_is_16_bit_code)
|| (r_type == elfcpp::R_MICROMIPS_26_S1 && !target_is_micromips_code)
|| ((r_type == elfcpp::R_MIPS_26 || r_type == elfcpp::R_MIPS_JALR)
unsigned int got_offset = 0;
int gp_offset = 0;
- bool calculate_only = false;
- Valtype calculated_value = 0;
+ // Whether we have to extract addend from instruction.
bool extract_addend = rel_type == elfcpp::SHT_REL;
unsigned int r_types[3] = { r_type, r_type2, r_type3 };
if (r_types[i] == elfcpp::R_MIPS_NONE)
break;
- // TODO(Vladimir)
- // Check if the next relocation is for the same instruction.
- calculate_only = i == 2 ? false
- : r_types[i+1] != elfcpp::R_MIPS_NONE;
+ // If we didn't apply previous relocation, use its result as addend
+ // for current.
+ if (this->calculate_only_)
+ {
+ r_addend = this->calculated_value_;
+ extract_addend = false;
+ }
+
+ // In the N32 and 64-bit ABIs there may be multiple consecutive
+ // relocations for the same offset. In that case we are
+ // supposed to treat the output of each relocation as the addend
+ // for the next. For N64 ABI, we are checking offsets only in a
+ // third operation in a record (r_type3).
+ this->calculate_only_ =
+ (object->is_n64() && i < 2
+ ? r_types[i+1] != elfcpp::R_MIPS_NONE
+ : (r_offset == next_r_offset) && (next_r_type != elfcpp::R_MIPS_NONE));
if (object->is_n64())
{
break;
case elfcpp::R_MIPS_16:
reloc_status = Reloc_funcs::rel16(view, object, psymval, r_addend,
- extract_addend, calculate_only,
- &calculated_value);
+ extract_addend,
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_32:
if (should_apply_static_reloc(mips_sym, r_types[i], output_section,
target))
reloc_status = Reloc_funcs::rel32(view, object, psymval, r_addend,
- extract_addend, calculate_only,
- &calculated_value);
+ extract_addend,
+ this->calculate_only_,
+ &this->calculated_value_);
if (mips_sym != NULL
&& (mips_sym->is_mips16() || mips_sym->is_micromips())
&& mips_sym->global_got_area() == GGA_RELOC_ONLY)
if (should_apply_static_reloc(mips_sym, r_types[i], output_section,
target))
reloc_status = Reloc_funcs::rel64(view, object, psymval, r_addend,
- extract_addend, calculate_only,
- &calculated_value, false);
+ extract_addend,
+ this->calculate_only_,
+ &this->calculated_value_, false);
else if (target->is_output_n64() && r_addend != 0)
// Only apply the addend. The static relocation was RELA, but the
// dynamic relocation is REL, so we need to apply the addend.
reloc_status = Reloc_funcs::rel64(view, object, psymval, r_addend,
- extract_addend, calculate_only,
- &calculated_value, true);
+ extract_addend,
+ this->calculate_only_,
+ &this->calculated_value_, true);
break;
case elfcpp::R_MIPS_REL32:
gold_unreachable();
case elfcpp::R_MIPS_PC32:
reloc_status = Reloc_funcs::relpc32(view, object, psymval, address,
r_addend, extract_addend,
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS16_26:
case elfcpp::R_MICROMIPS_26_S1:
reloc_status = Reloc_funcs::rel26(view, object, psymval, address,
gsym == NULL, r_addend, extract_addend, gsym, cross_mode_jump,
- r_types[i], target->jal_to_bal(), calculate_only,
- &calculated_value);
+ r_types[i], target->jal_to_bal(), this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_HI16:
r_addend, address,
gp_disp, r_types[i],
extract_addend, 0,
- target, calculate_only,
- &calculated_value);
+ target,
+ this->calculate_only_,
+ &this->calculated_value_);
else if (rel_type == elfcpp::SHT_REL)
reloc_status = Reloc_funcs::relhi16(view, object, psymval, r_addend,
address, gp_disp, r_types[i],
reloc_status = Reloc_funcs::rello16(target, view, object, psymval,
r_addend, extract_addend, address,
gp_disp, r_types[i], r_sym,
- rel_type, calculate_only,
- &calculated_value);
+ rel_type, this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_LITERAL:
target->adjusted_gp_value(object),
r_addend, extract_addend,
gsym == NULL, r_types[i],
- calculate_only, &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_PC16:
reloc_status = Reloc_funcs::relpc16(view, object, psymval, address,
r_addend, extract_addend,
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_PC21_S2:
reloc_status = Reloc_funcs::relpc21(view, object, psymval, address,
r_addend, extract_addend,
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_PC26_S2:
reloc_status = Reloc_funcs::relpc26(view, object, psymval, address,
r_addend, extract_addend,
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_PC18_S3:
reloc_status = Reloc_funcs::relpc18(view, object, psymval, address,
r_addend, extract_addend,
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_PC19_S2:
reloc_status = Reloc_funcs::relpc19(view, object, psymval, address,
r_addend, extract_addend,
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_PCHI16:
reloc_status = Reloc_funcs::do_relpchi16(view, object, psymval,
r_addend, address,
extract_addend, 0,
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
else if (rel_type == elfcpp::SHT_REL)
reloc_status = Reloc_funcs::relpchi16(view, object, psymval,
r_addend, address, r_sym,
case elfcpp::R_MIPS_PCLO16:
reloc_status = Reloc_funcs::relpclo16(view, object, psymval, r_addend,
extract_addend, address, r_sym,
- rel_type, calculate_only,
- &calculated_value);
+ rel_type, this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MICROMIPS_PC7_S1:
reloc_status = Reloc_funcs::relmicromips_pc7_s1(view, object, psymval,
- address, r_addend,
- extract_addend,
- calculate_only,
- &calculated_value);
+ address, r_addend,
+ extract_addend,
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MICROMIPS_PC10_S1:
reloc_status = Reloc_funcs::relmicromips_pc10_s1(view, object,
- psymval, address,
- r_addend, extract_addend,
- calculate_only,
- &calculated_value);
+ psymval, address,
+ r_addend, extract_addend,
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MICROMIPS_PC16_S1:
reloc_status = Reloc_funcs::relmicromips_pc16_s1(view, object,
- psymval, address,
- r_addend, extract_addend,
- calculate_only,
- &calculated_value);
+ psymval, address,
+ r_addend, extract_addend,
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_GPREL32:
reloc_status = Reloc_funcs::relgprel32(view, object, psymval,
target->adjusted_gp_value(object),
r_addend, extract_addend,
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_GOT_HI16:
case elfcpp::R_MIPS_CALL_HI16:
object, r_addend);
gp_offset = target->got_section()->gp_offset(got_offset, object);
reloc_status = Reloc_funcs::relgot_hi16(view, gp_offset,
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
update_got_entry = changed_symbol_value;
break;
object, r_addend);
gp_offset = target->got_section()->gp_offset(got_offset, object);
reloc_status = Reloc_funcs::relgot_lo16(view, gp_offset,
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
update_got_entry = changed_symbol_value;
break;
gp_offset = target->got_section()->gp_offset(got_offset, object);
if (eh_reloc(r_types[i]))
reloc_status = Reloc_funcs::releh(view, gp_offset,
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
else
reloc_status = Reloc_funcs::relgot(view, gp_offset,
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_CALL16:
case elfcpp::R_MIPS16_CALL16:
object);
gp_offset = target->got_section()->gp_offset(got_offset, object);
reloc_status = Reloc_funcs::relgot(view, gp_offset,
- calculate_only, &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
// TODO(sasa): We should also initialize update_got_entry
// in other place swhere relgot is called.
update_got_entry = changed_symbol_value;
object);
gp_offset = target->got_section()->gp_offset(got_offset, object);
reloc_status = Reloc_funcs::relgot(view, gp_offset,
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
}
else
{
if (rel_type == elfcpp::SHT_RELA)
reloc_status = Reloc_funcs::do_relgot16_local(view, object,
- psymval, r_addend,
- extract_addend, 0,
- target,
- calculate_only,
- &calculated_value);
+ psymval, r_addend,
+ extract_addend, 0,
+ target,
+ this->calculate_only_,
+ &this->calculated_value_);
else if (rel_type == elfcpp::SHT_REL)
reloc_status = Reloc_funcs::relgot16_local(view, object,
psymval, r_addend,
GOT_TYPE_TLS_PAIR,
object, r_addend);
gp_offset = target->got_section()->gp_offset(got_offset, object);
- reloc_status = Reloc_funcs::relgot(view, gp_offset, calculate_only,
- &calculated_value);
+ reloc_status = Reloc_funcs::relgot(view, gp_offset,
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_TLS_GOTTPREL:
GOT_TYPE_TLS_OFFSET,
object, r_addend);
gp_offset = target->got_section()->gp_offset(got_offset, object);
- reloc_status = Reloc_funcs::relgot(view, gp_offset, calculate_only,
- &calculated_value);
+ reloc_status = Reloc_funcs::relgot(view, gp_offset,
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_TLS_LDM:
// the module index.
got_offset = target->got_section()->tls_ldm_offset(object);
gp_offset = target->got_section()->gp_offset(got_offset, object);
- reloc_status = Reloc_funcs::relgot(view, gp_offset, calculate_only,
- &calculated_value);
+ reloc_status = Reloc_funcs::relgot(view, gp_offset,
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_GOT_PAGE:
case elfcpp::R_MICROMIPS_GOT_PAGE:
reloc_status = Reloc_funcs::relgotpage(target, view, object, psymval,
r_addend, extract_addend,
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_GOT_OFST:
case elfcpp::R_MICROMIPS_GOT_OFST:
reloc_status = Reloc_funcs::relgotofst(target, view, object, psymval,
r_addend, extract_addend,
- local, calculate_only,
- &calculated_value);
+ local, this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_JALR:
cross_mode_jump, r_types[i],
target->jalr_to_bal(),
target->jr_to_b(),
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_TLS_DTPREL_HI16:
case elfcpp::R_MICROMIPS_TLS_DTPREL_HI16:
reloc_status = Reloc_funcs::tlsrelhi16(view, object, psymval,
elfcpp::DTP_OFFSET, r_addend,
- extract_addend, calculate_only,
- &calculated_value);
+ extract_addend,
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_TLS_DTPREL_LO16:
case elfcpp::R_MIPS16_TLS_DTPREL_LO16:
case elfcpp::R_MICROMIPS_TLS_DTPREL_LO16:
reloc_status = Reloc_funcs::tlsrello16(view, object, psymval,
elfcpp::DTP_OFFSET, r_addend,
- extract_addend, calculate_only,
- &calculated_value);
+ extract_addend,
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_TLS_DTPREL32:
case elfcpp::R_MIPS_TLS_DTPREL64:
reloc_status = Reloc_funcs::tlsrel32(view, object, psymval,
elfcpp::DTP_OFFSET, r_addend,
- extract_addend, calculate_only,
- &calculated_value);
+ extract_addend,
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_TLS_TPREL_HI16:
case elfcpp::R_MIPS16_TLS_TPREL_HI16:
case elfcpp::R_MICROMIPS_TLS_TPREL_HI16:
reloc_status = Reloc_funcs::tlsrelhi16(view, object, psymval,
elfcpp::TP_OFFSET, r_addend,
- extract_addend, calculate_only,
- &calculated_value);
+ extract_addend,
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_TLS_TPREL_LO16:
case elfcpp::R_MIPS16_TLS_TPREL_LO16:
case elfcpp::R_MICROMIPS_TLS_TPREL_LO16:
reloc_status = Reloc_funcs::tlsrello16(view, object, psymval,
elfcpp::TP_OFFSET, r_addend,
- extract_addend, calculate_only,
- &calculated_value);
+ extract_addend,
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_TLS_TPREL32:
case elfcpp::R_MIPS_TLS_TPREL64:
reloc_status = Reloc_funcs::tlsrel32(view, object, psymval,
elfcpp::TP_OFFSET, r_addend,
- extract_addend, calculate_only,
- &calculated_value);
+ extract_addend,
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_SUB:
case elfcpp::R_MICROMIPS_SUB:
reloc_status = Reloc_funcs::relsub(view, object, psymval, r_addend,
extract_addend,
- calculate_only, &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_HIGHER:
case elfcpp::R_MICROMIPS_HIGHER:
reloc_status = Reloc_funcs::relhigher(view, object, psymval, r_addend,
- extract_addend, calculate_only,
- &calculated_value);
+ extract_addend,
+ this->calculate_only_,
+ &this->calculated_value_);
break;
case elfcpp::R_MIPS_HIGHEST:
case elfcpp::R_MICROMIPS_HIGHEST:
reloc_status = Reloc_funcs::relhighest(view, object, psymval,
r_addend, extract_addend,
- calculate_only,
- &calculated_value);
+ this->calculate_only_,
+ &this->calculated_value_);
break;
default:
gold_error_at_location(relinfo, relnum, r_offset,
else
got->update_got_entry(got_offset, psymval->value(object, 0));
}
-
- r_addend = calculated_value;
}
- bool jal_shuffle = jal_reloc(r_type) ? !parameters->options().relocatable()
- : false;
+ bool jal_shuffle = jal_reloc(r_type);
Reloc_funcs::mips_reloc_shuffle(view, r_type, jal_shuffle);
// Report any errors.
case Reloc_funcs::STATUS_OKAY:
break;
case Reloc_funcs::STATUS_OVERFLOW:
- gold_error_at_location(relinfo, relnum, r_offset,
- _("relocation overflow"));
+ if (gsym == NULL)
+ gold_error_at_location(relinfo, relnum, r_offset,
+ _("relocation overflow: "
+ "%u against local symbol %u in %s"),
+ r_type, r_sym, object->name().c_str());
+ else if (gsym->is_defined() && gsym->source() == Symbol::FROM_OBJECT)
+ gold_error_at_location(relinfo, relnum, r_offset,
+ _("relocation overflow: "
+ "%u against '%s' defined in %s"),
+ r_type, gsym->demangled_name().c_str(),
+ gsym->object()->name().c_str());
+ else
+ gold_error_at_location(relinfo, relnum, r_offset,
+ _("relocation overflow: %u against '%s'"),
+ r_type, gsym->demangled_name().c_str());
break;
case Reloc_funcs::STATUS_BAD_RELOC:
gold_error_at_location(relinfo, relnum, r_offset,
return "mips:loongson_2e";
case elfcpp::E_MIPS_MACH_LS2F:
return "mips:loongson_2f";
- case elfcpp::E_MIPS_MACH_LS3A:
- return "mips:loongson_3a";
+ case elfcpp::E_MIPS_MACH_GS464:
+ return "mips:gs464";
+ case elfcpp::E_MIPS_MACH_GS464E:
+ return "mips:gs464e";
+ case elfcpp::E_MIPS_MACH_GS264E:
+ return "mips:gs264e";
case elfcpp::E_MIPS_MACH_OCTEON:
return "mips:octeon";
case elfcpp::E_MIPS_MACH_OCTEON2:
NULL, // attributes_vendor
"__start", // entry_symbol_name
32, // hash_entry_size
+ elfcpp::SHT_PROGBITS, // unwind_section_type
};
template<int size, bool big_endian>
NULL, // attributes_vendor
"_start", // entry_symbol_name
32, // hash_entry_size
+ elfcpp::SHT_PROGBITS, // unwind_section_type
};
// Target selector for Mips. Note this is never instantiated directly.