diff options
Diffstat (limited to 'gold/i386.cc')
-rw-r--r-- | gold/i386.cc | 312 |
1 files changed, 276 insertions, 36 deletions
diff --git a/gold/i386.cc b/gold/i386.cc index 468cb90..53af5ee 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -1,10 +1,14 @@ // i386.cc -- i386 target support for gold. #include "gold.h" + +#include <cstring> + #include "elfcpp.h" #include "reloc.h" #include "i386.h" #include "object.h" +#include "symtab.h" #include "layout.h" #include "output.h" #include "target.h" @@ -22,13 +26,15 @@ class Target_i386 : public Sized_target<32, false> { public: Target_i386() - : Sized_target<32, false>(&i386_info) + : Sized_target<32, false>(&i386_info), + got_(NULL) { } // Scan the relocations to look for symbol adjustments. void scan_relocs(const General_options& options, Symbol_table* symtab, + Layout* layout, Sized_object<32, false>* object, unsigned int sh_type, const unsigned char* prelocs, @@ -52,12 +58,16 @@ class Target_i386 : public Sized_target<32, false> struct Scan { inline void - local(const General_options& options, Sized_object<32, false>* object, + local(const General_options& options, Symbol_table* symtab, + Layout* layout, Target_i386* target, + Sized_object<32, false>* object, const elfcpp::Rel<32, false>& reloc, unsigned int r_type, const elfcpp::Sym<32, false>& lsym); inline void - global(const General_options& options, Sized_object<32, false>* object, + global(const General_options& options, Symbol_table* symtab, + Layout* layout, Target_i386* target, + Sized_object<32, false>* object, const elfcpp::Rel<32, false>& reloc, unsigned int r_type, Symbol* gsym); }; @@ -66,9 +76,25 @@ class Target_i386 : public Sized_target<32, false> class Relocate { public: - // Do a relocation. - static inline void - relocate(const Relocate_info<32, false>*, size_t relnum, + Relocate() + : skip_call_tls_get_addr_(false) + { } + + ~Relocate() + { + if (this->skip_call_tls_get_addr_) + { + // FIXME: This needs to specify the location somehow. + fprintf(stderr, _("%s: missing expected TLS relocation\n"), + program_name); + gold_exit(false); + } + } + + // Do a relocation. Return false if the caller should not issue + // any warnings about this relocation. + inline bool + relocate(const Relocate_info<32, false>*, Target_i386*, size_t relnum, const elfcpp::Rel<32, false>&, unsigned int r_type, Sized_symbol<32>*, elfcpp::Elf_types<32>::Elf_Addr, @@ -77,7 +103,7 @@ class Target_i386 : public Sized_target<32, false> private: // Do a TLS relocation. - static inline void + inline void relocate_tls(const Relocate_info<32, false>*, size_t relnum, const elfcpp::Rel<32, false>&, unsigned int r_type, Sized_symbol<32>*, @@ -93,6 +119,15 @@ class Target_i386 : public Sized_target<32, false> unsigned char* view, off_t view_size); + // Do a TLS Global-Dynamic to Local-Exec transition. + inline void + tls_gd_to_le(const Relocate_info<32, false>*, size_t relnum, + Output_segment* tls_segment, + const elfcpp::Rel<32, false>&, unsigned int r_type, + elfcpp::Elf_types<32>::Elf_Addr value, + unsigned char* view, + off_t view_size); + // Check the range for a TLS relocation. static inline void check_range(const Relocate_info<32, false>*, size_t relnum, @@ -102,6 +137,10 @@ class Target_i386 : public Sized_target<32, false> static inline void check_tls(const Relocate_info<32, false>*, size_t relnum, const elfcpp::Rel<32, false>&, bool); + + // This is set if we should skip the next reloc, which should be a + // PLT32 reloc against ___tls_get_addr. + bool skip_call_tls_get_addr_; }; // Adjust TLS relocation type based on the options and whether this @@ -109,9 +148,16 @@ class Target_i386 : public Sized_target<32, false> static unsigned int optimize_tls_reloc(const General_options*, bool is_local, int r_type); + // Get the GOT section, creating it if necessary. + Output_section_got<32, false>* + got_section(Symbol_table*, Layout*); + // Information about this specific target which we pass to the // general Target structure. static const Target::Target_info i386_info; + + // The GOT section. + Output_section_got<32, false>* got_; }; const Target::Target_info Target_i386::i386_info = @@ -126,6 +172,34 @@ const Target::Target_info Target_i386::i386_info = 0x1000 // common_pagesize }; +// Get the GOT section, creating it if necessary. + +Output_section_got<32, false>* +Target_i386::got_section(Symbol_table* symtab, Layout* layout) +{ + if (this->got_ == NULL) + { + this->got_ = new Output_section_got<32, false>(); + + assert(symtab != NULL && layout != NULL); + layout->add_output_section_data(".got", elfcpp::SHT_PROGBITS, + elfcpp::SHF_ALLOC, this->got_); + + // The first three entries are reserved. + this->got_->add_constant(0); + this->got_->add_constant(0); + this->got_->add_constant(0); + + // Define _GLOBAL_OFFSET_TABLE_ at the start of the section. + symtab->define_in_output_data(this, "_GLOBAL_OFFSET_TABLE_", this->got_, + 0, 0, elfcpp::STT_OBJECT, + elfcpp::STB_GLOBAL, + elfcpp::STV_HIDDEN, 0, + false, false); + } + return this->got_; +} + // Optimize the TLS relocation type based on what we know about the // symbol. IS_LOCAL is true if this symbol can be resolved entirely // locally--i.e., does not have to be in the dynamic symbol table. @@ -188,6 +262,9 @@ Target_i386::optimize_tls_reloc(const General_options* options, bool is_local, inline void Target_i386::Scan::local(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Target_i386* target, Sized_object<32, false>* object, const elfcpp::Rel<32, false>&, unsigned int r_type, const elfcpp::Sym<32, false>&) @@ -211,6 +288,12 @@ Target_i386::Scan::local(const General_options& options, case elfcpp::R_386_PC8: break; + case elfcpp::R_386_GOTOFF: + case elfcpp::R_386_GOTPC: + // We need a GOT section. + target->got_section(symtab, layout); + break; + case elfcpp::R_386_COPY: case elfcpp::R_386_GLOB_DAT: case elfcpp::R_386_JUMP_SLOT: @@ -261,8 +344,6 @@ Target_i386::Scan::local(const General_options& options, case elfcpp::R_386_GOT32: case elfcpp::R_386_PLT32: - case elfcpp::R_386_GOTOFF: - case elfcpp::R_386_GOTPC: case elfcpp::R_386_32PLT: case elfcpp::R_386_TLS_GD_32: case elfcpp::R_386_TLS_GD_PUSH: @@ -284,6 +365,9 @@ Target_i386::Scan::local(const General_options& options, inline void Target_i386::Scan::global(const General_options& options, + Symbol_table* symtab, + Layout* layout, + Target_i386* target, Sized_object<32, false>* object, const elfcpp::Rel<32, false>&, unsigned int r_type, Symbol* gsym) @@ -307,6 +391,37 @@ Target_i386::Scan::global(const General_options& options, // relocation in order to avoid a COPY relocation. break; + case elfcpp::R_386_GOT32: + // The symbol requires a GOT entry. + if (!gsym->has_got_offset()) + { + Output_section_got<32, false>* got = target->got_section(symtab, + layout); + const unsigned int got_offset = got->add_global(gsym); + gsym->set_got_offset(got_offset); + + // If this symbol is not resolved locally, we need to add a + // dynamic relocation for it. + if (!gsym->is_resolved_locally()) + abort(); + } + break; + + case elfcpp::R_386_PLT32: + // If the symbol is resolved locally, this is just a PC32 reloc. + if (gsym->is_resolved_locally()) + break; + fprintf(stderr, + _("%s: %s: unsupported reloc %u against global symbol %s\n"), + program_name, object->name().c_str(), r_type, gsym->name()); + break; + + case elfcpp::R_386_GOTOFF: + case elfcpp::R_386_GOTPC: + // We need a GOT section. + target->got_section(symtab, layout); + break; + case elfcpp::R_386_COPY: case elfcpp::R_386_GLOB_DAT: case elfcpp::R_386_JUMP_SLOT: @@ -332,7 +447,7 @@ Target_i386::Scan::global(const General_options& options, case elfcpp::R_386_TLS_GOTDESC: case elfcpp::R_386_TLS_DESC_CALL: r_type = Target_i386::optimize_tls_reloc(&options, - !gsym->in_dynsym(), + gsym->is_resolved_locally(), r_type); switch (r_type) { @@ -357,10 +472,6 @@ Target_i386::Scan::global(const General_options& options, } break; - case elfcpp::R_386_GOT32: - case elfcpp::R_386_PLT32: - case elfcpp::R_386_GOTOFF: - case elfcpp::R_386_GOTPC: case elfcpp::R_386_32PLT: case elfcpp::R_386_TLS_GD_32: case elfcpp::R_386_TLS_GD_PUSH: @@ -384,6 +495,7 @@ Target_i386::Scan::global(const General_options& options, void Target_i386::scan_relocs(const General_options& options, Symbol_table* symtab, + Layout* layout, Sized_object<32, false>* object, unsigned int sh_type, const unsigned char* prelocs, @@ -399,9 +511,12 @@ Target_i386::scan_relocs(const General_options& options, gold_exit(false); } - gold::scan_relocs<32, false, elfcpp::SHT_REL, Target_i386::Scan>( + gold::scan_relocs<32, false, Target_i386, elfcpp::SHT_REL, + Target_i386::Scan>( options, symtab, + layout, + this, object, prelocs, reloc_count, @@ -412,8 +527,9 @@ Target_i386::scan_relocs(const General_options& options, // Perform a relocation. -inline void +inline bool Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, + Target_i386* target, size_t relnum, const elfcpp::Rel<32, false>& rel, unsigned int r_type, @@ -423,6 +539,23 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, elfcpp::Elf_types<32>::Elf_Addr address, off_t view_size) { + if (this->skip_call_tls_get_addr_) + { + if (r_type != elfcpp::R_386_PLT32 + || gsym == NULL + || strcmp(gsym->name(), "___tls_get_addr") != 0) + { + fprintf(stderr, _("%s: %s: missing expected TLS relocation\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str()); + gold_exit(false); + } + + this->skip_call_tls_get_addr_ = false; + + return false; + } + switch (r_type) { case elfcpp::R_386_NONE: @@ -454,6 +587,34 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, Relocate_functions<32, false>::pcrel8(view, value, address); break; + case elfcpp::R_386_PLT32: + if (gsym->is_resolved_locally()) + Relocate_functions<32, false>::pcrel32(view, value, address); + else + fprintf(stderr, _("%s: %s: unsupported reloc %u\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str(), + r_type); + break; + + case elfcpp::R_386_GOT32: + // Local GOT offsets not yet supported. + assert(gsym); + assert(gsym->has_got_offset()); + value = gsym->got_offset(); + Relocate_functions<32, false>::rel32(view, value); + break; + + case elfcpp::R_386_GOTOFF: + value -= target->got_section(NULL, NULL)->address(); + Relocate_functions<32, false>::rel32(view, value); + break; + + case elfcpp::R_386_GOTPC: + value = target->got_section(NULL, NULL)->address(); + Relocate_functions<32, false>::pcrel32(view, value, address); + break; + case elfcpp::R_386_COPY: case elfcpp::R_386_GLOB_DAT: case elfcpp::R_386_JUMP_SLOT: @@ -480,15 +641,10 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, case elfcpp::R_386_TLS_LE_32: case elfcpp::R_386_TLS_GOTDESC: case elfcpp::R_386_TLS_DESC_CALL: - Target_i386::Relocate::relocate_tls(relinfo, relnum, rel, r_type, - gsym, value, view, address, - view_size); + this->relocate_tls(relinfo, relnum, rel, r_type, gsym, value, view, + address, view_size); break; - case elfcpp::R_386_GOT32: - case elfcpp::R_386_PLT32: - case elfcpp::R_386_GOTOFF: - case elfcpp::R_386_GOTPC: case elfcpp::R_386_32PLT: case elfcpp::R_386_TLS_GD_32: case elfcpp::R_386_TLS_GD_PUSH: @@ -507,6 +663,8 @@ Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, // gold_exit(false); break; } + + return true; } // Perform a TLS relocation. @@ -531,7 +689,7 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, gold_exit(false); } - const bool is_local = gsym == NULL || !gsym->in_dynsym(); + const bool is_local = gsym == NULL || gsym->is_resolved_locally(); const unsigned int opt_r_type = Target_i386::optimize_tls_reloc(relinfo->options, is_local, r_type); switch (r_type) @@ -564,6 +722,20 @@ Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, break; case elfcpp::R_386_TLS_GD: + if (opt_r_type == elfcpp::R_386_TLS_LE_32) + { + this->tls_gd_to_le(relinfo, relnum, tls_segment, + rel, r_type, value, view, + view_size); + break; + } + fprintf(stderr, _("%s: %s: unsupported reloc %u\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str(), + r_type); + // gold_exit(false); + break; + case elfcpp::R_386_TLS_LDM: case elfcpp::R_386_TLS_LDO_32: case elfcpp::R_386_TLS_GOTDESC: @@ -667,14 +839,79 @@ Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo, Target_i386::Relocate::check_tls(relinfo, relnum, rel, 0); } - if (r_type == elfcpp::R_386_TLS_IE_32) - value = tls_segment->vaddr() + tls_segment->memsz() - value; - else // elfcpp::R_386_TLS_IE, elfcpp::R_386_TLS_GOTIE - value = value - (tls_segment->vaddr() + tls_segment->memsz()); + value = tls_segment->vaddr() + tls_segment->memsz() - value; + if (r_type == elfcpp::R_386_TLS_IE || r_type == elfcpp::R_386_TLS_GOTIE) + value = - value; Relocate_functions<32, false>::rel32(view, value); } +// Do a relocation in which we convert a TLS Global-Dynamic to a +// Local-Exec. + +inline void +Target_i386::Relocate::tls_gd_to_le(const Relocate_info<32, false>* relinfo, + size_t relnum, + Output_segment* tls_segment, + const elfcpp::Rel<32, false>& rel, + unsigned int, + elfcpp::Elf_types<32>::Elf_Addr value, + unsigned char* view, + off_t view_size) +{ + // leal foo(,%reg,1),%eax; call ___tls_get_addr + // ==> movl %gs,0,%eax; subl $foo@tpoff,%eax + // leal foo(%reg),%eax; call ___tls_get_addr + // ==> movl %gs:0,%eax; subl $foo@tpoff,%eax + + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -2); + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, 9); + + unsigned char op1 = view[-1]; + unsigned char op2 = view[-2]; + + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + op2 == 0x8d || op2 == 0x04); + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + view[4] == 0xe8); + + int roff = 5; + + if (op2 == 0x04) + { + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -3); + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + view[-3] == 0x8d); + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + ((op1 & 0xc7) == 0x05 + && op1 != (4 << 3))); + memcpy(view - 3, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12); + } + else + { + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + (op1 & 0xf8) == 0x80 && (op1 & 7) != 4); + if (rel.get_r_offset() + 9 < view_size && view[9] == 0x90) + { + // There is a trailing nop. Use the size byte subl. + memcpy(view - 2, "\x65\xa1\0\0\0\0\x81\xe8\0\0\0", 12); + roff = 6; + } + else + { + // Use the five byte subl. + memcpy(view - 2, "\x65\xa1\0\0\0\0\x2d\0\0\0", 11); + } + } + + value = tls_segment->vaddr() + tls_segment->memsz() - value; + Relocate_functions<32, false>::rel32(view + roff, value); + + // The next reloc should be a PLT32 reloc against __tls_get_addr. + // We can skip it. + this->skip_call_tls_get_addr_ = true; +} + // Check the range for a TLS relocation. inline void @@ -724,8 +961,10 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo, { assert(sh_type == elfcpp::SHT_REL); - gold::relocate_section<32, false, elfcpp::SHT_REL, Target_i386::Relocate>( + gold::relocate_section<32, false, Target_i386, elfcpp::SHT_REL, + Target_i386::Relocate>( relinfo, + this, prelocs, reloc_count, view, @@ -733,10 +972,6 @@ Target_i386::relocate_section(const Relocate_info<32, false>* relinfo, view_size); } -// The i386 target. - -Target_i386 target_i386; - // The selector for i386 object files. class Target_selector_i386 : public Target_selector @@ -747,16 +982,21 @@ public: { } Target* - recognize(int machine, int osabi, int abiversion) const; + recognize(int machine, int osabi, int abiversion); + + private: + Target_i386* target_; }; // Recognize an i386 object file when we already know that the machine // number is EM_386. Target* -Target_selector_i386::recognize(int, int, int) const +Target_selector_i386::recognize(int, int, int) { - return &target_i386; + if (this->target_ == NULL) + this->target_ = new Target_i386(); + return this->target_; } Target_selector_i386 target_selector_i386; |