diff --git a/elfcpp/elfcpp_internal.h b/elfcpp/elfcpp_internal.h index 0d69baebfa..e4c863644a 100644 --- a/elfcpp/elfcpp_internal.h +++ b/elfcpp/elfcpp_internal.h @@ -16,7 +16,7 @@ namespace elfcpp namespace internal { -#ifdef WORDS_BIG_ENDIAN +#ifdef WORDS_BIGENDIAN const bool host_big_endian = true; #else const bool host_big_endian = false; diff --git a/gold/archive.cc b/gold/archive.cc index 8639643967..a64109aef1 100644 --- a/gold/archive.cc +++ b/gold/archive.cc @@ -300,7 +300,7 @@ Archive::include_member(Symbol_table* symtab, Layout* layout, gold_exit(false); } - Object* obj = make_elf_object((std::string(this->input_file_->name()) + Object* obj = make_elf_object((std::string(this->input_file_->filename()) + "(" + n + ")"), this->input_file_, memoff, p, bytes); diff --git a/gold/config.in b/gold/config.in index 3a452ffd88..1d40c66bac 100644 --- a/gold/config.in +++ b/gold/config.in @@ -69,3 +69,7 @@ /* Version number of package */ #undef VERSION + +/* Define to 1 if your processor stores words with the most significant byte + first (like Motorola and SPARC, unlike Intel and VAX). */ +#undef WORDS_BIGENDIAN diff --git a/gold/configure b/gold/configure index c0d93b7d1c..a64cd709b5 100755 --- a/gold/configure +++ b/gold/configure @@ -3869,6 +3869,238 @@ echo "${ECHO_T}found xgettext program is not GNU xgettext; ignore it" >&6 +echo "$as_me:$LINENO: checking whether byte ordering is bigendian" >&5 +echo $ECHO_N "checking whether byte ordering is bigendian... $ECHO_C" >&6 +if test "${ac_cv_c_bigendian+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # See if sys/param.h defines the BYTE_ORDER macro. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +#if !BYTE_ORDER || !BIG_ENDIAN || !LITTLE_ENDIAN + bogus endian macros +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + # It does; now see whether it defined to BIG_ENDIAN or not. +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +#if BYTE_ORDER != BIG_ENDIAN + not big endian +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +ac_cv_c_bigendian=no +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +# It does not; compile a test program. +if test "$cross_compiling" = yes; then + # try to guess the endianness by grepping values into an object file + ac_cv_c_bigendian=unknown + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +short ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 }; +short ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 }; +void _ascii () { char *s = (char *) ascii_mm; s = (char *) ascii_ii; } +short ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 }; +short ebcdic_mm[] = { 0xC2C9, 0xC785, 0x95C4, 0x8981, 0x95E2, 0xA8E2, 0 }; +void _ebcdic () { char *s = (char *) ebcdic_mm; s = (char *) ebcdic_ii; } +int +main () +{ + _ascii (); _ebcdic (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + (eval $ac_compile) 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && + { ac_try='test -z "$ac_c_werror_flag" + || test ! -s conftest.err' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; } && + { ac_try='test -s conftest.$ac_objext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + if grep BIGenDianSyS conftest.$ac_objext >/dev/null ; then + ac_cv_c_bigendian=yes +fi +if grep LiTTleEnDian conftest.$ac_objext >/dev/null ; then + if test "$ac_cv_c_bigendian" = unknown; then + ac_cv_c_bigendian=no + else + # finding both strings is unlikely to happen, but who knows? + ac_cv_c_bigendian=unknown + fi +fi +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +int +main () +{ + /* Are we little or big endian? From Harbison&Steele. */ + union + { + long l; + char c[sizeof (long)]; + } u; + u.l = 1; + exit (u.c[sizeof (long) - 1] == 1); +} +_ACEOF +rm -f conftest$ac_exeext +if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + (eval $ac_link) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5 + (eval $ac_try) 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_c_bigendian=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_c_bigendian=yes +fi +rm -f core *.core gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi +fi +rm -f conftest.err conftest.$ac_objext conftest.$ac_ext +fi +echo "$as_me:$LINENO: result: $ac_cv_c_bigendian" >&5 +echo "${ECHO_T}$ac_cv_c_bigendian" >&6 +case $ac_cv_c_bigendian in + yes) + +cat >>confdefs.h <<\_ACEOF +#define WORDS_BIGENDIAN 1 +_ACEOF + ;; + no) + ;; + *) + { { echo "$as_me:$LINENO: error: unknown endianness +presetting ac_cv_c_bigendian=no (or yes) will help" >&5 +echo "$as_me: error: unknown endianness +presetting ac_cv_c_bigendian=no (or yes) will help" >&2;} + { (exit 1); exit 1; }; } ;; +esac + + + GCC_WARN_CFLAGS="-W -Wall -Wstrict-prototypes -Wmissing-prototypes" diff --git a/gold/configure.ac b/gold/configure.ac index 024ac48650..42cba7f8b4 100644 --- a/gold/configure.ac +++ b/gold/configure.ac @@ -16,6 +16,8 @@ AC_PROG_INSTALL ZW_GNU_GETTEXT_SISTER_DIR AM_PO_SUBDIRS +AC_C_BIGENDIAN + AC_EXEEXT AM_BINUTILS_WARNINGS diff --git a/gold/gold.cc b/gold/gold.cc index 6874e1b84e..c39f999585 100644 --- a/gold/gold.cc +++ b/gold/gold.cc @@ -56,19 +56,43 @@ gold_unreachable() abort(); } -} // End namespace gold. +// This class arranges to run the functions done in the middle of the +// link. It is just a closure. -namespace +class Middle_runner : public Task_function_runner { + public: + Middle_runner(const General_options& options, + const Input_objects* input_objects, + Symbol_table* symtab, + Layout* layout) + : options_(options), input_objects_(input_objects), symtab_(symtab), + layout_(layout) + { } -using namespace gold; + void + run(Workqueue*); + + private: + const General_options& options_; + const Input_objects* input_objects_; + Symbol_table* symtab_; + Layout* layout_; +}; + +void +Middle_runner::run(Workqueue* workqueue) +{ + queue_middle_tasks(this->options_, this->input_objects_, this->symtab_, + this->layout_, workqueue); +} // Queue up the initial set of tasks for this link job. void queue_initial_tasks(const General_options& options, const Dirsearch& search_path, - const Command_line::Input_argument_list& inputs, + const Input_argument_list& inputs, Workqueue* workqueue, Input_objects* input_objects, Symbol_table* symtab, Layout* layout) { @@ -80,7 +104,7 @@ queue_initial_tasks(const General_options& options, // each input file. We associate the blocker with the following // input file, to give us a convenient place to delete it. Task_token* this_blocker = NULL; - for (Command_line::Input_argument_list::const_iterator p = inputs.begin(); + for (Input_argument_list::const_iterator p = inputs.begin(); p != inputs.end(); ++p) { @@ -92,14 +116,65 @@ queue_initial_tasks(const General_options& options, this_blocker = next_blocker; } - workqueue->queue(new Layout_task(options, input_objects, symtab, layout, - this_blocker)); + workqueue->queue(new Task_function(new Middle_runner(options, + input_objects, + symtab, + layout), + this_blocker)); } -} // end anonymous namespace. +// Queue up the middle set of tasks. These are the tasks which run +// after all the input objects have been found and all the symbols +// have been read, but before we lay out the output file. -namespace gold +void +queue_middle_tasks(const General_options& options, + const Input_objects* input_objects, + Symbol_table* symtab, + Layout* layout, + Workqueue* workqueue) { + // Read the relocations of the input files. We do this to find + // which symbols are used by relocations which require a GOT and/or + // a PLT entry, or a COPY reloc. When we implement garbage + // collection we will do it here by reading the relocations in a + // breadth first search by references. + // + // We could also read the relocations during the first pass, and + // mark symbols at that time. That is how the old GNU linker works. + // Doing that is more complex, since we may later decide to discard + // some of the sections, and thus change our minds about the types + // of references made to the symbols. + Task_token* blocker = new Task_token(); + Task_token* symtab_lock = new Task_token(); + for (Input_objects::Object_list::const_iterator p = input_objects->begin(); + p != input_objects->end(); + ++p) + { + // We can read and process the relocations in any order. But we + // only want one task to write to the symbol table at a time. + // So we queue up a task for each object to read the + // relocations. That task will in turn queue a task to wait + // until it can write to the symbol table. + blocker->add_blocker(); + workqueue->queue(new Read_relocs(options, symtab, *p, symtab_lock, + blocker)); + } + + // Allocate common symbols. This requires write access to the + // symbol table, but is independent of the relocation processing. + // blocker->add_blocker(); + // workqueue->queue(new Allocate_commons_task(options, symtab, layout, + // symtab_lock, blocker)); + + // When all those tasks are complete, we can start laying out the + // output file. + workqueue->queue(new Task_function(new Layout_task_runner(options, + input_objects, + symtab, + layout), + blocker)); +} // Queue up the final set of tasks. This is called at the end of // Layout_task. @@ -122,8 +197,8 @@ queue_final_tasks(const General_options& options, ++p) { final_blocker->add_blocker(); - workqueue->queue(new Relocate_task(options, symtab, layout->sympool(), - *p, of, final_blocker)); + workqueue->queue(new Relocate_task(options, symtab, layout, *p, of, + final_blocker)); } // Queue a task to write out the symbol table. @@ -138,11 +213,14 @@ queue_final_tasks(const General_options& options, // Queue a task to close the output file. This will be blocked by // FINAL_BLOCKER. - workqueue->queue(new Close_task(of, final_blocker)); + workqueue->queue(new Task_function(new Close_task_runner(of), + final_blocker)); } } // End namespace gold. +using namespace gold; + int main(int argc, char** argv) { diff --git a/gold/gold.h b/gold/gold.h index 9ddf2f792a..1140d663a7 100644 --- a/gold/gold.h +++ b/gold/gold.h @@ -137,6 +137,8 @@ namespace gold { class General_options; +class Input_argument_list; +class Dirsearch; class Input_objects; class Symbol_table; class Layout; @@ -166,6 +168,25 @@ gold_nomem() ATTRIBUTE_NORETURN; extern void gold_unreachable() ATTRIBUTE_NORETURN; +// Queue up the first set of tasks. +extern void +queue_initial_tasks(const General_options&, + const Dirsearch&, + const Input_argument_list&, + Workqueue*, + Input_objects*, + Symbol_table*, + Layout*); + +// Queue up the middle set of tasks. +extern void +queue_middle_tasks(const General_options&, + const Input_objects*, + Symbol_table*, + Layout*, + Workqueue*); + +// Queue up the final set of tasks. extern void queue_final_tasks(const General_options&, const Input_objects*, diff --git a/gold/i386.cc b/gold/i386.cc index 32ee8814bb..468cb90efe 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -2,8 +2,11 @@ #include "gold.h" #include "elfcpp.h" +#include "reloc.h" #include "i386.h" #include "object.h" +#include "layout.h" +#include "output.h" #include "target.h" #include "target-reloc.h" #include "target-select.h" @@ -22,31 +25,92 @@ class Target_i386 : public Sized_target<32, false> : Sized_target<32, false>(&i386_info) { } + // Scan the relocations to look for symbol adjustments. void - relocate_section(const Symbol_table* symtab, - Sized_object<32, false>*, - unsigned int, - const unsigned char*, - size_t, - unsigned int, - const elfcpp::Elf_types<32>::Elf_Addr*, - Symbol**, - unsigned char*, - elfcpp::Elf_types<32>::Elf_Addr, - off_t); + scan_relocs(const General_options& options, + Symbol_table* symtab, + Sized_object<32, false>* object, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + size_t local_symbol_count, + const unsigned char* plocal_symbols, + Symbol** global_symbols); - // The class which implements relocation. - struct Relocate - { - inline void - operator()(Sized_object<32, false>*, const elfcpp::Rel<32, false>&, - unsigned int r_type, Sized_symbol<32>*, - elfcpp::Elf_types<32>::Elf_Addr, - unsigned char*, elfcpp::Elf_types<32>::Elf_Addr); - - }; + // Relocate a section. + void + relocate_section(const Relocate_info<32, false>*, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + unsigned char* view, + elfcpp::Elf_types<32>::Elf_Addr view_address, + off_t view_size); private: + // The class which scans relocations. + struct Scan + { + inline void + local(const General_options& options, 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, + const elfcpp::Rel<32, false>& reloc, unsigned int r_type, + Symbol* gsym); + }; + + // The class which implements relocation. + class Relocate + { + public: + // Do a relocation. + static inline void + relocate(const Relocate_info<32, false>*, size_t relnum, + const elfcpp::Rel<32, false>&, + unsigned int r_type, Sized_symbol<32>*, + elfcpp::Elf_types<32>::Elf_Addr, + unsigned char*, elfcpp::Elf_types<32>::Elf_Addr, + off_t); + + private: + // Do a TLS relocation. + static inline void + relocate_tls(const Relocate_info<32, false>*, size_t relnum, + const elfcpp::Rel<32, false>&, + unsigned int r_type, Sized_symbol<32>*, + elfcpp::Elf_types<32>::Elf_Addr, + unsigned char*, elfcpp::Elf_types<32>::Elf_Addr, off_t); + + // Do a TLS Initial-Exec to Local-Exec transition. + static inline void + tls_ie_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, + const elfcpp::Rel<32, false>&, off_t, off_t); + + // Check the validity of a TLS relocation. This is like assert. + static inline void + check_tls(const Relocate_info<32, false>*, size_t relnum, + const elfcpp::Rel<32, false>&, bool); + }; + + // Adjust TLS relocation type based on the options and whether this + // is a local symbol. + static unsigned int + optimize_tls_reloc(const General_options*, bool is_local, int r_type); + + // Information about this specific target which we pass to the + // general Target structure. static const Target::Target_info i386_info; }; @@ -62,59 +126,271 @@ const Target::Target_info Target_i386::i386_info = 0x1000 // common_pagesize }; -// Perform a relocation. +// 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. + +unsigned int +Target_i386::optimize_tls_reloc(const General_options* options, bool is_local, + int r_type) +{ + // If we are generating a shared library, then we can't do anything + // in the linker. + if (options->is_shared()) + return r_type; + + switch (r_type) + { + case elfcpp::R_386_TLS_GD: + case elfcpp::R_386_TLS_GOTDESC: + case elfcpp::R_386_TLS_DESC_CALL: + // These are Global-Dynamic which permits fully general TLS + // access. Since we know that we are generating an executable, + // we can convert this to Initial-Exec. If we also know that + // this is a local symbol, we can further switch to Local-Exec. + if (is_local) + return elfcpp::R_386_TLS_LE_32; + return elfcpp::R_386_TLS_IE_32; + + case elfcpp::R_386_TLS_LDM: + // This is Local-Dynamic, which refers to a local symbol in the + // dynamic TLS block. Since we know that we generating an + // executable, we can switch to Local-Exec. + return elfcpp::R_386_TLS_LE_32; + + case elfcpp::R_386_TLS_LDO_32: + // Another type of Local-Dynamic relocation. + return elfcpp::R_386_TLS_LE; + + case elfcpp::R_386_TLS_IE: + case elfcpp::R_386_TLS_GOTIE: + case elfcpp::R_386_TLS_IE_32: + // These are Initial-Exec relocs which get the thread offset + // from the GOT. If we know that we are linking against the + // local symbol, we can switch to Local-Exec, which links the + // thread offset into the instruction. + if (is_local) + return elfcpp::R_386_TLS_LE_32; + return r_type; + + case elfcpp::R_386_TLS_LE: + case elfcpp::R_386_TLS_LE_32: + // When we already have Local-Exec, there is nothing further we + // can do. + return r_type; + + default: + abort(); + } +} + +// Scan a relocation for a local symbol. inline void -Target_i386::Relocate::operator()(Sized_object<32, false>* object, - const elfcpp::Rel<32, false>&, - unsigned int r_type, - Sized_symbol<32>*, - elfcpp::Elf_types<32>::Elf_Addr value, - unsigned char* view, - elfcpp::Elf_types<32>::Elf_Addr address) +Target_i386::Scan::local(const General_options& options, + Sized_object<32, false>* object, + const elfcpp::Rel<32, false>&, unsigned int r_type, + const elfcpp::Sym<32, false>&) { switch (r_type) { case elfcpp::R_386_NONE: + case elfcpp::R_386_GNU_VTINHERIT: + case elfcpp::R_386_GNU_VTENTRY: break; case elfcpp::R_386_32: - { - elfcpp::Elf_Word* wv = reinterpret_cast(view); - unsigned int x = elfcpp::read_elf_word(wv); - elfcpp::write_elf_word(wv, x + value); - } + case elfcpp::R_386_16: + case elfcpp::R_386_8: + // FIXME: If we are generating a shared object we need to copy + // this relocation into the object. break; case elfcpp::R_386_PC32: - { - elfcpp::Elf_Word* wv = reinterpret_cast(view); - unsigned int x = elfcpp::read_elf_word(wv); - elfcpp::write_elf_word(wv, x + value - address); - } + case elfcpp::R_386_PC16: + case elfcpp::R_386_PC8: break; - default: - fprintf(stderr, _("%s: %s: unsupported reloc %u\n"), + case elfcpp::R_386_COPY: + case elfcpp::R_386_GLOB_DAT: + case elfcpp::R_386_JUMP_SLOT: + case elfcpp::R_386_RELATIVE: + case elfcpp::R_386_TLS_TPOFF: + case elfcpp::R_386_TLS_DTPMOD32: + case elfcpp::R_386_TLS_DTPOFF32: + case elfcpp::R_386_TLS_TPOFF32: + case elfcpp::R_386_TLS_DESC: + fprintf(stderr, _("%s: %s: unexpected reloc %u in object file\n"), program_name, object->name().c_str(), r_type); - // gold_exit(false); + gold_exit(false); + break; + + case elfcpp::R_386_TLS_IE: + case elfcpp::R_386_TLS_GOTIE: + case elfcpp::R_386_TLS_LE: + case elfcpp::R_386_TLS_GD: + case elfcpp::R_386_TLS_LDM: + case elfcpp::R_386_TLS_LDO_32: + case elfcpp::R_386_TLS_IE_32: + case elfcpp::R_386_TLS_LE_32: + case elfcpp::R_386_TLS_GOTDESC: + case elfcpp::R_386_TLS_DESC_CALL: + r_type = Target_i386::optimize_tls_reloc(&options, true, r_type); + switch (r_type) + { + case elfcpp::R_386_TLS_LE: + case elfcpp::R_386_TLS_LE_32: + // FIXME: If generating a shared object, we need to copy + // this relocation into the object. + break; + + case elfcpp::R_386_TLS_IE: + case elfcpp::R_386_TLS_GOTIE: + case elfcpp::R_386_TLS_GD: + case elfcpp::R_386_TLS_LDM: + case elfcpp::R_386_TLS_LDO_32: + case elfcpp::R_386_TLS_IE_32: + case elfcpp::R_386_TLS_GOTDESC: + case elfcpp::R_386_TLS_DESC_CALL: + fprintf(stderr, + _("%s: %s: unsupported reloc %u against local symbol\n"), + program_name, object->name().c_str(), r_type); + break; + } + 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: + case elfcpp::R_386_TLS_GD_CALL: + case elfcpp::R_386_TLS_GD_POP: + case elfcpp::R_386_TLS_LDM_32: + case elfcpp::R_386_TLS_LDM_PUSH: + case elfcpp::R_386_TLS_LDM_CALL: + case elfcpp::R_386_TLS_LDM_POP: + case elfcpp::R_386_USED_BY_INTEL_200: + default: + fprintf(stderr, _("%s: %s: unsupported reloc %u against local symbol\n"), + program_name, object->name().c_str(), r_type); + break; } } -// Relocate section data. +// Scan a relocation for a global symbol. + +inline void +Target_i386::Scan::global(const General_options& options, + Sized_object<32, false>* object, + const elfcpp::Rel<32, false>&, unsigned int r_type, + Symbol* gsym) +{ + switch (r_type) + { + case elfcpp::R_386_NONE: + case elfcpp::R_386_GNU_VTINHERIT: + case elfcpp::R_386_GNU_VTENTRY: + break; + + case elfcpp::R_386_32: + case elfcpp::R_386_PC32: + case elfcpp::R_386_16: + case elfcpp::R_386_PC16: + case elfcpp::R_386_8: + case elfcpp::R_386_PC8: + // FIXME: If we are generating a shared object we may need to + // copy this relocation into the object. If this symbol is + // defined in a shared object, we may need to copy this + // relocation in order to avoid a COPY relocation. + break; + + case elfcpp::R_386_COPY: + case elfcpp::R_386_GLOB_DAT: + case elfcpp::R_386_JUMP_SLOT: + case elfcpp::R_386_RELATIVE: + case elfcpp::R_386_TLS_TPOFF: + case elfcpp::R_386_TLS_DTPMOD32: + case elfcpp::R_386_TLS_DTPOFF32: + case elfcpp::R_386_TLS_TPOFF32: + case elfcpp::R_386_TLS_DESC: + fprintf(stderr, _("%s: %s: unexpected reloc %u in object file\n"), + program_name, object->name().c_str(), r_type); + gold_exit(false); + break; + + case elfcpp::R_386_TLS_IE: + case elfcpp::R_386_TLS_GOTIE: + case elfcpp::R_386_TLS_LE: + case elfcpp::R_386_TLS_GD: + case elfcpp::R_386_TLS_LDM: + case elfcpp::R_386_TLS_LDO_32: + case elfcpp::R_386_TLS_IE_32: + case elfcpp::R_386_TLS_LE_32: + case elfcpp::R_386_TLS_GOTDESC: + case elfcpp::R_386_TLS_DESC_CALL: + r_type = Target_i386::optimize_tls_reloc(&options, + !gsym->in_dynsym(), + r_type); + switch (r_type) + { + case elfcpp::R_386_TLS_LE: + case elfcpp::R_386_TLS_LE_32: + // FIXME: If generating a shared object, we need to copy + // this relocation into the object. + break; + + case elfcpp::R_386_TLS_IE: + case elfcpp::R_386_TLS_GOTIE: + case elfcpp::R_386_TLS_GD: + case elfcpp::R_386_TLS_LDM: + case elfcpp::R_386_TLS_LDO_32: + case elfcpp::R_386_TLS_IE_32: + case elfcpp::R_386_TLS_GOTDESC: + case elfcpp::R_386_TLS_DESC_CALL: + fprintf(stderr, + _("%s: %s: unsupported reloc %u against global symbol %s\n"), + program_name, object->name().c_str(), r_type, gsym->name()); + break; + } + 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: + case elfcpp::R_386_TLS_GD_CALL: + case elfcpp::R_386_TLS_GD_POP: + case elfcpp::R_386_TLS_LDM_32: + case elfcpp::R_386_TLS_LDM_PUSH: + case elfcpp::R_386_TLS_LDM_CALL: + case elfcpp::R_386_TLS_LDM_POP: + case elfcpp::R_386_USED_BY_INTEL_200: + default: + fprintf(stderr, + _("%s: %s: unsupported reloc %u against global symbol %s\n"), + program_name, object->name().c_str(), r_type, gsym->name()); + break; + } +} + +// Scan relocations for a section. void -Target_i386::relocate_section(const Symbol_table* symtab, - Sized_object<32, false>* object, - unsigned int sh_type, - const unsigned char* prelocs, - size_t reloc_count, - unsigned int local_count, - const elfcpp::Elf_types<32>::Elf_Addr* values, - Symbol** global_syms, - unsigned char* view, - elfcpp::Elf_types<32>::Elf_Addr address, - off_t view_size) +Target_i386::scan_relocs(const General_options& options, + Symbol_table* symtab, + Sized_object<32, false>* object, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + size_t local_symbol_count, + const unsigned char* plocal_symbols, + Symbol** global_symbols) { if (sh_type == elfcpp::SHT_RELA) { @@ -123,14 +399,335 @@ Target_i386::relocate_section(const Symbol_table* symtab, gold_exit(false); } - gold::relocate_section<32, false, elfcpp::SHT_REL, Target_i386::Relocate>( + gold::scan_relocs<32, false, elfcpp::SHT_REL, Target_i386::Scan>( + options, symtab, object, prelocs, reloc_count, - local_count, - values, - global_syms, + local_symbol_count, + plocal_symbols, + global_symbols); +} + +// Perform a relocation. + +inline void +Target_i386::Relocate::relocate(const Relocate_info<32, false>* relinfo, + size_t relnum, + const elfcpp::Rel<32, false>& rel, + unsigned int r_type, + Sized_symbol<32>* gsym, + elfcpp::Elf_types<32>::Elf_Addr value, + unsigned char* view, + elfcpp::Elf_types<32>::Elf_Addr address, + off_t view_size) +{ + switch (r_type) + { + case elfcpp::R_386_NONE: + case elfcpp::R_386_GNU_VTINHERIT: + case elfcpp::R_386_GNU_VTENTRY: + break; + + case elfcpp::R_386_32: + Relocate_functions<32, false>::rel32(view, value); + break; + + case elfcpp::R_386_PC32: + Relocate_functions<32, false>::pcrel32(view, value, address); + break; + + case elfcpp::R_386_16: + Relocate_functions<32, false>::rel16(view, value); + break; + + case elfcpp::R_386_PC16: + Relocate_functions<32, false>::pcrel16(view, value, address); + break; + + case elfcpp::R_386_8: + Relocate_functions<32, false>::rel8(view, value); + break; + + case elfcpp::R_386_PC8: + Relocate_functions<32, false>::pcrel8(view, value, address); + break; + + case elfcpp::R_386_COPY: + case elfcpp::R_386_GLOB_DAT: + case elfcpp::R_386_JUMP_SLOT: + case elfcpp::R_386_RELATIVE: + case elfcpp::R_386_TLS_TPOFF: + case elfcpp::R_386_TLS_DTPMOD32: + case elfcpp::R_386_TLS_DTPOFF32: + case elfcpp::R_386_TLS_TPOFF32: + case elfcpp::R_386_TLS_DESC: + fprintf(stderr, _("%s: %s: unexpected reloc %u in object file\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str(), + r_type); + gold_exit(false); + break; + + case elfcpp::R_386_TLS_IE: + case elfcpp::R_386_TLS_GOTIE: + case elfcpp::R_386_TLS_LE: + case elfcpp::R_386_TLS_GD: + case elfcpp::R_386_TLS_LDM: + case elfcpp::R_386_TLS_LDO_32: + case elfcpp::R_386_TLS_IE_32: + 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); + 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: + case elfcpp::R_386_TLS_GD_CALL: + case elfcpp::R_386_TLS_GD_POP: + case elfcpp::R_386_TLS_LDM_32: + case elfcpp::R_386_TLS_LDM_PUSH: + case elfcpp::R_386_TLS_LDM_CALL: + case elfcpp::R_386_TLS_LDM_POP: + case elfcpp::R_386_USED_BY_INTEL_200: + default: + 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; + } +} + +// Perform a TLS relocation. + +inline void +Target_i386::Relocate::relocate_tls(const Relocate_info<32, false>* relinfo, + size_t relnum, + const elfcpp::Rel<32, false>& rel, + unsigned int r_type, + Sized_symbol<32>* gsym, + elfcpp::Elf_types<32>::Elf_Addr value, + unsigned char* view, + elfcpp::Elf_types<32>::Elf_Addr, + off_t view_size) +{ + Output_segment* tls_segment = relinfo->layout->tls_segment(); + if (tls_segment == NULL) + { + fprintf(stderr, _("%s: %s: TLS reloc but no TLS segment\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str()); + gold_exit(false); + } + + const bool is_local = gsym == NULL || !gsym->in_dynsym(); + const unsigned int opt_r_type = + Target_i386::optimize_tls_reloc(relinfo->options, is_local, r_type); + switch (r_type) + { + case elfcpp::R_386_TLS_LE_32: + value = tls_segment->vaddr() + tls_segment->memsz() - value; + Relocate_functions<32, false>::rel32(view, value); + break; + + case elfcpp::R_386_TLS_LE: + value = value - (tls_segment->vaddr() + tls_segment->memsz()); + Relocate_functions<32, false>::rel32(view, value); + break; + + case elfcpp::R_386_TLS_IE: + case elfcpp::R_386_TLS_GOTIE: + case elfcpp::R_386_TLS_IE_32: + if (opt_r_type == elfcpp::R_386_TLS_LE_32) + { + Target_i386::Relocate::tls_ie_to_le(relinfo, relnum, tls_segment, + rel, r_type, value, view, + view_size); + break; + } + fprintf(stderr, _("%s: %s: unsupported reloc type %u\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str(), + r_type); + // gold_exit(false); + break; + + case elfcpp::R_386_TLS_GD: + case elfcpp::R_386_TLS_LDM: + case elfcpp::R_386_TLS_LDO_32: + case elfcpp::R_386_TLS_GOTDESC: + case elfcpp::R_386_TLS_DESC_CALL: + 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; + } +} + +// Do a relocation in which we convert a TLS Initial-Exec to a +// Local-Exec. + +inline void +Target_i386::Relocate::tls_ie_to_le(const Relocate_info<32, false>* relinfo, + size_t relnum, + Output_segment* tls_segment, + const elfcpp::Rel<32, false>& rel, + unsigned int r_type, + elfcpp::Elf_types<32>::Elf_Addr value, + unsigned char* view, + off_t view_size) +{ + // We have to actually change the instructions, which means that we + // need to examine the opcodes to figure out which instruction we + // are looking at. + if (r_type == elfcpp::R_386_TLS_IE) + { + // movl %gs:XX,%eax ==> movl $YY,%eax + // movl %gs:XX,%reg ==> movl $YY,%reg + // addl %gs:XX,%reg ==> addl $YY,%reg + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -1); + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, 4); + + unsigned char op1 = view[-1]; + if (op1 == 0xa1) + { + // movl XX,%eax ==> movl $YY,%eax + view[-1] = 0xb8; + } + else + { + Target_i386::Relocate::check_range(relinfo, relnum, rel, + view_size, -2); + + unsigned char op2 = view[-2]; + if (op2 == 0x8b) + { + // movl XX,%reg ==> movl $YY,%reg + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + (op1 & 0xc7) == 0x05); + view[-2] = 0xc7; + view[-1] = 0xc0 | ((op1 >> 3) & 7); + } + else if (op2 == 0x03) + { + // addl XX,%reg ==> addl $YY,%reg + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + (op1 & 0xc7) == 0x05); + view[-2] = 0x81; + view[-1] = 0xc0 | ((op1 >> 3) & 7); + } + else + Target_i386::Relocate::check_tls(relinfo, relnum, rel, 0); + } + } + else + { + // subl %gs:XX(%reg1),%reg2 ==> subl $YY,%reg2 + // movl %gs:XX(%reg1),%reg2 ==> movl $YY,%reg2 + // addl %gs:XX(%reg1),%reg2 ==> addl $YY,$reg2 + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, -2); + Target_i386::Relocate::check_range(relinfo, relnum, rel, view_size, 4); + + unsigned char op1 = view[-1]; + unsigned char op2 = view[-2]; + Target_i386::Relocate::check_tls(relinfo, relnum, rel, + (op1 & 0xc0) == 0x80 && (op1 & 7) != 4); + if (op2 == 0x8b) + { + // movl %gs:XX(%reg1),%reg2 ==> movl $YY,%reg2 + view[-2] = 0xc7; + view[-1] = 0xc0 | ((op1 >> 3) & 7); + } + else if (op2 == 0x2b) + { + // subl %gs:XX(%reg1),%reg2 ==> subl $YY,%reg2 + view[-2] = 0x81; + view[-1] = 0xe8 | ((op1 >> 3) & 7); + } + else if (op2 == 0x03) + { + // addl %gs:XX(%reg1),%reg2 ==> addl $YY,$reg2 + view[-2] = 0x81; + view[-1] = 0xc0 | ((op1 >> 3) & 7); + } + else + 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()); + + Relocate_functions<32, false>::rel32(view, value); +} + +// Check the range for a TLS relocation. + +inline void +Target_i386::Relocate::check_range(const Relocate_info<32, false>* relinfo, + size_t relnum, + const elfcpp::Rel<32, false>& rel, + off_t view_size, off_t off) +{ + off_t offset = rel.get_r_offset() + off; + if (offset < 0 || offset > view_size) + { + fprintf(stderr, _("%s: %s: TLS relocation out of range\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str()); + gold_exit(false); + } +} + +// Check the validity of a TLS relocation. This is like assert. + +inline void +Target_i386::Relocate::check_tls(const Relocate_info<32, false>* relinfo, + size_t relnum, + const elfcpp::Rel<32, false>& rel, + bool valid) +{ + if (!valid) + { + fprintf(stderr, + _("%s: %s: TLS relocation against invalid instruction\n"), + program_name, + relinfo->location(relnum, rel.get_r_offset()).c_str()); + gold_exit(false); + } +} + +// Relocate section data. + +void +Target_i386::relocate_section(const Relocate_info<32, false>* relinfo, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + unsigned char* view, + elfcpp::Elf_types<32>::Elf_Addr address, + off_t view_size) +{ + assert(sh_type == elfcpp::SHT_REL); + + gold::relocate_section<32, false, elfcpp::SHT_REL, Target_i386::Relocate>( + relinfo, + prelocs, + reloc_count, view, address, view_size); diff --git a/gold/layout.cc b/gold/layout.cc index 61b6895682..a81fd4393f 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -14,36 +14,13 @@ namespace gold { -// Layout_task methods. - -Layout_task::~Layout_task() -{ -} - -// This task can be run when it is unblocked. - -Task::Is_runnable_type -Layout_task::is_runnable(Workqueue*) -{ - if (this->this_blocker_->is_blocked()) - return IS_BLOCKED; - return IS_RUNNABLE; -} - -// We don't need to hold any locks for the duration of this task. In -// fact this task will be the only one running. - -Task_locker* -Layout_task::locks(Workqueue*) -{ - return NULL; -} +// Layout_task_runner methods. // Lay out the sections. This is called after all the input objects // have been read. void -Layout_task::run(Workqueue* workqueue) +Layout_task_runner::run(Workqueue* workqueue) { off_t file_size = this->layout_->finalize(this->input_objects_, this->symtab_); @@ -63,7 +40,7 @@ Layout_task::run(Workqueue* workqueue) Layout::Layout(const General_options& options) : options_(options), last_shndx_(0), namepool_(), sympool_(), signatures_(), section_name_map_(), segment_list_(), section_list_(), - special_output_list_() + special_output_list_(), tls_segment_(NULL) { // Make space for more than enough segments for a typical file. // This is just for efficiency--it's OK if we wind up needing more. @@ -256,30 +233,16 @@ Layout::make_output_section(const char* name, elfcpp::Elf_Word type, } // If we see a loadable SHF_TLS section, we create a PT_TLS - // segment. + // segment. There can only be one such segment. if ((flags & elfcpp::SHF_TLS) != 0) { - // See if we already have an equivalent PT_TLS segment. - for (p = this->segment_list_.begin(); - p != segment_list_.end(); - ++p) + if (this->tls_segment_ == NULL) { - if ((*p)->type() == elfcpp::PT_TLS - && (((*p)->flags() & elfcpp::PF_W) - == (seg_flags & elfcpp::PF_W))) - { - (*p)->add_output_section(os, seg_flags); - break; - } - } - - if (p == this->segment_list_.end()) - { - Output_segment* oseg = new Output_segment(elfcpp::PT_TLS, - seg_flags); - this->segment_list_.push_back(oseg); - oseg->add_output_section(os, seg_flags); + this->tls_segment_ = new Output_segment(elfcpp::PT_TLS, + seg_flags); + this->segment_list_.push_back(this->tls_segment_); } + this->tls_segment_->add_output_section(os, seg_flags); } } @@ -440,6 +403,14 @@ Layout::segment_precedes(const Output_segment* seg1, if (type2 == elfcpp::PT_LOAD && type1 != elfcpp::PT_LOAD) return false; + // We put the PT_TLS segment last, because that is where the dynamic + // linker expects to find it (this is just for efficiency; other + // positions would also work correctly). + if (type1 == elfcpp::PT_TLS && type2 != elfcpp::PT_TLS) + return false; + if (type2 == elfcpp::PT_TLS && type1 != elfcpp::PT_TLS) + return true; + const elfcpp::Elf_Word flags1 = seg1->flags(); const elfcpp::Elf_Word flags2 = seg2->flags(); @@ -865,30 +836,12 @@ Write_symbols_task::run(Workqueue*) this->symtab_->write_globals(this->target_, this->sympool_, this->of_); } -// Close_task methods. - -// We can't run until FINAL_BLOCKER is unblocked. - -Task::Is_runnable_type -Close_task::is_runnable(Workqueue*) -{ - if (this->final_blocker_->is_blocked()) - return IS_BLOCKED; - return IS_RUNNABLE; -} - -// We don't lock anything. - -Task_locker* -Close_task::locks(Workqueue*) -{ - return NULL; -} +// Close_task_runner methods. // Run the task--close the file. void -Close_task::run(Workqueue*) +Close_task_runner::run(Workqueue*) { this->of_->close(); } diff --git a/gold/layout.h b/gold/layout.h index 028d714e6a..4ec2a4ad5a 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -25,46 +25,35 @@ class Output_segment; class Output_data; class Target; -// This Task handles mapping the input sections to output sections and -// laying them out in memory. +// This task function handles mapping the input sections to output +// sections and laying them out in memory. -class Layout_task : public Task +class Layout_task_runner : public Task_function_runner { public: // OPTIONS is the command line options, INPUT_OBJECTS is the list of - // input objects, THIS_BLOCKER is a token which blocks this task - // from executing until all the input symbols have been read. - Layout_task(const General_options& options, - const Input_objects* input_objects, - Symbol_table* symtab, - Layout* layout, - Task_token* this_blocker) + // input objects, SYMTAB is the symbol table, LAYOUT is the layout + // object. + Layout_task_runner(const General_options& options, + const Input_objects* input_objects, + Symbol_table* symtab, + Layout* layout) : options_(options), input_objects_(input_objects), symtab_(symtab), - layout_(layout), this_blocker_(this_blocker) + layout_(layout) { } - ~Layout_task(); - - // The standard Task methods. - - Is_runnable_type - is_runnable(Workqueue*); - - Task_locker* - locks(Workqueue*); - + // Run the operation. void run(Workqueue*); private: - Layout_task(const Layout_task&); - Layout_task& operator=(const Layout_task&); + Layout_task_runner(const Layout_task_runner&); + Layout_task_runner& operator=(const Layout_task_runner&); const General_options& options_; const Input_objects* input_objects_; Symbol_table* symtab_; Layout* layout_; - Task_token* this_blocker_; }; // This class handles the details of laying out input sections. @@ -105,6 +94,11 @@ class Layout off_t finalize(const Input_objects*, Symbol_table*); + // Return the TLS segment. + Output_segment* + tls_segment() const + { return this->tls_segment_; } + // Write out data not associated with an input file or the symbol // table. void @@ -234,6 +228,8 @@ class Layout // The list of sections which require special output because they // are not comprised of input sections. Data_list special_output_list_; + // A pointer to the PT_TLS segment if there is one. + Output_segment* tls_segment_; }; // This task handles writing out data which is not part of a section @@ -295,29 +291,21 @@ class Write_symbols_task : public Task Task_token* final_blocker_; }; -// This task handles closing the file. +// This task function handles closing the file. -class Close_task : public Task +class Close_task_runner : public Task_function_runner { public: - Close_task(Output_file* of, Task_token* final_blocker) - : of_(of), final_blocker_(final_blocker) + Close_task_runner(Output_file* of) + : of_(of) { } - // The standard task methods. - - Is_runnable_type - is_runnable(Workqueue*); - - Task_locker* - locks(Workqueue*); - + // Run the operation. void run(Workqueue*); private: Output_file* of_; - Task_token* final_blocker_; }; } // End namespace gold. diff --git a/gold/object.cc b/gold/object.cc index 8f1241ad87..5efb3cbd7f 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -87,6 +87,33 @@ Sized_object::section_header(unsigned int shnum) return this->get_view(symtabshdroff, This::shdr_size); } +// Return the name of section SHNUM. + +template +std::string +Sized_object::do_section_name(unsigned int shnum) +{ + Task_lock_obj tl(*this); + + // Read the section names. + typename This::Shdr shdrnames(this->section_header(this->shstrndx_)); + const unsigned char* pnamesu = this->get_view(shdrnames.get_sh_offset(), + shdrnames.get_sh_size()); + const char* pnames = reinterpret_cast(pnamesu); + + typename This::Shdr shdr(this->section_header(shnum)); + if (shdr.get_sh_name() >= shdrnames.get_sh_size()) + { + fprintf(stderr, + _("%s: %s: bad section name offset for section %u: %lu\n"), + program_name, this->name().c_str(), shnum, + static_cast(shdr.get_sh_name())); + gold_exit(false); + } + + return std::string(pnames + shdr.get_sh_name()); +} + // Set up an object file bsaed on the file header. This sets up the // target and reads the section information. @@ -182,7 +209,9 @@ Sized_object::do_read_symbols(Read_symbols_data* sd) // We only need the external symbols. const int sym_size = This::sym_size; - off_t locsize = symtabshdr.get_sh_info() * sym_size; + const unsigned int loccount = symtabshdr.get_sh_info(); + this->local_symbol_count_ = loccount; + off_t locsize = loccount * sym_size; off_t extoff = symtabshdr.get_sh_offset() + locsize; off_t extsize = symtabshdr.get_sh_size() - locsize; @@ -190,14 +219,15 @@ Sized_object::do_read_symbols(Read_symbols_data* sd) File_view* fvsymtab = this->get_lasting_view(extoff, extsize); // Read the section header for the symbol names. + unsigned int shnum = this->shnum(); unsigned int strtab_shnum = symtabshdr.get_sh_link(); - if (strtab_shnum == 0 || strtab_shnum >= this->shnum()) + if (strtab_shnum == 0 || strtab_shnum >= shnum) { fprintf(stderr, _("%s: %s: invalid symbol table name index: %u\n"), program_name, this->name().c_str(), strtab_shnum); gold_exit(false); } - typename This::Shdr strtabshdr(this->section_header(strtab_shnum)); + typename This::Shdr strtabshdr(pshdrs + strtab_shnum * This::shdr_size); if (strtabshdr.get_sh_type() != elfcpp::SHT_STRTAB) { fprintf(stderr, @@ -493,14 +523,13 @@ Sized_object::do_finalize_local_symbols(off_t off, assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB); // Read the local symbols. - unsigned int loccount = symtabshdr.get_sh_info(); const int sym_size = This::sym_size; + const unsigned int loccount = this->local_symbol_count_; + assert(loccount == symtabshdr.get_sh_info()); off_t locsize = loccount * sym_size; const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(), locsize); - this->local_symbol_count_ = loccount; - this->values_ = new typename elfcpp::Elf_types::Elf_Addr[loccount]; // Read the section header for the symbol names. @@ -587,12 +616,12 @@ Sized_object::write_local_symbols(Output_file* of, // Read the symbol table section header. typename This::Shdr symtabshdr(this->section_header(this->symtab_shnum_)); assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB); - unsigned int local_symbol_count = this->local_symbol_count_; - assert(local_symbol_count == symtabshdr.get_sh_info()); + const unsigned int loccount = this->local_symbol_count_; + assert(loccount == symtabshdr.get_sh_info()); // Read the local symbols. const int sym_size = This::sym_size; - off_t locsize = local_symbol_count * sym_size; + off_t locsize = loccount * sym_size; const unsigned char* psyms = this->get_view(symtabshdr.get_sh_offset(), locsize); @@ -615,7 +644,7 @@ Sized_object::write_local_symbols(Output_file* of, psyms += sym_size; unsigned char* ov = oview; - for (unsigned int i = 1; i < local_symbol_count; ++i, psyms += sym_size) + for (unsigned int i = 1; i < loccount; ++i, psyms += sym_size) { elfcpp::Sym isym(psyms); elfcpp::Sym_write osym(ov); @@ -665,6 +694,31 @@ Input_objects::add_object(Object* obj) this->any_dynamic_ = true; } +// Relocate_info methods. + +// Return a string describing the location of a relocation. This is +// only used in error messages. + +template +std::string +Relocate_info::location(size_t relnum, off_t) const +{ + std::string ret(this->object->name()); + ret += ": reloc "; + char buf[100]; + snprintf(buf, sizeof buf, "%zu", relnum); + ret += buf; + ret += " in reloc section "; + snprintf(buf, sizeof buf, "%u", this->reloc_shndx); + ret += buf; + ret += " (" + this->object->section_name(this->reloc_shndx); + ret += ") for section "; + snprintf(buf, sizeof buf, "%u", this->data_shndx); + ret += buf; + ret += " (" + this->object->section_name(this->data_shndx) + ")"; + return ret; +} + } // End namespace gold. namespace @@ -830,4 +884,16 @@ class Sized_object<64, false>; template class Sized_object<64, true>; +template +struct Relocate_info<32, false>; + +template +struct Relocate_info<32, true>; + +template +struct Relocate_info<64, false>; + +template +struct Relocate_info<64, true>; + } // End namespace gold. diff --git a/gold/object.h b/gold/object.h index ca227c5d88..49a1ce9b86 100644 --- a/gold/object.h +++ b/gold/object.h @@ -4,6 +4,8 @@ #define GOLD_OBJECT_H #include +#include +#include #include #include "elfcpp.h" @@ -14,6 +16,7 @@ namespace gold { +class General_options; class Stringpool; class Layout; class Output_section; @@ -39,6 +42,35 @@ struct Read_symbols_data off_t symbol_names_size; }; +// Data about a single relocation section. This is read in +// read_relocs and processed in scan_relocs. + +struct Section_relocs +{ + // Index of reloc section. + unsigned int reloc_shndx; + // Index of section that relocs apply to. + unsigned int data_shndx; + // Contents of reloc section. + File_view* contents; + // Reloc section type. + unsigned int sh_type; + // Number of reloc entries. + size_t reloc_count; +}; + +// Relocations in an object file. This is read in read_relocs and +// processed in scan_relocs. + +struct Read_relocs_data +{ + typedef std::vector Relocs_list; + // The relocations. + Relocs_list relocs; + // The local symbols. + File_view* local_symbols; +}; + // Object is an interface which represents either a 32-bit or a 64-bit // input object. This can be a regular object file (ET_REL) or a // shared object (ET_DYN). The actual instantiations are @@ -98,22 +130,33 @@ class Object Sized_target* sized_target(ACCEPT_SIZE_ENDIAN_ONLY); - // Read the symbol and relocation information. + // Read the symbol information. void read_symbols(Read_symbols_data* sd) { return this->do_read_symbols(sd); } - // Add symbol information to the global symbol table. - void - add_symbols(Symbol_table* symtab, Read_symbols_data* sd) - { this->do_add_symbols(symtab, sd); } - // Pass sections which should be included in the link to the Layout // object, and record where the sections go in the output file. void layout(Layout* lay, Read_symbols_data* sd) { this->do_layout(lay, sd); } + // Add symbol information to the global symbol table. + void + add_symbols(Symbol_table* symtab, Read_symbols_data* sd) + { this->do_add_symbols(symtab, sd); } + + // Read the relocs. + void + read_relocs(Read_relocs_data* rd) + { return this->do_read_relocs(rd); } + + // Scan the relocs and adjust the symbol table. + void + scan_relocs(const General_options& options, Symbol_table* symtab, + Read_relocs_data* rd) + { return this->do_scan_relocs(options, symtab, rd); } + // Initial local symbol processing: set the offset where local // symbol information will be stored; add local symbol names to // *POOL; return the offset following the local symbols. @@ -124,8 +167,8 @@ class Object // Relocate the input sections and write out the local symbols. void relocate(const General_options& options, const Symbol_table* symtab, - const Stringpool* sympool, Output_file* of) - { return this->do_relocate(options, symtab, sympool, of); } + const Layout* layout, Output_file* of) + { return this->do_relocate(options, symtab, layout, of); } // Return whether an input section is being included in the link. bool @@ -141,6 +184,12 @@ class Object inline Output_section* output_section(unsigned int shnum, off_t* poff); + // Return the name of a section given a section index. This is only + // used for error messages. + std::string + section_name(unsigned int shnum) + { return this->do_section_name(shnum); } + protected: // What we need to know to map an input section to an output // section. We keep an array of these, one for each input section, @@ -163,6 +212,14 @@ class Object virtual void do_add_symbols(Symbol_table*, Read_symbols_data*) = 0; + // Read the relocs--implemented by child class. + virtual void + do_read_relocs(Read_relocs_data*) = 0; + + // Scan the relocs--implemented by child class. + virtual void + do_scan_relocs(const General_options&, Symbol_table*, Read_relocs_data*) = 0; + // Lay out sections--implemented by child class. virtual void do_layout(Layout*, Read_symbols_data*) = 0; @@ -175,7 +232,11 @@ class Object // symbols--implemented by child class. virtual void do_relocate(const General_options& options, const Symbol_table* symtab, - const Stringpool*, Output_file* of) = 0; + const Layout*, Output_file* of) = 0; + + // Get the name of a section--implemented by child class. + virtual std::string + do_section_name(unsigned int shnum) = 0; // Get the file. Input_file* @@ -282,14 +343,22 @@ class Sized_object : public Object void do_read_symbols(Read_symbols_data*); - // Lay out the input sections. - void - do_layout(Layout*, Read_symbols_data*); - // Add the symbols to the symbol table. void do_add_symbols(Symbol_table*, Read_symbols_data*); + // Read the relocs. + void + do_read_relocs(Read_relocs_data*); + + // Scan the relocs and adjust the symbol table. + void + do_scan_relocs(const General_options&, Symbol_table*, Read_relocs_data*); + + // Lay out the input sections. + void + do_layout(Layout*, Read_symbols_data*); + // Finalize the local symbols. off_t do_finalize_local_symbols(off_t, Stringpool*); @@ -297,7 +366,11 @@ class Sized_object : public Object // Relocate the input sections and write out the local symbols. void do_relocate(const General_options& options, const Symbol_table* symtab, - const Stringpool*, Output_file* of); + const Layout*, Output_file* of); + + // Get the name of a section. + std::string + do_section_name(unsigned int shnum); // Return the appropriate Sized_target structure. Sized_target* @@ -352,7 +425,8 @@ class Sized_object : public Object // Relocate the sections in the output file. void - relocate_sections(const Symbol_table*, const unsigned char* pshdrs, Views*); + relocate_sections(const General_options& options, const Symbol_table*, + const Layout*, const unsigned char* pshdrs, Views*); // Write out the local symbols. void @@ -424,6 +498,37 @@ class Input_objects bool any_dynamic_; }; +// Some of the information we pass to the relocation routines. We +// group this together to avoid passing a dozen different arguments. + +template +struct Relocate_info +{ + // Command line options. + const General_options* options; + // Symbol table. + const Symbol_table* symtab; + // Layout. + const Layout* layout; + // Object being relocated. + Sized_object* object; + // Number of local symbols. + unsigned int local_symbol_count; + // Values of local symbols. + typename elfcpp::Elf_types::Elf_Addr *values; + // Global symbols. + Symbol** symbols; + // Section index of relocation section. + unsigned int reloc_shndx; + // Section index of section being relocated. + unsigned int data_shndx; + + // Return a string showing the location of a relocation. This is + // only used for error messages. + std::string + location(size_t relnum, off_t reloffset) const; +}; + // Return an Object appropriate for the input file. P is BYTES long, // and holds the ELF header. diff --git a/gold/options.cc b/gold/options.cc index e5a16f1dc4..8e0465f2af 100644 --- a/gold/options.cc +++ b/gold/options.cc @@ -217,6 +217,8 @@ options::Command_line_options::options[] = &General_options::set_output_file_name), GENERAL_NOARG('r', NULL, N_("Generate relocatable output"), NULL, ONE_DASH, &General_options::set_relocatable), + GENERAL_NOARG('\0', "shared", N_("Generate shared library"), + NULL, ONE_DASH, &General_options::set_shared), GENERAL_NOARG('\0', "static", N_("Do not link against shared libraries"), NULL, ONE_DASH, &General_options::set_static), SPECIAL('\0', "help", N_("Report usage information"), NULL, @@ -232,6 +234,7 @@ General_options::General_options() : search_path_(), output_file_name_("a.out"), is_relocatable_(false), + is_shared_(false), is_static_(false) { } diff --git a/gold/options.h b/gold/options.h index ba32ef5699..7e890faa43 100644 --- a/gold/options.h +++ b/gold/options.h @@ -14,6 +14,7 @@ #include #include +#include namespace gold { @@ -52,6 +53,11 @@ class General_options is_relocatable() const { return this->is_relocatable_; } + // --shared: Whether generating a shared object. + bool + is_shared() const + { return this->is_shared_; } + // --static: Whether doing a static link. bool is_static() const @@ -73,6 +79,10 @@ class General_options set_relocatable() { this->is_relocatable_ = true; } + void + set_shared() + { this->is_shared_ = true; } + void set_static() { this->is_static_ = true; } @@ -80,6 +90,7 @@ class General_options Dir_list search_path_; const char* output_file_name_; bool is_relocatable_; + bool is_shared_; bool is_static_; // Don't copy this structure. @@ -143,6 +154,11 @@ class Input_argument Position_dependent_options options_; }; +// A list of input files. +class Input_argument_list : public std::vector +{ +}; + // All the information read from the command line. class Command_line @@ -164,8 +180,6 @@ class Command_line options() const { return this->options_; } - typedef std::list Input_argument_list; - // Get the list of input files. const Input_argument_list& inputs() const diff --git a/gold/output.h b/gold/output.h index 368e4ba438..e036b98bc1 100644 --- a/gold/output.h +++ b/gold/output.h @@ -443,6 +443,11 @@ class Output_segment flags() const { return this->flags_; } + // Return the memory size. + uint64_t + memsz() const + { return this->memsz_; } + // Return the maximum alignment of the Output_data. uint64_t max_data_align() const; diff --git a/gold/po/gold.pot b/gold/po/gold.pot index f086e6d40c..66f9572026 100644 --- a/gold/po/gold.pot +++ b/gold/po/gold.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2006-10-10 11:40-0700\n" +"POT-Creation-Date: 2006-10-20 13:39-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -91,7 +91,7 @@ msgstr "" msgid "%s: cannot open %s: %s\n" msgstr "" -#: gold.cc:76 +#: gold.cc:100 msgid "no input files" msgstr "" @@ -139,14 +139,49 @@ msgstr "" msgid "pthread_cond_signal failed" msgstr "" -#: i386.cc:98 +#: i386.cc:223 i386.cc:319 i386.cc:466 +#, c-format +msgid "%s: %s: unexpected reloc %u in object file\n" +msgstr "" + +#: i386.cc:256 i386.cc:277 +#, c-format +msgid "%s: %s: unsupported reloc %u against local symbol\n" +msgstr "" + +#: i386.cc:354 i386.cc:376 +#, c-format +msgid "%s: %s: unsupported reloc %u against global symbol %s\n" +msgstr "" + +#: i386.cc:397 +#, c-format +msgid "%s: %s: unsupported RELA reloc section\n" +msgstr "" + +#: i386.cc:503 i386.cc:571 #, c-format msgid "%s: %s: unsupported reloc %u\n" msgstr "" -#: i386.cc:121 +#: i386.cc:528 #, c-format -msgid "%s: %s: unsupported RELA reloc section\n" +msgid "%s: %s: TLS reloc but no TLS segment\n" +msgstr "" + +#: i386.cc:559 +#, c-format +msgid "%s: %s: unsupported reloc type %u\n" +msgstr "" + +#: i386.cc:689 +#, c-format +msgid "%s: %s: TLS relocation out of range\n" +msgstr "" + +#: i386.cc:707 +#, c-format +msgid "%s: %s: TLS relocation against invalid instruction\n" msgstr "" #: object.cc:60 @@ -159,103 +194,103 @@ msgstr "" msgid "%s: %s: bad e_shentsize field (%d != %d)\n" msgstr "" -#: object.cc:104 -#, c-format -msgid "%s: %s: unsupported ELF machine number %d\n" -msgstr "" - -#: object.cc:196 -#, c-format -msgid "%s: %s: invalid symbol table name index: %u\n" -msgstr "" - -#: object.cc:204 -#, c-format -msgid "%s: %s: symbol table name section has wrong type: %u\n" -msgstr "" - -#: object.cc:256 -#, c-format -msgid "%s: %s: section group %u link %u out of range\n" -msgstr "" - -#: object.cc:266 -#, c-format -msgid "%s: %s: section group %u info %u out of range\n" -msgstr "" - -#: object.cc:277 -#, c-format -msgid "%s; %s: symtab section %u link %u out of range\n" -msgstr "" - -#: object.cc:293 -#, c-format -msgid "%s: %s: symbol %u name offset %u out of range\n" -msgstr "" - -#: object.cc:315 -#, c-format -msgid "%s: %s: section %u in section group %u out of range" -msgstr "" - -#: object.cc:388 +#: object.cc:108 object.cc:418 #, c-format msgid "%s: %s: bad section name offset for section %u: %lu\n" msgstr "" -#: object.cc:449 +#: object.cc:131 +#, c-format +msgid "%s: %s: unsupported ELF machine number %d\n" +msgstr "" + +#: object.cc:226 +#, c-format +msgid "%s: %s: invalid symbol table name index: %u\n" +msgstr "" + +#: object.cc:234 +#, c-format +msgid "%s: %s: symbol table name section has wrong type: %u\n" +msgstr "" + +#: object.cc:286 +#, c-format +msgid "%s: %s: section group %u link %u out of range\n" +msgstr "" + +#: object.cc:296 +#, c-format +msgid "%s: %s: section group %u info %u out of range\n" +msgstr "" + +#: object.cc:307 +#, c-format +msgid "%s; %s: symtab section %u link %u out of range\n" +msgstr "" + +#: object.cc:323 +#, c-format +msgid "%s: %s: symbol %u name offset %u out of range\n" +msgstr "" + +#: object.cc:345 +#, c-format +msgid "%s: %s: section %u in section group %u out of range" +msgstr "" + +#: object.cc:479 #, c-format msgid "%s: %s: size of symbols is not multiple of symbol size\n" msgstr "" -#: object.cc:537 +#: object.cc:566 #, c-format msgid "%s: %s: unknown section index %u for local symbol %u\n" msgstr "" -#: object.cc:548 +#: object.cc:577 #, c-format msgid "%s: %s: local symbol %u section index %u out of range\n" msgstr "" #. elfcpp::ET_DYN -#: object.cc:701 +#: object.cc:755 #, c-format msgid "%s: %s: dynamic objects are not yet supported\n" msgstr "" -#: object.cc:725 object.cc:778 object.cc:799 +#: object.cc:779 object.cc:832 object.cc:853 #, c-format msgid "%s: %s: ELF file too short\n" msgstr "" -#: object.cc:734 +#: object.cc:788 #, c-format msgid "%s: %s: invalid ELF version 0\n" msgstr "" -#: object.cc:737 +#: object.cc:791 #, c-format msgid "%s: %s: unsupported ELF version %d\n" msgstr "" -#: object.cc:745 +#: object.cc:799 #, c-format msgid "%s: %s: invalid ELF class 0\n" msgstr "" -#: object.cc:752 +#: object.cc:806 #, c-format msgid "%s: %s: unsupported ELF class %d\n" msgstr "" -#: object.cc:760 +#: object.cc:814 #, c-format msgid "%s: %s: invalid ELF data encoding\n" msgstr "" -#: object.cc:767 +#: object.cc:821 #, c-format msgid "%s: %s: unsupported ELF data encoding %d\n" msgstr "" @@ -296,32 +331,36 @@ msgid "Generate relocatable output" msgstr "" #: options.cc:220 -msgid "Do not link against shared libraries" +msgid "Generate shared library" msgstr "" #: options.cc:222 +msgid "Do not link against shared libraries" +msgstr "" + +#: options.cc:224 msgid "Report usage information" msgstr "" -#: options.cc:319 options.cc:370 options.cc:434 +#: options.cc:322 options.cc:373 options.cc:437 msgid "missing argument" msgstr "" -#: options.cc:332 options.cc:379 +#: options.cc:335 options.cc:382 msgid "unknown option" msgstr "" -#: options.cc:448 +#: options.cc:451 #, c-format msgid "%s: use the --help option for usage information\n" msgstr "" -#: options.cc:457 +#: options.cc:460 #, c-format msgid "%s: %s: %s\n" msgstr "" -#: options.cc:466 +#: options.cc:469 #, c-format msgid "%s: -%c: %s\n" msgstr "" @@ -361,29 +400,28 @@ msgstr "" msgid "%s: %s: close: %s\n" msgstr "" -#. Here we have to handle archives and any other input file -#. types we need. -#: readsyms.cc:110 +#. Here we have to handle any other input file types we need. +#: readsyms.cc:109 #, c-format msgid "%s: %s: not an object or archive\n" msgstr "" -#: reloc.cc:165 +#: reloc.cc:165 reloc.cc:392 #, c-format msgid "%s: %s: relocation section %u has bad info %u\n" msgstr "" -#: reloc.cc:182 +#: reloc.cc:176 reloc.cc:409 #, c-format msgid "%s: %s: relocation section %u uses unexpected symbol table %u\n" msgstr "" -#: reloc.cc:201 +#: reloc.cc:192 reloc.cc:428 #, c-format msgid "%s: %s: unexpected entsize for reloc section %u: %lu != %u" msgstr "" -#: reloc.cc:212 +#: reloc.cc:203 reloc.cc:439 #, c-format msgid "%s: %s: reloc section %u size %lu uneven" msgstr "" @@ -408,12 +446,12 @@ msgstr "" msgid "%s: %s: bad global symbol name offset %u at %lu\n" msgstr "" -#: target-reloc.h:76 +#: target-reloc.h:145 #, c-format -msgid "%s: %s: reloc %zu has bad offset %lu\n" +msgid "%s: %s: reloc has bad offset %zu\n" msgstr "" -#: target-reloc.h:107 +#: target-reloc.h:176 #, c-format msgid "%s: %s: undefined reference to '%s'\n" msgstr "" diff --git a/gold/readsyms.cc b/gold/readsyms.cc index adc8fac6e9..3a5650ac80 100644 --- a/gold/readsyms.cc +++ b/gold/readsyms.cc @@ -76,10 +76,10 @@ Read_symbols::run(Workqueue* workqueue) Read_symbols_data* sd = new Read_symbols_data; obj->read_symbols(sd); - workqueue->queue(new Add_symbols(this->symtab_, this->layout_, - obj, sd, - this->this_blocker_, - this->next_blocker_)); + workqueue->queue_front(new Add_symbols(this->symtab_, this->layout_, + obj, sd, + this->this_blocker_, + this->next_blocker_)); // Opening the file locked it, so now we need to unlock it. input_file->file().unlock(); @@ -105,8 +105,7 @@ Read_symbols::run(Workqueue* workqueue) } } - // Here we have to handle archives and any other input file - // types we need. + // Here we have to handle any other input file types we need. fprintf(stderr, _("%s: %s: not an object or archive\n"), program_name, input_file->file().filename().c_str()); gold_exit(false); diff --git a/gold/reloc.cc b/gold/reloc.cc index 905eeae599..bb672e4e10 100644 --- a/gold/reloc.cc +++ b/gold/reloc.cc @@ -10,6 +10,86 @@ namespace gold { +// Read_relocs methods. + +// These tasks just read the relocation information from the file. +// After reading it, the start another task to process the +// information. These tasks requires access to the file. + +Task::Is_runnable_type +Read_relocs::is_runnable(Workqueue*) +{ + return this->object_->is_locked() ? IS_LOCKED : IS_RUNNABLE; +} + +// Lock the file. + +Task_locker* +Read_relocs::locks(Workqueue*) +{ + return new Task_locker_obj(*this->object_); +} + +// Read the relocations and then start a Scan_relocs_task. + +void +Read_relocs::run(Workqueue* workqueue) +{ + Read_relocs_data *rd = new Read_relocs_data; + this->object_->read_relocs(rd); + workqueue->queue_front(new Scan_relocs(this->options_, this->symtab_, + this->object_, rd, this->symtab_lock_, + this->blocker_)); +} + +// Scan_relocs methods. + +// These tasks scan the relocations read by Read_relocs and mark up +// the symbol table to indicate which relocations are required. We +// use a lock on the symbol table to keep them from interfering with +// each other. + +Task::Is_runnable_type +Scan_relocs::is_runnable(Workqueue*) +{ + return this->symtab_lock_->is_writable() ? IS_RUNNABLE : IS_LOCKED; +} + +// Return the locks we hold: one on the file, one on the symbol table +// and one blocker. + +class Scan_relocs::Scan_relocs_locker : public Task_locker +{ + public: + Scan_relocs_locker(Object* object, Task_token& symtab_lock, Task* task, + Task_token& blocker, Workqueue* workqueue) + : objlock_(*object), symtab_locker_(symtab_lock, task), + blocker_(blocker, workqueue) + { } + + private: + Task_locker_obj objlock_; + Task_locker_write symtab_locker_; + Task_locker_block blocker_; +}; + +Task_locker* +Scan_relocs::locks(Workqueue* workqueue) +{ + return new Scan_relocs_locker(this->object_, *this->symtab_lock_, this, + *this->blocker_, workqueue); +} + +// Scan the relocs. + +void +Scan_relocs::run(Workqueue*) +{ + this->object_->scan_relocs(this->options_, this->symtab_, this->rd_); + delete this->rd_; + this->rd_ = NULL; +} + // Relocate_task methods. // These tasks are always runnable. @@ -48,17 +128,154 @@ Relocate_task::locks(Workqueue* workqueue) void Relocate_task::run(Workqueue*) { - this->object_->relocate(this->options_, this->symtab_, this->sympool_, + this->object_->relocate(this->options_, this->symtab_, this->layout_, this->of_); } +// Read the relocs and local symbols from the object file and store +// the information in RD. + +template +void +Sized_object::do_read_relocs(Read_relocs_data* rd) +{ + rd->relocs.clear(); + + unsigned int shnum = this->shnum(); + if (shnum == 0) + return; + + rd->relocs.reserve(shnum / 2); + + const unsigned char *pshdrs = this->get_view(this->shoff_, + shnum * This::shdr_size); + // Skip the first, dummy, section. + const unsigned char *ps = pshdrs + This::shdr_size; + for (unsigned int i = 1; i < shnum; ++i, ps += This::shdr_size) + { + typename This::Shdr shdr(ps); + + unsigned int sh_type = shdr.get_sh_type(); + if (sh_type != elfcpp::SHT_REL && sh_type != elfcpp::SHT_RELA) + continue; + + unsigned int shndx = shdr.get_sh_info(); + if (shndx >= shnum) + { + fprintf(stderr, _("%s: %s: relocation section %u has bad info %u\n"), + program_name, this->name().c_str(), i, shndx); + gold_exit(false); + } + + if (!this->is_section_included(shndx)) + continue; + + if (shdr.get_sh_link() != this->symtab_shnum_) + { + fprintf(stderr, + _("%s: %s: relocation section %u uses unexpected " + "symbol table %u\n"), + program_name, this->name().c_str(), i, shdr.get_sh_link()); + gold_exit(false); + } + + off_t sh_size = shdr.get_sh_size(); + + unsigned int reloc_size; + if (sh_type == elfcpp::SHT_REL) + reloc_size = elfcpp::Elf_sizes::rel_size; + else + reloc_size = elfcpp::Elf_sizes::rela_size; + if (reloc_size != shdr.get_sh_entsize()) + { + fprintf(stderr, + _("%s: %s: unexpected entsize for reloc section %u: " + "%lu != %u"), + program_name, this->name().c_str(), i, + static_cast(shdr.get_sh_entsize()), + reloc_size); + gold_exit(false); + } + + size_t reloc_count = sh_size / reloc_size; + if (reloc_count * reloc_size != sh_size) + { + fprintf(stderr, _("%s: %s: reloc section %u size %lu uneven"), + program_name, this->name().c_str(), i, + static_cast(sh_size)); + gold_exit(false); + } + + rd->relocs.push_back(Section_relocs()); + Section_relocs& sr(rd->relocs.back()); + sr.reloc_shndx = i; + sr.data_shndx = shndx; + sr.contents = this->get_lasting_view(shdr.get_sh_offset(), sh_size); + sr.sh_type = sh_type; + sr.reloc_count = reloc_count; + } + + // Read the local symbols. + if (this->symtab_shnum_ == 0 || this->local_symbol_count_ == 0) + rd->local_symbols = NULL; + else + { + typename This::Shdr symtabshdr(pshdrs + + this->symtab_shnum_ * This::shdr_size); + assert(symtabshdr.get_sh_type() == elfcpp::SHT_SYMTAB); + const int sym_size = This::sym_size; + const unsigned int loccount = this->local_symbol_count_; + assert(loccount == symtabshdr.get_sh_info()); + off_t locsize = loccount * sym_size; + rd->local_symbols = this->get_lasting_view(symtabshdr.get_sh_offset(), + locsize); + } +} + +// Scan the relocs and adjust the symbol table. This looks for +// relocations which require GOT/PLT/COPY relocations. + +template +void +Sized_object::do_scan_relocs(const General_options& options, + Symbol_table* symtab, + Read_relocs_data* rd) +{ + Sized_target* target = this->sized_target(); + + const unsigned char* local_symbols; + if (rd->local_symbols == NULL) + local_symbols = NULL; + else + local_symbols = rd->local_symbols->data(); + + for (Read_relocs_data::Relocs_list::iterator p = rd->relocs.begin(); + p != rd->relocs.end(); + ++p) + { + target->scan_relocs(options, symtab, this, p->sh_type, + p->contents->data(), p->reloc_count, + this->local_symbol_count_, + local_symbols, + this->symbols_); + delete p->contents; + p->contents = NULL; + } + + if (rd->local_symbols != NULL) + { + delete rd->local_symbols; + rd->local_symbols = NULL; + } +} + // Relocate the input sections and write out the local symbols. template void -Sized_object::do_relocate(const General_options&, +Sized_object::do_relocate(const General_options& options, const Symbol_table* symtab, - const Stringpool* sympool, + const Layout* layout, Output_file* of) { unsigned int shnum = this->shnum(); @@ -78,7 +295,7 @@ Sized_object::do_relocate(const General_options&, // Apply relocations. - this->relocate_sections(symtab, pshdrs, &views); + this->relocate_sections(options, symtab, layout, pshdrs, &views); // Write out the accumulated views. for (unsigned int i = 1; i < shnum; ++i) @@ -89,7 +306,7 @@ Sized_object::do_relocate(const General_options&, } // Write out the local symbols. - this->write_local_symbols(of, sympool); + this->write_local_symbols(of, layout->sympool()); } // Write section data to the output file. PSHDRS points to the @@ -127,9 +344,8 @@ Sized_object::write_sections(const unsigned char* pshdrs, off_t sh_size = shdr.get_sh_size(); unsigned char* view = of->get_output_view(start, sh_size); - this->input_file()->file().read(shdr.get_sh_offset(), - sh_size, - view); + this->read(shdr.get_sh_offset(), sh_size, view); + pvs->view = view; pvs->address = os->address() + map_sections[i].offset; pvs->offset = start; @@ -142,14 +358,25 @@ Sized_object::write_sections(const unsigned char* pshdrs, template void -Sized_object::relocate_sections(const Symbol_table* symtab, - const unsigned char* pshdrs, - Views* pviews) +Sized_object::relocate_sections( + const General_options& options, + const Symbol_table* symtab, + const Layout* layout, + const unsigned char* pshdrs, + Views* pviews) { unsigned int shnum = this->shnum(); - std::vector& map_sections(this->map_to_output()); Sized_target* target = this->sized_target(); + Relocate_info relinfo; + relinfo.options = &options; + relinfo.symtab = symtab; + relinfo.layout = layout; + relinfo.object = this; + relinfo.local_symbol_count = this->local_symbol_count_; + relinfo.values = this->values_; + relinfo.symbols = this->symbols_; + const unsigned char* p = pshdrs + This::shdr_size; for (unsigned int i = 1; i < shnum; ++i, p += This::shdr_size) { @@ -167,7 +394,7 @@ Sized_object::relocate_sections(const Symbol_table* symtab, gold_exit(false); } - if (map_sections[index].output_section == NULL) + if (!this->is_section_included(index)) { // This relocation section is against a section which we // discarded. @@ -215,10 +442,12 @@ Sized_object::relocate_sections(const Symbol_table* symtab, gold_exit(false); } - target->relocate_section(symtab, this, sh_type, prelocs, reloc_count, - this->local_symbol_count_, - this->values_, - this->symbols_, + relinfo.reloc_shndx = i; + relinfo.data_shndx = index; + target->relocate_section(&relinfo, + sh_type, + prelocs, + reloc_count, (*pviews)[index].view, (*pviews)[index].address, (*pviews)[index].view_size); @@ -228,32 +457,72 @@ Sized_object::relocate_sections(const Symbol_table* symtab, // Instantiate the templates we need. We could use the configure // script to restrict this to only the ones for implemented targets. +template +void +Sized_object<32, false>::do_read_relocs(Read_relocs_data* rd); + +template +void +Sized_object<32, true>::do_read_relocs(Read_relocs_data* rd); + +template +void +Sized_object<64, false>::do_read_relocs(Read_relocs_data* rd); + +template +void +Sized_object<64, true>::do_read_relocs(Read_relocs_data* rd); + +template +void +Sized_object<32, false>::do_scan_relocs(const General_options& options, + Symbol_table* symtab, + Read_relocs_data* rd); + +template +void +Sized_object<32, true>::do_scan_relocs(const General_options& options, + Symbol_table* symtab, + Read_relocs_data* rd); + +template +void +Sized_object<64, false>::do_scan_relocs(const General_options& options, + Symbol_table* symtab, + Read_relocs_data* rd); + +template +void +Sized_object<64, true>::do_scan_relocs(const General_options& options, + Symbol_table* symtab, + Read_relocs_data* rd); + template void Sized_object<32, false>::do_relocate(const General_options& options, const Symbol_table* symtab, - const Stringpool* sympool, + const Layout* layout, Output_file* of); template void Sized_object<32, true>::do_relocate(const General_options& options, const Symbol_table* symtab, - const Stringpool* sympool, + const Layout* layout, Output_file* of); template void Sized_object<64, false>::do_relocate(const General_options& options, const Symbol_table* symtab, - const Stringpool* sympool, + const Layout* layout, Output_file* of); template void Sized_object<64, true>::do_relocate(const General_options& options, const Symbol_table* symtab, - const Stringpool* sympool, + const Layout* layout, Output_file* of); diff --git a/gold/reloc.h b/gold/reloc.h index 287bb79c17..a2e9d54729 100644 --- a/gold/reloc.h +++ b/gold/reloc.h @@ -3,18 +3,98 @@ #ifndef GOLD_RELOC_H #define GOLD_RELOC_H +#include + #include "workqueue.h" namespace gold { +class Object; +class Read_relocs_data; +class Stringpool; + +// A class to read the relocations for an object file, and then queue +// up a task to see if they require any GOT/PLT/COPY relocations in +// the symbol table. + +class Read_relocs : public Task +{ + public: + // SYMTAB_LOCK is used to lock the symbol table. BLOCKER should be + // unblocked when the Scan_relocs task completes. + Read_relocs(const General_options& options, Symbol_table* symtab, + Object* object, Task_token* symtab_lock, + Task_token* blocker) + : options_(options), symtab_(symtab), object_(object), + symtab_lock_(symtab_lock), blocker_(blocker) + { } + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + const General_options& options_; + Symbol_table* symtab_; + Object* object_; + Task_token* symtab_lock_; + Task_token* blocker_; +}; + +// Scan the relocations for an object to see if they require any +// GOT/PLT/COPY relocations. + +class Scan_relocs : public Task +{ + public: + // SYMTAB_LOCK is used to lock the symbol table. BLOCKER should be + // unblocked when the task completes. + Scan_relocs(const General_options& options, Symbol_table* symtab, + Object* object, Read_relocs_data* rd, Task_token* symtab_lock, + Task_token* blocker) + : options_(options), symtab_(symtab), object_(object), rd_(rd), + symtab_lock_(symtab_lock), blocker_(blocker) + { } + + // The standard Task methods. + + Is_runnable_type + is_runnable(Workqueue*); + + Task_locker* + locks(Workqueue*); + + void + run(Workqueue*); + + private: + class Scan_relocs_locker; + + const General_options& options_; + Symbol_table* symtab_; + Object* object_; + Read_relocs_data* rd_; + Task_token* symtab_lock_; + Task_token* blocker_; +}; + +// A class to perform all the relocations for an object file. + class Relocate_task : public Task { public: Relocate_task(const General_options& options, const Symbol_table* symtab, - const Stringpool* sympool, Object* object, Output_file* of, + const Layout* layout, Object* object, Output_file* of, Task_token* final_blocker) - : options_(options), symtab_(symtab), sympool_(sympool), object_(object), + : options_(options), symtab_(symtab), layout_(layout), object_(object), of_(of), final_blocker_(final_blocker) { } @@ -34,12 +114,432 @@ class Relocate_task : public Task const General_options& options_; const Symbol_table* symtab_; - const Stringpool* sympool_; + const Layout* layout_; Object* object_; Output_file* of_; Task_token* final_blocker_; }; +// Integer swapping routines used by relocation functions. FIXME: +// Maybe these should be more general, and/or shared with elfcpp. + +// Endian simply indicates whether the host is big endian or not, +// based on the results of the configure script. + +struct Endian +{ + public: + // Used for template specializations. +#ifdef WORDS_BIGENDIAN + static const bool host_big_endian = true; +#else + static const bool host_big_endian = false; +#endif +}; + +// Valtype_base is a template based on size (8, 16, 32, 64) which +// defines a typedef Valtype for the unsigned integer of the specified +// size. + +template +struct Valtype_base; + +template<> +struct Valtype_base<8> +{ + typedef unsigned char Valtype; +}; + +template<> +struct Valtype_base<16> +{ + typedef uint16_t Valtype; +}; + +template<> +struct Valtype_base<32> +{ + typedef uint32_t Valtype; +}; + +template<> +struct Valtype_base<64> +{ + typedef uint64_t Valtype; +}; + +// Convert_host is a template based on size and on whether the host +// and target have the same endianness. It defines the type Valtype, +// and defines a function convert_host which takes an argument of type +// Valtype and swaps it if the host and target have different +// endianness. + +template +struct Convert_host; + +template +struct Convert_host +{ + typedef typename Valtype_base::Valtype Valtype; + + static inline Valtype + convert_host(Valtype v) + { return v; } +}; + +template<> +struct Convert_host<8, false> +{ + typedef Valtype_base<8>::Valtype Valtype; + + static inline Valtype + convert_host(Valtype v) + { return v; } +}; + +template<> +struct Convert_host<16, false> +{ + typedef Valtype_base<16>::Valtype Valtype; + + static inline Valtype + convert_host(Valtype v) + { return bswap_16(v); } +}; + +template<> +struct Convert_host<32, false> +{ + typedef Valtype_base<32>::Valtype Valtype; + + static inline Valtype + convert_host(Valtype v) + { return bswap_32(v); } +}; + +template<> +struct Convert_host<64, false> +{ + typedef Valtype_base<64>::Valtype Valtype; + + static inline Valtype + convert_host(Valtype v) + { return bswap_64(v); } +}; + +// Convert is a template based on size and on whether we have a big +// endian target. It defines Valtype and convert_host like +// Convert_host. That is, it is just like Convert_host except in the +// meaning of the second template parameter. + +template +struct Convert +{ + typedef typename Valtype_base::Valtype Valtype; + + static inline Valtype + convert_host(Valtype v) + { return Convert_host + ::convert_host(v); } +}; + +// Swap is a template based on size and on whether the target is big +// endian. It defines the type Valtype and the functions readval and +// writeval. The functions read and write values of the appropriate +// size out of buffers, swapping them if necessary. + +template +struct Swap +{ + typedef typename Valtype_base::Valtype Valtype; + + static inline Valtype + readval(const Valtype* wv) + { return Convert::convert_host(*wv); } + + static inline void + writeval(Valtype* wv, Valtype v) + { *wv = Convert::convert_host(v); } +}; + +// Swap_unaligned is a template based on size and on whether the +// target is big endian. It defines the type Valtype and the +// functions readval_unaligned and writeval_unaligned. The functions +// read and write values of the appropriate size out of buffers which +// may be misaligned. + +template +class Swap_unaligned; + +template +class Swap_unaligned<8, big_endian> +{ +public: + typedef typename Valtype_base<8>::Valtype Valtype; + + static inline Valtype + readval_unaligned(const unsigned char* wv) + { return *wv; } + + static inline void + writeval_unaligned(unsigned char* wv, Valtype v) + { *wv = v; } +}; + +template<> +class Swap_unaligned<16, false> +{ +public: + typedef Valtype_base<16>::Valtype Valtype; + + static inline Valtype + readval_unaligned(const unsigned char* wv) + { + return (wv[1] << 8) | wv[0]; + } + + static inline void + writeval_unaligned(unsigned char* wv, Valtype v) + { + wv[1] = v >> 8; + wv[0] = v; + } +}; + +template<> +class Swap_unaligned<16, true> +{ +public: + typedef Valtype_base<16>::Valtype Valtype; + + static inline Valtype + readval_unaligned(const unsigned char* wv) + { + return (wv[0] << 8) | wv[1]; + } + + static inline void + writeval_unaligned(unsigned char* wv, Valtype v) + { + wv[0] = v >> 8; + wv[1] = v; + } +}; + +template<> +class Swap_unaligned<32, false> +{ +public: + typedef Valtype_base<32>::Valtype Valtype; + + static inline Valtype + readval_unaligned(const unsigned char* wv) + { + return (wv[3] << 24) | (wv[2] << 16) | (wv[1] << 8) | wv[0]; + } + + static inline void + writeval_unaligned(unsigned char* wv, Valtype v) + { + wv[3] = v >> 24; + wv[2] = v >> 16; + wv[1] = v >> 8; + wv[0] = v; + } +}; + +template<> +class Swap_unaligned<32, true> +{ +public: + typedef Valtype_base<32>::Valtype Valtype; + + static inline Valtype + readval_unaligned(const unsigned char* wv) + { + return (wv[0] << 24) | (wv[1] << 16) | (wv[2] << 8) | wv[3]; + } + + static inline void + writeval_unaligned(unsigned char* wv, Valtype v) + { + wv[0] = v >> 24; + wv[1] = v >> 16; + wv[2] = v >> 8; + wv[3] = v; + } +}; + +template<> +class Swap_unaligned<64, false> +{ +public: + typedef Valtype_base<64>::Valtype Valtype; + + static inline Valtype + readval_unaligned(const unsigned char* wv) + { + return ((static_cast(wv[7]) << 56) + | (static_cast(wv[6]) << 48) + | (static_cast(wv[5]) << 40) + | (static_cast(wv[4]) << 32) + | (static_cast(wv[3]) << 24) + | (static_cast(wv[2]) << 16) + | (static_cast(wv[1]) << 8) + | static_cast(wv[0])); + } + + static inline void + writeval_unaligned(unsigned char* wv, Valtype v) + { + wv[7] = v >> 56; + wv[6] = v >> 48; + wv[5] = v >> 40; + wv[4] = v >> 32; + wv[3] = v >> 24; + wv[2] = v >> 16; + wv[1] = v >> 8; + wv[0] = v; + } +}; + +template<> +class Swap_unaligned<64, true> +{ +public: + typedef Valtype_base<64>::Valtype Valtype; + + static inline Valtype + readval_unaligned(const unsigned char* wv) + { + return ((static_cast(wv[0]) << 56) + | (static_cast(wv[1]) << 48) + | (static_cast(wv[2]) << 40) + | (static_cast(wv[3]) << 32) + | (static_cast(wv[4]) << 24) + | (static_cast(wv[5]) << 16) + | (static_cast(wv[6]) << 8) + | static_cast(wv[7])); + } + + static inline void + writeval_unaligned(unsigned char* wv, Valtype v) + { + wv[7] = v >> 56; + wv[6] = v >> 48; + wv[5] = v >> 40; + wv[4] = v >> 32; + wv[3] = v >> 24; + wv[2] = v >> 16; + wv[1] = v >> 8; + wv[0] = v; + } +}; + +// Standard relocation routines which are used on many targets. Here +// SIZE and BIG_ENDIAN refer to the target, not the relocation type. + +template +class Relocate_functions +{ +private: + // Do a simple relocation with the addend in the section contents. + // VALSIZE is the size of the value. + template + static inline void + rel(unsigned char* view, typename Swap::Valtype value) + { + typedef typename Swap::Valtype Valtype; + Valtype* wv = reinterpret_cast(view); + Valtype x = Swap::readval(wv); + Swap::writeval(wv, x + value); + } + + // Do a simple PC relative relocation with the addend in the section + // contents. VALSIZE is the size of the value. + template + static inline void + pcrel(unsigned char* view, typename Swap::Valtype value, + typename elfcpp::Elf_types::Elf_Addr address) + { + typedef typename Swap::Valtype Valtype; + Valtype* wv = reinterpret_cast(view); + Valtype x = Swap::readval(wv); + Swap::writeval(wv, x + value - address); + } + + typedef Relocate_functions This; + +public: + // Do a simple 8-bit REL relocation with the addend in the object + // file data. + static inline void + rel8(unsigned char* view, unsigned char value) + { + This::template rel<8>(view, value); + } + + // Do a simple 8-bit PC relative relocation with the addend in the + // object file data. + static inline void + pcrel8(unsigned char* view, unsigned char value, + typename elfcpp::Elf_types::Elf_Addr address) + { + This::template pcrel<8>(view, value, address); + } + + // Do a simple 16-bit REL relocation with the addend in the object + // file data. + static inline void + rel16(unsigned char* view, elfcpp::Elf_Half value) + { + This::template rel<16>(view, value); + } + + // Do a simple 32-bit PC relative REL relocation with the addend in + // the object file data. + static inline void + pcrel16(unsigned char* view, elfcpp::Elf_Word value, + typename elfcpp::Elf_types::Elf_Addr address) + { + This::template pcrel<16>(view, value, address); + } + + // Do a simple 32-bit REL relocation with the addend in the section + // contents. + static inline void + rel32(unsigned char* view, elfcpp::Elf_Word value) + { + This::template rel<32>(view, value); + } + + // Do a simple 32-bit PC relative REL relocation with the addend in + // the section contents. + static inline void + pcrel32(unsigned char* view, elfcpp::Elf_Word value, + typename elfcpp::Elf_types::Elf_Addr address) + { + This::template pcrel<32>(view, value, address); + } + + // Do a simple 64-bit REL relocation with the addend in the section + // contents. + static inline void + rel64(unsigned char* view, elfcpp::Elf_Word value) + { + This::template rel<64>(view, value); + } + + // Do a simple 64-bit PC relative REL relocation with the addend in + // the section contents. + static inline void + pcrel64(unsigned char* view, elfcpp::Elf_Word value, + typename elfcpp::Elf_types::Elf_Addr address) + { + This::template pcrel<64>(view, value, address); + } +}; + } // End namespace gold. #endif // !defined(GOLD_RELOC_H) diff --git a/gold/symtab.h b/gold/symtab.h index 23d54e4d58..2c5fbc93d3 100644 --- a/gold/symtab.h +++ b/gold/symtab.h @@ -94,6 +94,12 @@ class Symbol set_in_dyn() { this->in_dyn_ = true; } + // Return whether this symbol needs an entry in the dynamic symbol + // table. FIXME: Needs to be fleshed out. + bool + in_dynsym() const + { return this->in_dyn_; } + protected: // Instances of this class should always be created at a specific // size. diff --git a/gold/target-reloc.h b/gold/target-reloc.h index f972b117d9..2d1fe306fb 100644 --- a/gold/target-reloc.h +++ b/gold/target-reloc.h @@ -4,6 +4,7 @@ #define GOLD_TARGET_RELOC_H #include "elfcpp.h" +#include "object.h" #include "symtab.h" namespace gold @@ -29,7 +30,79 @@ struct Reloc_types static const int reloc_size = elfcpp::Elf_sizes::rela_size; }; -// This function implements the generic part of relocation handling. +// This function implements the generic part of reloc scanning. This +// is an inline function which takes a class whose operator() +// implements the machine specific part of scanning. We do it this +// way to avoidmaking a function call for each relocation, and to +// avoid repeating the generic code for each target. + +template +inline void +scan_relocs( + const General_options& options, + Symbol_table* symtab, + Sized_object* object, + const unsigned char* prelocs, + size_t reloc_count, + size_t local_count, + const unsigned char* plocal_syms, + Symbol** global_syms) +{ + typedef typename Reloc_types::Reloc Reltype; + const int reloc_size = Reloc_types::reloc_size; + const int sym_size = elfcpp::Elf_sizes::sym_size; + Scan scan; + + for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) + { + Reltype reloc(prelocs); + + typename elfcpp::Elf_types::Elf_WXword r_info = reloc.get_r_info(); + unsigned int r_sym = elfcpp::elf_r_sym(r_info); + unsigned int r_type = elfcpp::elf_r_type(r_info); + + if (r_sym < local_count) + { + assert(plocal_syms != NULL); + typename elfcpp::Sym lsym(plocal_syms + + r_sym * sym_size); + const unsigned int shndx = lsym.get_st_shndx(); + if (shndx < elfcpp::SHN_LORESERVE + && !object->is_section_included(lsym.get_st_shndx())) + { + // RELOC is a relocation against a local symbol in a + // section we are discarding. We can ignore this + // relocation. It will eventually become a reloc + // against the value zero. + // + // FIXME: We should issue a warning if this is an + // allocated section; is this the best place to do it? + // + // FIXME: The old GNU linker would in some cases look + // for the linkonce section which caused this section to + // be discarded, and, if the other section was the same + // size, change the reloc to refer to the other section. + // That seems risky and weird to me, and I don't know of + // any case where it is actually required. + + continue; + } + + scan.local(options, object, reloc, r_type, lsym); + } + else + { + Symbol* gsym = global_syms[r_sym - local_count]; + assert(gsym != NULL); + if (gsym->is_forwarder()) + gsym = symtab->resolve_forwards(gsym); + + scan.global(options, object, reloc, r_type, gsym); + } + } +} + +// This function implements the generic part of relocation processing. // This is an inline function which take a class whose operator() // implements the machine specific part of relocation. We do it this // way to avoid making a function call for each relocation, and to @@ -37,27 +110,19 @@ struct Reloc_types // target. // SIZE is the ELF size: 32 or 64. BIG_ENDIAN is the endianness of -// the data. SH_TYPE is the section type: SHT_REL or SHT_RELA. RELOC -// implements operator() to do a relocation. +// the data. SH_TYPE is the section type: SHT_REL or SHT_RELA. +// RELOCATE implements operator() to do a relocation. -// OBJECT is the object for we are processing relocs. SH_TYPE is the -// type of relocation: SHT_REL or SHT_RELA. PRELOCS points to the -// relocation data. RELOC_COUNT is the number of relocs. LOCAL_COUNT -// is the number of local symbols. LOCAL_VALUES holds the values of -// the local symbols. GLOBAL_SYMS points to the global symbols. VIEW -// is the section data, VIEW_ADDRESS is its memory address, and -// VIEW_SIZE is the size. +// PRELOCS points to the relocation data. RELOC_COUNT is the number +// of relocs. VIEW is the section data, VIEW_ADDRESS is its memory +// address, and VIEW_SIZE is the size. template inline void relocate_section( - const Symbol_table* symtab, - Sized_object* object, + const Relocate_info* relinfo, const unsigned char* prelocs, size_t reloc_count, - size_t local_count, - const typename elfcpp::Elf_types::Elf_Addr* local_values, - Symbol** global_syms, unsigned char* view, typename elfcpp::Elf_types::Elf_Addr view_address, off_t view_size) @@ -66,6 +131,10 @@ relocate_section( const int reloc_size = Reloc_types::reloc_size; Relocate relocate; + unsigned int local_count = relinfo->local_symbol_count; + typename elfcpp::Elf_types::Elf_Addr *local_values = relinfo->values; + Symbol** global_syms = relinfo->symbols; + for (size_t i = 0; i < reloc_count; ++i, prelocs += reloc_size) { Reltype reloc(prelocs); @@ -73,9 +142,9 @@ relocate_section( off_t offset = reloc.get_r_offset(); if (offset < 0 || offset >= view_size) { - fprintf(stderr, _("%s: %s: reloc %zu has bad offset %lu\n"), - program_name, object->name().c_str(), i, - static_cast(offset)); + fprintf(stderr, _("%s: %s: reloc has bad offset %zu\n"), + program_name, relinfo->location(i, offset).c_str(), + static_cast(offset)); gold_exit(false); } @@ -96,7 +165,7 @@ relocate_section( Symbol* gsym = global_syms[r_sym - local_count]; assert(gsym != NULL); if (gsym->is_forwarder()) - gsym = symtab->resolve_forwards(gsym); + gsym = relinfo->symtab->resolve_forwards(gsym); sym = static_cast*>(gsym); value = sym->value(); @@ -105,13 +174,14 @@ relocate_section( && sym->binding() != elfcpp::STB_WEAK) { fprintf(stderr, _("%s: %s: undefined reference to '%s'\n"), - program_name, object->name().c_str(), sym->name()); + program_name, relinfo->location(i, offset).c_str(), + sym->name()); // gold_exit(false); } } - relocate(object, reloc, r_type, sym, value, view + offset, - view_address + offset); + relocate.relocate(relinfo, i, reloc, r_type, sym, value, view + offset, + view_address + offset, view_size); } } diff --git a/gold/target.h b/gold/target.h index 5230bb2c60..75f149e070 100644 --- a/gold/target.h +++ b/gold/target.h @@ -21,9 +21,12 @@ namespace gold { +class General_options; class Object; template class Sized_object; +template +struct Relocate_info; // The abstract class for target specific handling. @@ -129,32 +132,45 @@ class Sized_target : public Target // Resolve a symbol for the target. This should be overridden by a // target which needs to take special action. TO is the // pre-existing symbol. SYM is the new symbol, seen in OBJECT. + // This will only be called if has_resolve() returns true. virtual void resolve(Symbol*, const elfcpp::Sym&, Object*) { abort(); } - // Relocate section data. SYMTAB is the symbol table. OBJECT is - // the object in which the section appears. SH_TYPE is the type of - // the relocation section, SHT_REL or SHT_RELA. PRELOCS points to - // the relocation information. RELOC_COUNT is the number of relocs. - // LOCAL_COUNT is the number of local symbols. The VALUES and - // GLOBAL_SYMS have symbol table information. VIEW is a view into - // the output file holding the section contents, VIEW_ADDRESS is the - // virtual address of the view, and VIEW_SIZE is the size of the - // view. + // Scan the relocs for a section, and record any information + // required for the symbol. OPTIONS is the command line options. + // SYMTAB is the symbol table. OBJECT is the object in which the + // section appears. SH_TYPE is the type of the relocation section, + // SHT_REL or SHT_RELA. PRELOCS points to the relocation data. + // RELOC_COUNT is the number of relocs. LOCAL_SYMBOL_COUNT is the + // number of local symbols. PLOCAL_SYMBOLS points to the local + // symbol data from OBJECT. GLOBAL_SYMBOLS is the array of pointers + // to the global symbol table from OBJECT. virtual void - relocate_section(const Symbol_table*, // symtab - Sized_object*, // object - unsigned int, // sh_type - const unsigned char*, // prelocs - size_t, // reloc_count - unsigned int, // local_count - const typename elfcpp::Elf_types::Elf_Addr*, // values - Symbol**, // global_syms - unsigned char*, // view - typename elfcpp::Elf_types::Elf_Addr, // view_address - off_t) // view_size - { abort(); } + scan_relocs(const General_options& options, + Symbol_table* symtab, + Sized_object* object, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + size_t local_symbol_count, + const unsigned char* plocal_symbols, + Symbol** global_symbols) = 0; + + // Relocate section data. SH_TYPE is the type of the relocation + // section, SHT_REL or SHT_RELA. PRELOCS points to the relocation + // information. RELOC_COUNT is the number of relocs. VIEW is a + // view into the output file holding the section contents, + // VIEW_ADDRESS is the virtual address of the view, and VIEW_SIZE is + // the size of the view. + virtual void + relocate_section(const Relocate_info*, + unsigned int sh_type, + const unsigned char* prelocs, + size_t reloc_count, + unsigned char* view, + typename elfcpp::Elf_types::Elf_Addr view_address, + off_t view_size) = 0; protected: Sized_target(const Target::Target_info* pti) diff --git a/gold/workqueue.cc b/gold/workqueue.cc index 716f93db78..3ef3422573 100644 --- a/gold/workqueue.cc +++ b/gold/workqueue.cc @@ -192,6 +192,8 @@ Workqueue::~Workqueue() assert(this->running_ == 0); } +// Add a task to the queue. + void Workqueue::queue(Task* t) { @@ -199,6 +201,15 @@ Workqueue::queue(Task* t) this->tasks_.push_back(t); } +// Add a task to the front of the queue. + +void +Workqueue::queue_front(Task* t) +{ + Hold_lock hl(this->tasks_lock_); + this->tasks_.push_front(t); +} + // Clear the list of completed tasks. Return whether we cleared // anything. The completed_lock_ must be held when this is called. diff --git a/gold/workqueue.h b/gold/workqueue.h index a97d86d4c6..5cce2d56cc 100644 --- a/gold/workqueue.h +++ b/gold/workqueue.h @@ -288,6 +288,58 @@ class Task run(Workqueue*) = 0; }; +// A simple task which waits for a blocker and then runs a function. + +class Task_function_runner +{ + public: + virtual ~Task_function_runner() + { } + + virtual void + run(Workqueue*) = 0; +}; + +class Task_function : public Task +{ + public: + // Both points should be allocated using new, and will be deleted + // after the task runs. + Task_function(Task_function_runner* runner, Task_token* blocker) + : runner_(runner), blocker_(blocker) + { } + + ~Task_function() + { + delete this->runner_; + delete this->blocker_; + } + + // The standard task methods. + + // Wait until the task is unblocked. + Is_runnable_type + is_runnable(Workqueue*) + { return this->blocker_->is_blocked() ? IS_BLOCKED : IS_RUNNABLE; } + + // This type of task does not normally hold any locks. + virtual Task_locker* + locks(Workqueue*) + { return NULL; } + + // Run the action. + void + run(Workqueue* workqueue) + { this->runner_->run(workqueue); } + + private: + Task_function(const Task_function&); + Task_function& operator=(const Task_function&); + + Task_function_runner* runner_; + Task_token* blocker_; +}; + // The workqueue class Workqueue_runner; @@ -302,6 +354,11 @@ class Workqueue void queue(Task*); + // Add a new task to the front of the work queue. It will be the + // next task to run if it is ready. + void + queue_front(Task*); + // Process all the tasks on the work queue. void process();