#include "target-select.h"
#include "tls.h"
#include "freebsd.h"
+#include "gc.h"
namespace
{
got_mod_index_offset_(-1U), tls_base_symbol_defined_(false)
{ }
+ // Hook for a new output section.
+ void
+ do_new_output_section(Output_section*) const;
+
// Scan the relocations to look for symbol adjustments.
void
- gc_process_relocs(const General_options& options,
- Symbol_table* symtab,
+ gc_process_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
// Scan the relocations to look for symbol adjustments.
void
- scan_relocs(const General_options& options,
- Symbol_table* symtab,
+ scan_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
// Finalize the sections.
void
- do_finalize_sections(Layout*);
+ do_finalize_sections(Layout*, const Input_objects*, Symbol_table*);
// Return the value to use for a dynamic which requires special
// treatment.
bool needs_special_offset_handling,
unsigned char* view,
elfcpp::Elf_types<64>::Elf_Addr view_address,
- section_size_type view_size);
+ section_size_type view_size,
+ const Reloc_symbol_changes*);
// Scan the relocs during a relocatable link.
void
- scan_relocatable_relocs(const General_options& options,
- Symbol_table* symtab,
+ scan_relocatable_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
do_is_defined_by_abi(const Symbol* sym) const
{ return strcmp(sym->name(), "__tls_get_addr") == 0; }
+ // Adjust -fstack-split code which calls non-stack-split code.
+ void
+ do_calls_non_split(Relobj* object, unsigned int shndx,
+ section_offset_type fnoffset, section_size_type fnsize,
+ unsigned char* view, section_size_type view_size,
+ std::string* from, std::string* to) const;
+
// Return the size of the GOT section.
section_size_type
got_size()
{ }
inline void
- local(const General_options& options, Symbol_table* symtab,
- Layout* layout, Target_x86_64* target,
+ local(Symbol_table* symtab, Layout* layout, Target_x86_64* target,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
Output_section* output_section,
const elfcpp::Sym<64, false>& lsym);
inline void
- global(const General_options& options, Symbol_table* symtab,
- Layout* layout, Target_x86_64* target,
+ global(Symbol_table* symtab, Layout* layout, Target_x86_64* target,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
Output_section* output_section,
Reloc_section*
rela_dyn_section(Layout*);
- // Return true if the symbol may need a COPY relocation.
- // References from an executable object to non-function symbols
- // defined in a dynamic object may need a COPY relocation.
- bool
- may_need_copy_reloc(Symbol* gsym)
- {
- return (!parameters->options().shared()
- && gsym->is_from_dynobj()
- && gsym->type() != elfcpp::STT_FUNC);
- }
-
// Add a potential copy relocation.
void
copy_reloc(Symbol_table* symtab, Layout* layout,
"/lib/ld64.so.1", // program interpreter
0x400000, // default_text_segment_address
0x1000, // abi_pagesize (overridable by -z max-page-size)
- 0x1000 // common_pagesize (overridable by -z common-page-size)
+ 0x1000, // common_pagesize (overridable by -z common-page-size)
+ elfcpp::SHN_UNDEF, // small_common_shndx
+ elfcpp::SHN_X86_64_LCOMMON, // large_common_shndx
+ 0, // small_common_section_flags
+ elfcpp::SHF_X86_64_LARGE, // large_common_section_flags
+ NULL, // attributes_section
+ NULL // attributes_vendor
};
+// This is called when a new output section is created. This is where
+// we handle the SHF_X86_64_LARGE.
+
+void
+Target_x86_64::do_new_output_section(Output_section *os) const
+{
+ if ((os->flags() & elfcpp::SHF_X86_64_LARGE) != 0)
+ os->set_is_large_section();
+}
+
// Get the GOT section, creating it if necessary.
Output_data_got<64, false>*
os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_WRITE),
- this->got_);
+ this->got_, false);
os->set_is_relro();
// The old GNU linker creates a .got.plt section. We just
os = layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_WRITE),
- this->got_plt_);
+ this->got_plt_, false);
os->set_is_relro();
// The first three entries are reserved.
gold_assert(layout != NULL);
this->rela_dyn_ = new Reloc_section(parameters->options().combreloc());
layout->add_output_section_data(".rela.dyn", elfcpp::SHT_RELA,
- elfcpp::SHF_ALLOC, this->rela_dyn_);
+ elfcpp::SHF_ALLOC, this->rela_dyn_, true);
}
return this->rela_dyn_;
}
{
this->rel_ = new Reloc_section(false);
layout->add_output_section_data(".rela.plt", elfcpp::SHT_RELA,
- elfcpp::SHF_ALLOC, this->rel_);
+ elfcpp::SHF_ALLOC, this->rel_, true);
}
void
void
Output_data_plt_x86_64::do_write(Output_file* of)
{
- const off_t offset = this->offset();
+ 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(offset, oview_size);
+ unsigned char* const oview = of->get_output_view(off, oview_size);
const off_t got_file_offset = this->got_plt_->offset();
const section_size_type got_size =
gold_assert(static_cast<section_size_type>(pov - oview) == oview_size);
gold_assert(static_cast<section_size_type>(got_pov - got_view) == got_size);
- of->write_output_view(offset, oview_size, oview);
+ of->write_output_view(off, oview_size, oview);
of->write_output_view(got_file_offset, got_size, got_view);
}
layout->add_output_section_data(".plt", elfcpp::SHT_PROGBITS,
(elfcpp::SHF_ALLOC
| elfcpp::SHF_EXECINSTR),
- this->plt_);
+ this->plt_, false);
}
}
// Scan a relocation for a local symbol.
inline void
-Target_x86_64::Scan::local(const General_options&,
- Symbol_table* symtab,
+Target_x86_64::Scan::local(Symbol_table* symtab,
Layout* layout,
Target_x86_64* target,
Sized_relobj<64, false>* object,
// Scan a relocation for a global symbol.
inline void
-Target_x86_64::Scan::global(const General_options&,
- Symbol_table* symtab,
+Target_x86_64::Scan::global(Symbol_table* symtab,
Layout* layout,
Target_x86_64* target,
Sized_relobj<64, false>* object,
// Make a dynamic relocation if necessary.
if (gsym->needs_dynamic_reloc(Symbol::ABSOLUTE_REF))
{
- if (target->may_need_copy_reloc(gsym))
+ if (gsym->may_need_copy_reloc())
{
target->copy_reloc(symtab, layout, object,
data_shndx, output_section, gsym, reloc);
target->make_plt_entry(symtab, layout, gsym);
// Make a dynamic relocation if necessary.
int flags = Symbol::NON_PIC_REF;
- if (gsym->type() == elfcpp::STT_FUNC)
+ if (gsym->is_func())
flags |= Symbol::FUNCTION_CALL;
if (gsym->needs_dynamic_reloc(flags))
{
- if (target->may_need_copy_reloc(gsym))
+ if (gsym->may_need_copy_reloc())
{
target->copy_reloc(symtab, layout, object,
data_shndx, output_section, gsym, reloc);
}
void
-Target_x86_64::gc_process_relocs(const General_options& options,
- Symbol_table* symtab,
+Target_x86_64::gc_process_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
gold::gc_process_relocs<64, false, Target_x86_64, elfcpp::SHT_RELA,
Target_x86_64::Scan>(
- options,
symtab,
layout,
this,
// Scan relocations for a section.
void
-Target_x86_64::scan_relocs(const General_options& options,
- Symbol_table* symtab,
+Target_x86_64::scan_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
gold::scan_relocs<64, false, Target_x86_64, elfcpp::SHT_RELA,
Target_x86_64::Scan>(
- options,
symtab,
layout,
this,
// Finalize the sections.
void
-Target_x86_64::do_finalize_sections(Layout* layout)
+Target_x86_64::do_finalize_sections(
+ Layout* layout,
+ const Input_objects*,
+ Symbol_table*)
{
// Fill in some more dynamic tags.
Output_data_dynamic* const odyn = layout->dynamic_data();
if (odyn != NULL)
{
- if (this->got_plt_ != NULL)
+ if (this->got_plt_ != NULL
+ && this->got_plt_->output_section() != NULL)
odyn->add_section_address(elfcpp::DT_PLTGOT, this->got_plt_);
- if (this->plt_ != NULL)
+ if (this->plt_ != NULL
+ && this->plt_->output_section() != NULL)
{
const Output_data* od = this->plt_->rel_plt();
odyn->add_section_size(elfcpp::DT_PLTRELSZ, od);
}
}
- if (this->rela_dyn_ != NULL)
+ if (this->rela_dyn_ != NULL
+ && this->rela_dyn_->output_section() != NULL)
{
const Output_data* od = this->rela_dyn_;
odyn->add_section_address(elfcpp::DT_RELA, od);
break;
case elfcpp::R_X86_64_DTPOFF32:
- gold_assert(tls_segment != NULL);
if (optimized_type == tls::TLSOPT_TO_LE)
{
// This relocation type is used in debugging information.
// haven't seen a TLSLD reloc, then we assume we should not
// optimize this reloc.
if (this->saw_tls_block_reloc_)
- value -= tls_segment->memsz();
+ {
+ gold_assert(tls_segment != NULL);
+ value -= tls_segment->memsz();
+ }
}
Relocate_functions<64, false>::rela32(view, value, addend);
break;
case elfcpp::R_X86_64_DTPOFF64:
- gold_assert(tls_segment != NULL);
if (optimized_type == tls::TLSOPT_TO_LE)
{
// See R_X86_64_DTPOFF32, just above, for why we test this.
if (this->saw_tls_block_reloc_)
- value -= tls_segment->memsz();
+ {
+ gold_assert(tls_segment != NULL);
+ value -= tls_segment->memsz();
+ }
}
Relocate_functions<64, false>::rela64(view, value, addend);
break;
// Relocate section data.
void
-Target_x86_64::relocate_section(const Relocate_info<64, false>* relinfo,
- unsigned int sh_type,
- const unsigned char* prelocs,
- size_t reloc_count,
- Output_section* output_section,
- bool needs_special_offset_handling,
- unsigned char* view,
- elfcpp::Elf_types<64>::Elf_Addr address,
- section_size_type view_size)
+Target_x86_64::relocate_section(
+ const Relocate_info<64, false>* relinfo,
+ unsigned int sh_type,
+ const unsigned char* prelocs,
+ size_t reloc_count,
+ Output_section* output_section,
+ bool needs_special_offset_handling,
+ unsigned char* view,
+ elfcpp::Elf_types<64>::Elf_Addr address,
+ section_size_type view_size,
+ const Reloc_symbol_changes* reloc_symbol_changes)
{
gold_assert(sh_type == elfcpp::SHT_RELA);
needs_special_offset_handling,
view,
address,
- view_size);
+ view_size,
+ reloc_symbol_changes);
}
// Return the size of a relocation while scanning during a relocatable
// Scan the relocs during a relocatable link.
void
-Target_x86_64::scan_relocatable_relocs(const General_options& options,
- Symbol_table* symtab,
+Target_x86_64::scan_relocatable_relocs(Symbol_table* symtab,
Layout* layout,
Sized_relobj<64, false>* object,
unsigned int data_shndx,
gold::scan_relocatable_relocs<64, false, elfcpp::SHT_RELA,
Scan_relocatable_relocs>(
- options,
symtab,
layout,
object,
// Nop sequences of various lengths.
const char nop1[1] = { 0x90 }; // nop
const char nop2[2] = { 0x66, 0x90 }; // xchg %ax %ax
- const char nop3[3] = { 0x8d, 0x76, 0x00 }; // leal 0(%esi),%esi
- const char nop4[4] = { 0x8d, 0x74, 0x26, 0x00}; // leal 0(%esi,1),%esi
- const char nop5[5] = { 0x90, 0x8d, 0x74, 0x26, // nop
- 0x00 }; // leal 0(%esi,1),%esi
- const char nop6[6] = { 0x8d, 0xb6, 0x00, 0x00, // leal 0L(%esi),%esi
+ const char nop3[3] = { 0x0f, 0x1f, 0x00 }; // nop (%rax)
+ const char nop4[4] = { 0x0f, 0x1f, 0x40, 0x00}; // nop 0(%rax)
+ const char nop5[5] = { 0x0f, 0x1f, 0x44, 0x00, // nop 0(%rax,%rax,1)
+ 0x00 };
+ const char nop6[6] = { 0x66, 0x0f, 0x1f, 0x44, // nopw 0(%rax,%rax,1)
0x00, 0x00 };
- const char nop7[7] = { 0x8d, 0xb4, 0x26, 0x00, // leal 0L(%esi,1),%esi
+ const char nop7[7] = { 0x0f, 0x1f, 0x80, 0x00, // nopl 0L(%rax)
0x00, 0x00, 0x00 };
- const char nop8[8] = { 0x90, 0x8d, 0xb4, 0x26, // nop
- 0x00, 0x00, 0x00, 0x00 }; // leal 0L(%esi,1),%esi
- const char nop9[9] = { 0x89, 0xf6, 0x8d, 0xbc, // movl %esi,%esi
- 0x27, 0x00, 0x00, 0x00, // leal 0L(%edi,1),%edi
+ const char nop8[8] = { 0x0f, 0x1f, 0x84, 0x00, // nopl 0L(%rax,%rax,1)
+ 0x00, 0x00, 0x00, 0x00 };
+ const char nop9[9] = { 0x66, 0x0f, 0x1f, 0x84, // nopw 0L(%rax,%rax,1)
+ 0x00, 0x00, 0x00, 0x00,
0x00 };
- const char nop10[10] = { 0x8d, 0x76, 0x00, 0x8d, // leal 0(%esi),%esi
- 0xbc, 0x27, 0x00, 0x00, // leal 0L(%edi,1),%edi
+ const char nop10[10] = { 0x66, 0x2e, 0x0f, 0x1f, // nopw %cs:0L(%rax,%rax,1)
+ 0x84, 0x00, 0x00, 0x00,
0x00, 0x00 };
- const char nop11[11] = { 0x8d, 0x74, 0x26, 0x00, // leal 0(%esi,1),%esi
- 0x8d, 0xbc, 0x27, 0x00, // leal 0L(%edi,1),%edi
+ const char nop11[11] = { 0x66, 0x66, 0x2e, 0x0f, // data16
+ 0x1f, 0x84, 0x00, 0x00, // nopw %cs:0L(%rax,%rax,1)
0x00, 0x00, 0x00 };
- const char nop12[12] = { 0x8d, 0xb6, 0x00, 0x00, // leal 0L(%esi),%esi
- 0x00, 0x00, 0x8d, 0xbf, // leal 0L(%edi),%edi
+ const char nop12[12] = { 0x66, 0x66, 0x66, 0x2e, // data16; data16
+ 0x0f, 0x1f, 0x84, 0x00, // nopw %cs:0L(%rax,%rax,1)
0x00, 0x00, 0x00, 0x00 };
- const char nop13[13] = { 0x8d, 0xb6, 0x00, 0x00, // leal 0L(%esi),%esi
- 0x00, 0x00, 0x8d, 0xbc, // leal 0L(%edi,1),%edi
- 0x27, 0x00, 0x00, 0x00,
+ const char nop13[13] = { 0x66, 0x66, 0x66, 0x66, // data16; data16; data16
+ 0x2e, 0x0f, 0x1f, 0x84, // nopw %cs:0L(%rax,%rax,1)
+ 0x00, 0x00, 0x00, 0x00,
0x00 };
- const char nop14[14] = { 0x8d, 0xb4, 0x26, 0x00, // leal 0L(%esi,1),%esi
- 0x00, 0x00, 0x00, 0x8d, // leal 0L(%edi,1),%edi
- 0xbc, 0x27, 0x00, 0x00,
+ const char nop14[14] = { 0x66, 0x66, 0x66, 0x66, // data16; data16; data16
+ 0x66, 0x2e, 0x0f, 0x1f, // data16
+ 0x84, 0x00, 0x00, 0x00, // nopw %cs:0L(%rax,%rax,1)
0x00, 0x00 };
- const char nop15[15] = { 0xeb, 0x0d, 0x90, 0x90, // jmp .+15
- 0x90, 0x90, 0x90, 0x90, // nop,nop,nop,...
- 0x90, 0x90, 0x90, 0x90,
- 0x90, 0x90, 0x90 };
+ const char nop15[15] = { 0x66, 0x66, 0x66, 0x66, // data16; data16; data16
+ 0x66, 0x66, 0x2e, 0x0f, // data16; data16
+ 0x1f, 0x84, 0x00, 0x00, // nopw %cs:0L(%rax,%rax,1)
+ 0x00, 0x00, 0x00 };
const char* nops[16] = {
NULL,
return std::string(nops[length], length);
}
+// FNOFFSET in section SHNDX in OBJECT is the start of a function
+// compiled with -fstack-split. The function calls non-stack-split
+// code. We have to change the function so that it always ensures
+// that it has enough stack space to run some random function.
+
+void
+Target_x86_64::do_calls_non_split(Relobj* object, unsigned int shndx,
+ section_offset_type fnoffset,
+ section_size_type fnsize,
+ unsigned char* view,
+ section_size_type view_size,
+ std::string* from,
+ std::string* to) const
+{
+ // The function starts with a comparison of the stack pointer and a
+ // field in the TCB. This is followed by a jump.
+
+ // cmp %fs:NN,%rsp
+ if (this->match_view(view, view_size, fnoffset, "\x64\x48\x3b\x24\x25", 5)
+ && fnsize > 9)
+ {
+ // We will call __morestack if the carry flag is set after this
+ // comparison. We turn the comparison into an stc instruction
+ // and some nops.
+ view[fnoffset] = '\xf9';
+ this->set_view_to_nop(view, view_size, fnoffset + 1, 8);
+ }
+ // lea NN(%rsp),%r10
+ else if (this->match_view(view, view_size, fnoffset, "\x4c\x8d\x94\x24", 4)
+ && fnsize > 8)
+ {
+ // This is loading an offset from the stack pointer for a
+ // comparison. The offset is negative, so we decrease the
+ // offset by the amount of space we need for the stack. This
+ // means we will avoid calling __morestack if there happens to
+ // be plenty of space on the stack already.
+ unsigned char* pval = view + fnoffset + 4;
+ uint32_t val = elfcpp::Swap_unaligned<32, false>::readval(pval);
+ val -= parameters->options().split_stack_adjust_size();
+ elfcpp::Swap_unaligned<32, false>::writeval(pval, val);
+ }
+ else
+ {
+ if (!object->has_no_split_stack())
+ object->error(_("failed to match split-stack sequence at "
+ "section %u offset %0zx"),
+ shndx, static_cast<size_t>(fnoffset));
+ return;
+ }
+
+ // We have to change the function so that it calls
+ // __morestack_non_split instead of __morestack. The former will
+ // allocate additional stack space.
+ *from = "__morestack";
+ *to = "__morestack_non_split";
+}
+
// The selector for x86_64 object files.
class Target_selector_x86_64 : public Target_selector_freebsd