From 21bb3914d64c5fb946423572fe895d2a08531030 Mon Sep 17 00:00:00 2001 From: Sriraman Tallam Date: Sat, 13 Feb 2010 02:04:21 +0000 Subject: [PATCH] 2010-02-12 Sriraman Tallam * arm.cc (Scan::local_reloc_may_be_function_pointer): New function. (Scan::global_reloc_may_be_function_pointer): New function. * sparc.cc (Scan::local_reloc_may_be_function_pointer): New function. (Scan::global_reloc_may_be_function_pointer): New function. * powerpc.cc (Scan::local_reloc_may_be_function_pointer): New function. (Scan::global_reloc_may_be_function_pointer): New function. * i386.cc (Scan::local_reloc_may_be_function_pointer): New function. (Scan::global_reloc_may_be_function_pointer): New function. * x86_64.cc (Scan::local_reloc_may_be_function_pointer): New function. (Scan::global_reloc_may_be_function_pointer): New function. (Scan::possible_function_pointer_reloc): New function. (Target_x86_64::can_check_for_function_pointers): New function. * gc.h (gc_process_relocs): Scan relocation types to determine if function pointers were taken for targets that support it. * icf.cc (Icf::find_identical_sections): Include functions for folding in safe ICF whose pointer is not taken. * icf.h (Secn_fptr_taken_set): New typedef. (fptr_section_id_): New member. (section_has_function_pointers): New function. (set_section_has_function_pointers): New function. (check_section_for_function_pointers): New function. * options.h: Fix comment for safe ICF option. * target.h (can_check_for_function_pointers): New function. * testsuite/Makefile.am: Add icf_safe_so_test test case. Modify icf_safe_test for X86-64. * testsuite/Makefile.in: Regenerate. * testsuite/icf_safe_so_test.cc: New file. * testsuite/icf_safe_so_test.sh: New file. * testsuite/icf_safe_test.cc (kept_func_3): New function. (main): Change to take pointer to function kept_func_3. * testsuite/icf_safe_test.sh (arch_specific_safe_fold): Check if safe folding is done correctly for X86-64. --- gold/ChangeLog | 35 ++++++++++ gold/arm.cc | 19 ++++++ gold/gc.h | 46 +++++++++++-- gold/i386.cc | 20 ++++++ gold/icf.cc | 43 ++++++++++-- gold/icf.h | 38 ++++++++++- gold/options.h | 3 +- gold/powerpc.cc | 22 ++++++ gold/sparc.cc | 23 +++++++ gold/target.h | 7 ++ gold/testsuite/Makefile.am | 22 ++++-- gold/testsuite/Makefile.in | 22 ++++-- gold/testsuite/icf_safe_so_test.cc | 73 ++++++++++++++++++++ gold/testsuite/icf_safe_so_test.sh | 69 +++++++++++++++++++ gold/testsuite/icf_safe_test.cc | 13 +++- gold/testsuite/icf_safe_test.sh | 21 +++++- gold/x86_64.cc | 104 +++++++++++++++++++++++++++++ 17 files changed, 556 insertions(+), 24 deletions(-) create mode 100644 gold/testsuite/icf_safe_so_test.cc create mode 100755 gold/testsuite/icf_safe_so_test.sh diff --git a/gold/ChangeLog b/gold/ChangeLog index 936d0b9e36..eb98592a2d 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,38 @@ +2010-02-12 Sriraman Tallam + + * arm.cc (Scan::local_reloc_may_be_function_pointer): New function. + (Scan::global_reloc_may_be_function_pointer): New function. + * sparc.cc (Scan::local_reloc_may_be_function_pointer): New function. + (Scan::global_reloc_may_be_function_pointer): New function. + * powerpc.cc (Scan::local_reloc_may_be_function_pointer): New function. + (Scan::global_reloc_may_be_function_pointer): New function. + * i386.cc (Scan::local_reloc_may_be_function_pointer): New function. + (Scan::global_reloc_may_be_function_pointer): New function. + * x86_64.cc (Scan::local_reloc_may_be_function_pointer): New function. + (Scan::global_reloc_may_be_function_pointer): New function. + (Scan::possible_function_pointer_reloc): New function. + (Target_x86_64::can_check_for_function_pointers): New function. + * gc.h (gc_process_relocs): Scan relocation types to determine if + function pointers were taken for targets that support it. + * icf.cc (Icf::find_identical_sections): Include functions for + folding in safe ICF whose pointer is not taken. + * icf.h (Secn_fptr_taken_set): New typedef. + (fptr_section_id_): New member. + (section_has_function_pointers): New function. + (set_section_has_function_pointers): New function. + (check_section_for_function_pointers): New function. + * options.h: Fix comment for safe ICF option. + * target.h (can_check_for_function_pointers): New function. + * testsuite/Makefile.am: Add icf_safe_so_test test case. + Modify icf_safe_test for X86-64. + * testsuite/Makefile.in: Regenerate. + * testsuite/icf_safe_so_test.cc: New file. + * testsuite/icf_safe_so_test.sh: New file. + * testsuite/icf_safe_test.cc (kept_func_3): New function. + (main): Change to take pointer to function kept_func_3. + * testsuite/icf_safe_test.sh (arch_specific_safe_fold): Check if safe + folding is done correctly for X86-64. + 2010-02-12 David S. Miller * output.h (Output_reloc::Output_reloc): Add diff --git a/gold/arm.cc b/gold/arm.cc index d40ab2c484..bdbdbd19ea 100644 --- a/gold/arm.cc +++ b/gold/arm.cc @@ -2171,6 +2171,25 @@ class Target_arm : public Sized_target<32, big_endian> const elfcpp::Rel<32, big_endian>& reloc, unsigned int r_type, Symbol* gsym); + inline bool + local_reloc_may_be_function_pointer(Symbol_table* , Layout* , Target_arm* , + Sized_relobj<32, big_endian>* , + unsigned int , + Output_section* , + const elfcpp::Rel<32, big_endian>& , + unsigned int , + const elfcpp::Sym<32, big_endian>&) + { return false; } + + inline bool + global_reloc_may_be_function_pointer(Symbol_table* , Layout* , Target_arm* , + Sized_relobj<32, big_endian>* , + unsigned int , + Output_section* , + const elfcpp::Rel<32, big_endian>& , + unsigned int , Symbol*) + { return false; } + private: static void unsupported_reloc_local(Sized_relobj<32, big_endian>*, diff --git a/gold/gc.h b/gold/gc.h index 1ffb590fb7..d4fd02e3bd 100644 --- a/gold/gc.h +++ b/gold/gc.h @@ -163,18 +163,19 @@ inline void gc_process_relocs( Symbol_table* symtab, Layout*, - Target_type* , + Target_type* target, Sized_relobj* src_obj, unsigned int src_indx, const unsigned char* prelocs, size_t reloc_count, Output_section*, - bool , + bool, size_t local_count, const unsigned char* plocal_syms) { Object *dst_obj; unsigned int dst_indx; + Scan scan; typedef typename Reloc_types::Reloc Reltype; const int reloc_size = Reloc_types::reloc_size; @@ -186,8 +187,14 @@ gc_process_relocs( bool is_icf_tracked = false; const char* cident_section_name = NULL; + std::string src_section_name = (parameters->options().icf_enabled() + ? src_obj->section_name(src_indx) + : ""); + + bool check_section_for_function_pointers = false; + if (parameters->options().icf_enabled() - && is_section_foldable_candidate(src_obj->section_name(src_indx).c_str())) + && is_section_foldable_candidate(src_section_name.c_str())) { is_icf_tracked = true; Section_id src_id(src_obj, src_indx); @@ -196,11 +203,16 @@ gc_process_relocs( addendvec = &symtab->icf()->addend_reloc_list()[src_id]; } + check_section_for_function_pointers = + symtab->icf()->check_section_for_function_pointers(src_section_name, + target); + 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); typename elfcpp::Elf_types::Elf_Swxword addend = Reloc_types::get_reloc_addend_noerror(&reloc); @@ -225,6 +237,18 @@ gc_process_relocs( (*addendvec).push_back(std::make_pair(symvalue, static_cast(addend))); } + + // When doing safe folding, check to see if this relocation is that + // of a function pointer being taken. + if (check_section_for_function_pointers + && lsym.get_st_type() != elfcpp::STT_OBJECT + && scan.local_reloc_may_be_function_pointer(symtab, NULL, NULL, + src_obj, src_indx, + NULL, reloc, r_type, + lsym)) + symtab->icf()->set_section_has_function_pointers( + src_obj, lsym.get_st_shndx()); + if (shndx == src_indx) continue; } @@ -239,9 +263,21 @@ gc_process_relocs( bool is_ordinary; dst_obj = gsym->object(); dst_indx = gsym->shndx(&is_ordinary); + Section_id dst_id(dst_obj, dst_indx); + + // When doing safe folding, check to see if this relocation is that + // of a function pointer being taken. + if (check_section_for_function_pointers + && gsym->type() != elfcpp::STT_OBJECT + && (!is_ordinary + || scan.global_reloc_may_be_function_pointer( + symtab, NULL, NULL, src_obj, src_indx, NULL, reloc, + r_type, gsym))) + symtab->icf()->set_section_has_function_pointers(dst_obj, dst_indx); + if (!is_ordinary) continue; - Section_id dst_id(dst_obj, dst_indx); + // If the symbol name matches '__start_XXX' then the section with // the C identifier like name 'XXX' should not be garbage collected. // A similar treatment to symbols with the name '__stop_XXX'. @@ -265,7 +301,7 @@ gc_process_relocs( static_cast(sized_gsym->value()); (*addendvec).push_back(std::make_pair(symvalue, static_cast(addend))); - } + } } if (parameters->options().gc_sections()) { diff --git a/gold/i386.cc b/gold/i386.cc index 97d98d724a..eb4ef0abc7 100644 --- a/gold/i386.cc +++ b/gold/i386.cc @@ -202,6 +202,26 @@ class Target_i386 : public Target_freebsd<32, false> const elfcpp::Rel<32, false>& reloc, unsigned int r_type, Symbol* gsym); + inline bool + local_reloc_may_be_function_pointer(Symbol_table* , Layout* , Target_i386* , + Sized_relobj<32, false>* , + unsigned int , + Output_section* , + const elfcpp::Rel<32, false>& , + unsigned int , + const elfcpp::Sym<32, false>&) + { return false; } + + inline bool + global_reloc_may_be_function_pointer(Symbol_table* , Layout* , + Target_i386* , + Sized_relobj<32, false>* , + unsigned int , + Output_section* , + const elfcpp::Rel<32, false>& , + unsigned int , Symbol*) + { return false; } + static void unsupported_reloc_local(Sized_relobj<32, false>*, unsigned int r_type); diff --git a/gold/icf.cc b/gold/icf.cc index 48cb9bfe70..ec3269c055 100644 --- a/gold/icf.cc +++ b/gold/icf.cc @@ -101,6 +101,36 @@ // when folding takes place. This could lead to unexpected run-time // behaviour. // +// Safe Folding : +// ------------ +// +// ICF in safe mode folds only ctors and dtors if their function pointers can +// never be taken. Also, for X86-64, safe folding uses the relocation +// type to determine if a function's pointer is taken or not and only folds +// functions whose pointers are definitely not taken. +// +// Caveat with safe folding : +// ------------------------ +// +// This applies only to x86_64. +// +// Position independent executables are created from PIC objects (compiled +// with -fPIC) and/or PIE objects (compiled with -fPIE). For PIE objects, the +// relocation types for function pointer taken and a call are the same. +// Now, it is not always possible to tell if an object used in the link of +// a pie executable is a PIC object or a PIE object. Hence, for pie +// executables, using relocation types to disambiguate function pointers is +// currently disabled. +// +// Further, it is not correct to use safe folding to build non-pie +// executables using PIC/PIE objects. PIC/PIE objects have different +// relocation types for function pointers than non-PIC objects, and the +// current implementation of safe folding does not handle those relocation +// types. Hence, if used, functions whose pointers are taken could still be +// folded causing unpredictable run-time behaviour if the pointers were used +// in comparisons. +// +// // // How to run : --icf=[safe|all|none] // Optional parameters : --icf-iterations --print-icf-sections @@ -560,6 +590,7 @@ Icf::find_identical_sections(const Input_objects* input_objects, std::vector num_tracked_relocs; std::vector is_secn_or_group_unique; std::vector section_contents; + const Target& target = parameters->target(); // Decide which sections are possible candidates first. @@ -577,14 +608,18 @@ Icf::find_identical_sections(const Input_objects* input_objects, if (parameters->options().gc_sections() && symtab->gc()->is_section_garbage(*p, i)) continue; + const char* mangled_func_name = strrchr(section_name, '.'); + gold_assert(mangled_func_name != NULL); // With --icf=safe, check if the mangled function name is a ctor // or a dtor. The mangled function name can be obtained from the // section name by stripping the section prefix. - const char* mangled_func_name = strrchr(section_name, '.'); - gold_assert(mangled_func_name != NULL); if (parameters->options().icf_safe_folding() - && !is_function_ctor_or_dtor(mangled_func_name + 1)) - continue; + && !is_function_ctor_or_dtor(mangled_func_name + 1) + && (!target.can_check_for_function_pointers() + || section_has_function_pointers(*p, i))) + { + continue; + } this->id_section_.push_back(Section_id(*p, i)); this->section_id_[Section_id(*p, i)] = section_num; this->kept_section_id_.push_back(section_num); diff --git a/gold/icf.h b/gold/icf.h index e336572105..76e491c094 100644 --- a/gold/icf.h +++ b/gold/icf.h @@ -50,9 +50,11 @@ class Icf typedef Unordered_map Uniq_secn_id_map; + typedef Unordered_set Secn_fptr_taken_set; Icf() : id_section_(), section_id_(), kept_section_id_(), + fptr_section_id_(), num_tracked_relocs(NULL), icf_ready_(false), section_reloc_list_(), symbol_reloc_list_(), addend_reloc_list_() @@ -88,7 +90,37 @@ class Icf // given section. bool is_section_folded(Object* obj, unsigned int shndx); - + + // Given an object and a section index, this returns true if the + // pointer of the function defined in this section is taken. + bool + section_has_function_pointers(Object *obj, unsigned int shndx) + { + return (this->fptr_section_id_.find(Section_id(obj, shndx)) + != this->fptr_section_id_.end()); + } + + // Records that a pointer of the function defined in this section + // is taken. + void + set_section_has_function_pointers(Object *obj, unsigned int shndx) + { + this->fptr_section_id_.insert(Section_id(obj, shndx)); + } + + // Checks if the section_name should be searched for relocs + // corresponding to taken function pointers. Ignores eh_frame + // and vtable sections. + inline bool + check_section_for_function_pointers(std::string section_name, + Target* target) + { + return (parameters->options().icf_safe_folding() + && target->can_check_for_function_pointers() + && !is_prefix_of(".rodata._ZTV", section_name.c_str()) + && !is_prefix_of(".eh_frame", section_name.c_str())); + } + // Returns a map of a section to a list of all sections referenced // by its relocations. Section_list& @@ -122,6 +154,10 @@ class Icf // section. If the id's are the same then this section is // not folded. std::vector kept_section_id_; + // Given a section id, this says if the pointer to this + // function is taken in which case it is dangerous to fold + // this function. + Secn_fptr_taken_set fptr_section_id_; unsigned int* num_tracked_relocs; // Flag to indicate if ICF has been run. bool icf_ready_; diff --git a/gold/options.h b/gold/options.h index 22662dbbdf..fee76dc853 100644 --- a/gold/options.h +++ b/gold/options.h @@ -914,7 +914,8 @@ class General_options DEFINE_enum(icf, options::TWO_DASHES, '\0', "none", N_("Identical Code Folding. " - "\'--icf=safe\' folds only ctors and dtors."), + "\'--icf=safe\' Folds ctors, dtors and functions whose" + " pointers are definitely not taken."), ("[none,all,safe]"), {"none", "all", "safe"}); diff --git a/gold/powerpc.cc b/gold/powerpc.cc index cc4678229e..0eda0a9edf 100644 --- a/gold/powerpc.cc +++ b/gold/powerpc.cc @@ -183,6 +183,28 @@ class Target_powerpc : public Sized_target const elfcpp::Rela& reloc, unsigned int r_type, Symbol* gsym); + inline bool + local_reloc_may_be_function_pointer(Symbol_table* , Layout* , + Target_powerpc* , + Sized_relobj* , + unsigned int , + Output_section* , + const elfcpp::Rela& , + unsigned int , + const elfcpp::Sym&) + { return false; } + + inline bool + global_reloc_may_be_function_pointer(Symbol_table* , Layout* , + Target_powerpc* , + Sized_relobj* , + unsigned int , + Output_section* , + const elfcpp::Rela& , + unsigned int , Symbol*) + { return false; } + private: static void unsupported_reloc_local(Sized_relobj*, diff --git a/gold/sparc.cc b/gold/sparc.cc index ab25203cc8..5355c7beaf 100644 --- a/gold/sparc.cc +++ b/gold/sparc.cc @@ -193,6 +193,29 @@ class Target_sparc : public Sized_target const elfcpp::Rela& reloc, unsigned int r_type, Symbol* gsym); + inline bool + local_reloc_may_be_function_pointer(Symbol_table* , Layout* , + Target_sparc* , + Sized_relobj* , + unsigned int , + Output_section* , + const elfcpp::Rela& , + unsigned int , + const elfcpp::Sym&) + { return false; } + + inline bool + global_reloc_may_be_function_pointer(Symbol_table* , Layout* , + Target_sparc* , + Sized_relobj* , + unsigned int , + Output_section* , + const elfcpp::Rela& , + unsigned int , Symbol*) + { return false; } + + private: static void unsupported_reloc_local(Sized_relobj*, diff --git a/gold/target.h b/gold/target.h index 20de7a4947..673153ff93 100644 --- a/gold/target.h +++ b/gold/target.h @@ -64,6 +64,13 @@ class Target virtual ~Target() { } + // Virtual function which is set to return true by a target if + // it can use relocation types to determine if a function's + // pointer is taken. + virtual bool + can_check_for_function_pointers() const + { return false; } + // Return the bit size that this target implements. This should // return 32 or 64. int diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am index a1889bb277..3797a9219a 100644 --- a/gold/testsuite/Makefile.am +++ b/gold/testsuite/Makefile.am @@ -170,14 +170,28 @@ icf_keep_unique_test.stdout: icf_keep_unique_test $(TEST_NM) -C icf_keep_unique_test > icf_keep_unique_test.stdout check_SCRIPTS += icf_safe_test.sh -check_DATA += icf_safe_test.stdout +check_DATA += icf_safe_test_1.stdout icf_safe_test_2.stdout MOSTLYCLEANFILES += icf_safe_test -icf_safe_test.o: icf_safe_test.cc +icf_safe_test.o: icf_safe_test.cc $(CXXCOMPILE) -O0 -c -ffunction-sections -g -o $@ $< icf_safe_test: icf_safe_test.o gcctestdir/ld $(CXXLINK) -Bgcctestdir/ -Wl,--icf=safe icf_safe_test.o -icf_safe_test.stdout: icf_safe_test - $(TEST_NM) icf_safe_test > icf_safe_test.stdout +icf_safe_test_1.stdout: icf_safe_test + $(TEST_NM) icf_safe_test > icf_safe_test_1.stdout +icf_safe_test_2.stdout: icf_safe_test + $(TEST_READELF) -h icf_safe_test > icf_safe_test_2.stdout + +check_SCRIPTS += icf_safe_so_test.sh +check_DATA += icf_safe_so_test_1.stdout icf_safe_so_test_2.stdout +MOSTLYCLEANFILES += icf_safe_so_test +icf_safe_so_test.o: icf_safe_so_test.cc + $(CXXCOMPILE) -O0 -c -ffunction-sections -fPIC -g -o $@ $< +icf_safe_so_test: icf_safe_so_test.o gcctestdir/ld + $(CXXLINK) -Bgcctestdir/ -Wl,--icf=safe icf_safe_so_test.o -fPIC -shared +icf_safe_so_test_1.stdout: icf_safe_so_test + $(TEST_NM) icf_safe_so_test > icf_safe_so_test_1.stdout +icf_safe_so_test_2.stdout: icf_safe_so_test + $(TEST_READELF) -h icf_safe_so_test > icf_safe_so_test_2.stdout check_PROGRAMS += basic_test check_PROGRAMS += basic_static_test diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in index e5fd65f9b9..e3c356a7af 100644 --- a/gold/testsuite/Makefile.in +++ b/gold/testsuite/Makefile.in @@ -61,6 +61,7 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_test.sh \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_keep_unique_test.sh \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_safe_test.sh \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_safe_so_test.sh \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_shared.sh weak_plt.sh \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ debug_msg.sh undef_symbol.sh \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ ver_test_1.sh ver_test_2.sh \ @@ -86,7 +87,10 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ gc_orphan_section_test.stdout \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_test.stdout \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_keep_unique_test.stdout \ -@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_safe_test.stdout \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_safe_test_1.stdout \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_safe_test_2.stdout \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_safe_so_test_1.stdout \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_safe_so_test_2.stdout \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_shared.dbg \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ weak_plt_shared.so debug_msg.err \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ debug_msg_so.err \ @@ -107,7 +111,7 @@ check_PROGRAMS = object_unittest$(EXEEXT) binary_unittest$(EXEEXT) \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ gc_comdat_test gc_tls_test \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ gc_orphan_section_test icf_test \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_keep_unique_test \ -@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_safe_test \ +@GCC_TRUE@@NATIVE_LINKER_TRUE@ icf_safe_test icf_safe_so_test \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ two_file_shared.dbg \ @GCC_TRUE@@NATIVE_LINKER_TRUE@ alt/weak_undef_lib.so @GCC_TRUE@@NATIVE_LINKER_TRUE@am__append_4 = basic_test \ @@ -2648,8 +2652,18 @@ uninstall-am: @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -O0 -c -ffunction-sections -g -o $@ $< @GCC_TRUE@@NATIVE_LINKER_TRUE@icf_safe_test: icf_safe_test.o gcctestdir/ld @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -Wl,--icf=safe icf_safe_test.o -@GCC_TRUE@@NATIVE_LINKER_TRUE@icf_safe_test.stdout: icf_safe_test -@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_NM) icf_safe_test > icf_safe_test.stdout +@GCC_TRUE@@NATIVE_LINKER_TRUE@icf_safe_test_1.stdout: icf_safe_test +@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_NM) icf_safe_test > icf_safe_test_1.stdout +@GCC_TRUE@@NATIVE_LINKER_TRUE@icf_safe_test_2.stdout: icf_safe_test +@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_READELF) -h icf_safe_test > icf_safe_test_2.stdout +@GCC_TRUE@@NATIVE_LINKER_TRUE@icf_safe_so_test.o: icf_safe_so_test.cc +@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -O0 -c -ffunction-sections -fPIC -g -o $@ $< +@GCC_TRUE@@NATIVE_LINKER_TRUE@icf_safe_so_test: icf_safe_so_test.o gcctestdir/ld +@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXLINK) -Bgcctestdir/ -Wl,--icf=safe icf_safe_so_test.o -fPIC -shared +@GCC_TRUE@@NATIVE_LINKER_TRUE@icf_safe_so_test_1.stdout: icf_safe_so_test +@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_NM) icf_safe_so_test > icf_safe_so_test_1.stdout +@GCC_TRUE@@NATIVE_LINKER_TRUE@icf_safe_so_test_2.stdout: icf_safe_so_test +@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_READELF) -h icf_safe_so_test > icf_safe_so_test_2.stdout @GCC_TRUE@@NATIVE_LINKER_TRUE@basic_test.o: basic_test.cc @GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -O0 -c -o $@ $< @GCC_TRUE@@NATIVE_LINKER_TRUE@basic_test: basic_test.o gcctestdir/ld diff --git a/gold/testsuite/icf_safe_so_test.cc b/gold/testsuite/icf_safe_so_test.cc new file mode 100644 index 0000000000..5e4453b75e --- /dev/null +++ b/gold/testsuite/icf_safe_so_test.cc @@ -0,0 +1,73 @@ +// icf_safe_so_test.cc -- a test case for gold + +// Copyright 2010 Free Software Foundation, Inc. +// Written by Sriraman Tallam . + +// This file is part of gold. + +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +// MA 02110-1301, USA. + +// The goal of this program is to verify if identical code folding +// in safe mode correctly folds functions in a shared object. The +// foo_* functions below should not be folded. For x86-64, +// foo_glob and bar_glob should be folded as their function pointers +// are addresses of PLT entries in shared objects. + +int __attribute__ ((visibility ("protected"))) +foo_prot() +{ + return 1; +} + +int __attribute__ ((visibility ("hidden"))) +foo_hidden() +{ + return 1; +} + +int __attribute__ ((visibility ("internal"))) +foo_internal() +{ + return 1; +} + +static int +foo_static() +{ + return 1; +} + +int foo_glob() +{ + return 2; +} + +int bar_glob() +{ + return 2; +} + +int main() +{ + int (*p)() = foo_glob; + (void)p; + foo_static(); + foo_prot(); + foo_hidden(); + foo_internal(); + return 0; +} + diff --git a/gold/testsuite/icf_safe_so_test.sh b/gold/testsuite/icf_safe_so_test.sh new file mode 100755 index 0000000000..db2e73ebc1 --- /dev/null +++ b/gold/testsuite/icf_safe_so_test.sh @@ -0,0 +1,69 @@ +# icf_safe_so_test.sh -- test --icf=safe + +# Copyright 2010 Free Software Foundation, Inc. +# Written by Sriraman Tallam . + +# This file is part of gold. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, +# MA 02110-1301, USA. + +# The goal of this program is to verify if --icf=safe works as expected. +# File icf_safe_so_test.cc is in this test. The goal of this script is +# to verify if identical code folding in safe mode correctly folds +# functions in a shared object. + +check_nofold() +{ + func_addr_1=`grep $2 $1 | awk '{print $1}'` + func_addr_2=`grep $3 $1 | awk '{print $1}'` + if [ $func_addr_1 = $func_addr_2 ]; + then + echo "Safe Identical Code Folding folded" $2 "and" $3 + exit 1 + fi +} + +check_fold() +{ + func_addr_1=`grep $2 $1 | awk '{print $1}'` + func_addr_2=`grep $3 $1 | awk '{print $1}'` + if [ $func_addr_1 != $func_addr_2 ]; + then + echo "Safe Identical Code Folding did not fold " $2 "and" $3 + exit 1 + fi +} + +arch_specific_safe_fold() +{ + grep_x86_64=`grep -q "Advanced Micro Devices X86-64" $2` + if [ $? == 0 ]; + then + check_fold $1 $3 $4 + else + check_nofold $1 $3 $4 + fi +} + +check_nofold icf_safe_so_test_1.stdout "foo_prot" "foo_hidden" +check_nofold icf_safe_so_test_1.stdout "foo_prot" "foo_internal" +check_nofold icf_safe_so_test_1.stdout "foo_prot" "foo_static" +check_nofold icf_safe_so_test_1.stdout "foo_hidden" "foo_internal" +check_nofold icf_safe_so_test_1.stdout "foo_hidden" "foo_static" +check_nofold icf_safe_so_test_1.stdout "foo_internal" "foo_static" +arch_specific_safe_fold icf_safe_so_test_1.stdout icf_safe_so_test_2.stdout \ + "foo_glob" "bar_glob" + diff --git a/gold/testsuite/icf_safe_test.cc b/gold/testsuite/icf_safe_test.cc index 9e8a36946b..3edb86576a 100644 --- a/gold/testsuite/icf_safe_test.cc +++ b/gold/testsuite/icf_safe_test.cc @@ -22,8 +22,10 @@ // The goal of this program is to verify if identical code folding // in safe mode correctly folds only ctors and dtors. kept_func_1 must -// not be folded into kept_func_2. The ctor and dtor of class A must -// be folded. +// not be folded into kept_func_2 other than for X86-64 which can +// use relocation types to determine if function pointers are taken. +// kept_func_3 should never be folded as its pointer is taken. The ctor +// and dtor of class A must be folded. class A { @@ -48,7 +50,14 @@ int kept_func_2() return 1; } +int kept_func_3() +{ + return 1; +} + int main() { + int (*p)() = kept_func_3; + p(); return 0; } diff --git a/gold/testsuite/icf_safe_test.sh b/gold/testsuite/icf_safe_test.sh index f77559ce22..540dcc2685 100755 --- a/gold/testsuite/icf_safe_test.sh +++ b/gold/testsuite/icf_safe_test.sh @@ -22,7 +22,8 @@ # The goal of this program is to verify if --icf=safe works as expected. # File icf_safe_test.cc is in this test. This program checks if only -# ctors and dtors are folded. +# ctors and dtors are folded, except for x86-64, which uses relocation +# types to detect if function pointers are taken. check_nofold() { @@ -46,5 +47,19 @@ check_fold() fi } -check_nofold icf_safe_test.stdout "kept_func_1" "kept_func_2" -check_fold icf_safe_test.stdout "_ZN1AD1Ev" "_ZN1AC1Ev" +arch_specific_safe_fold() +{ + grep_x86_64=`grep -q "Advanced Micro Devices X86-64" $2` + if [ $? == 0 ]; + then + check_fold $1 $3 $4 + else + check_nofold $1 $3 $4 + fi +} + +arch_specific_safe_fold icf_safe_test_1.stdout icf_safe_test_2.stdout \ + "kept_func_1" "kept_func_2" +check_fold icf_safe_test_1.stdout "_ZN1AD1Ev" "_ZN1AC1Ev" +check_nofold icf_safe_test_1.stdout "kept_func_3" "kept_func_1" +check_nofold icf_safe_test_1.stdout "kept_func_3" "kept_func_2" diff --git a/gold/x86_64.cc b/gold/x86_64.cc index 802d4984e7..e9dd5ae5d5 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -39,6 +39,7 @@ #include "tls.h" #include "freebsd.h" #include "gc.h" +#include "icf.h" namespace { @@ -69,6 +70,16 @@ class Target_x86_64 : public Target_freebsd<64, false> tls_base_symbol_defined_(false) { } + // This function should be defined in targets that can use relocation + // types to determine (implemented in local_reloc_may_be_function_pointer + // and global_reloc_may_be_function_pointer) + // if a function's pointer is taken. ICF uses this in safe mode to only + // fold those functions whose pointer is defintely not taken. For x86_64 + // pie binaries, safe ICF cannot be done by looking at relocation types. + inline bool + can_check_for_function_pointers() const + { return !parameters->options().pie(); } + // Hook for a new output section. void do_new_output_section(Output_section*) const; @@ -224,6 +235,26 @@ class Target_x86_64 : public Target_freebsd<64, false> const elfcpp::Rela<64, false>& reloc, unsigned int r_type, Symbol* gsym); + inline bool + local_reloc_may_be_function_pointer(Symbol_table* symtab, Layout* layout, + Target_x86_64* target, + Sized_relobj<64, false>* object, + unsigned int data_shndx, + Output_section* output_section, + const elfcpp::Rela<64, false>& reloc, + unsigned int r_type, + const elfcpp::Sym<64, false>& lsym); + + inline bool + global_reloc_may_be_function_pointer(Symbol_table* symtab, Layout* layout, + Target_x86_64* target, + Sized_relobj<64, false>* object, + unsigned int data_shndx, + Output_section* output_section, + const elfcpp::Rela<64, false>& reloc, + unsigned int r_type, + Symbol* gsym); + private: static void unsupported_reloc_local(Sized_relobj<64, false>*, unsigned int r_type); @@ -235,6 +266,9 @@ class Target_x86_64 : public Target_freebsd<64, false> void check_non_pic(Relobj*, unsigned int r_type); + inline bool + possible_function_pointer_reloc(unsigned int r_type); + // Whether we have issued an error about a non-PIC compilation. bool issued_non_pic_error_; }; @@ -1364,6 +1398,76 @@ Target_x86_64::Scan::unsupported_reloc_global(Sized_relobj<64, false>* object, object->name().c_str(), r_type, gsym->demangled_name().c_str()); } +// Returns true if this relocation type could be that of a function pointer +// only if the target is not position-independent code. +inline bool +Target_x86_64::Scan::possible_function_pointer_reloc(unsigned int r_type) +{ + if (parameters->options().shared()) + return false; + + switch (r_type) + { + case elfcpp::R_X86_64_64: + case elfcpp::R_X86_64_32: + case elfcpp::R_X86_64_32S: + case elfcpp::R_X86_64_16: + case elfcpp::R_X86_64_8: + { + return true; + } + } + return false; +} + +// For safe ICF, scan a relocation for a local symbol to check if it +// corresponds to a function pointer being taken. In that case mark +// the function whose pointer was taken as not foldable. + +inline bool +Target_x86_64::Scan::local_reloc_may_be_function_pointer( + Symbol_table* , + Layout* , + Target_x86_64* , + Sized_relobj<64, false>* , + unsigned int , + Output_section* , + const elfcpp::Rela<64, false>& , + unsigned int r_type, + const elfcpp::Sym<64, false>&) +{ + // When building a shared library, do not fold any local symbols as it is + // not possible to distinguish pointer taken versus a call by looking at + // the relocation types. + return (parameters->options().shared() + || possible_function_pointer_reloc(r_type)); +} + +// For safe ICF, scan a relocation for a global symbol to check if it +// corresponds to a function pointer being taken. In that case mark +// the function whose pointer was taken as not foldable. + +inline bool +Target_x86_64::Scan::global_reloc_may_be_function_pointer( + Symbol_table*, + Layout* , + Target_x86_64* , + Sized_relobj<64, false>* , + unsigned int , + Output_section* , + const elfcpp::Rela<64, false>& , + unsigned int r_type, + Symbol* gsym) +{ + // When building a shared library, do not fold symbols whose visibility + // is hidden, internal or protected. + return ((parameters->options().shared() + && (gsym->visibility() == elfcpp::STV_INTERNAL + || gsym->visibility() == elfcpp::STV_PROTECTED + || gsym->visibility() == elfcpp::STV_HIDDEN)) + || possible_function_pointer_reloc(r_type)); +} + // Scan a relocation for a global symbol. inline void -- 2.34.1