From 4aebb6312eb5dcd12f2f8420028547584b708907 Mon Sep 17 00:00:00 2001 From: Rahul Chaudhry Date: Wed, 15 Feb 2017 00:37:10 -0800 Subject: Improved support for --icf=safe when used with -pie. gold/ * x86_64.cc (Target_x86_64::do_can_check_for_function_pointers): Return true even when building pie binaries. (Target_x86_64::possible_function_pointer_reloc): Check opcode for R_X86_64_PC32 relocations. (Target_x86_64::local_reloc_may_be_function_pointer): Pass extra arguments to local_reloc_may_be_function_pointer. (Target_x86_64::global_reloc_may_be_function_pointer): Likewise. * gc.h (gc_process_relocs): Add check for STT_FUNC. * testsuite/Makefile.am (icf_safe_pie_test): New test case. * testsuite/Makefile.in: Regenerate. * testsuite/icf_safe_pie_test.sh: New shell script. --- gold/x86_64.cc | 85 ++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 68 insertions(+), 17 deletions(-) (limited to 'gold/x86_64.cc') diff --git a/gold/x86_64.cc b/gold/x86_64.cc index d21c268..7f1742d 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -729,10 +729,13 @@ class Target_x86_64 : public Sized_target // 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. + // pie binaries, safe ICF cannot be done by looking at only relocation + // types, and for certain cases (e.g. R_X86_64_PC32), the instruction + // opcode is checked as well to distinguish a function call from taking + // a function's pointer. bool do_can_check_for_function_pointers() const - { return !parameters->options().pie(); } + { return true; } // Return the base for a DW_EH_PE_datarel encoding. uint64_t @@ -924,7 +927,10 @@ class Target_x86_64 : public Sized_target check_non_pic(Relobj*, unsigned int r_type, Symbol*); inline bool - possible_function_pointer_reloc(unsigned int r_type); + possible_function_pointer_reloc(Sized_relobj_file* src_obj, + unsigned int src_indx, + unsigned int r_offset, + unsigned int r_type); bool reloc_needs_plt_for_ifunc(Sized_relobj_file*, @@ -3277,7 +3283,11 @@ Target_x86_64::Scan::unsupported_reloc_global( // Returns true if this relocation type could be that of a function pointer. template inline bool -Target_x86_64::Scan::possible_function_pointer_reloc(unsigned int r_type) +Target_x86_64::Scan::possible_function_pointer_reloc( + Sized_relobj_file* src_obj, + unsigned int src_indx, + unsigned int r_offset, + unsigned int r_type) { switch (r_type) { @@ -3296,6 +3306,41 @@ Target_x86_64::Scan::possible_function_pointer_reloc(unsigned int r_type) { return true; } + case elfcpp::R_X86_64_PC32: + { + // This relocation may be used both for function calls and + // for taking address of a function. We distinguish between + // them by checking the opcodes. + uint64_t sh_flags = src_obj->section_flags(src_indx); + bool is_executable = (sh_flags & elfcpp::SHF_EXECINSTR) != 0; + if (is_executable) + { + section_size_type stype; + const unsigned char* view = src_obj->section_contents(src_indx, + &stype, + true); + + // call + if (r_offset >= 1 + && view[r_offset - 1] == 0xe8) + return false; + + // jmp + if (r_offset >= 1 + && view[r_offset - 1] == 0xe9) + return false; + + // jo/jno/jb/jnb/je/jne/jna/ja/js/jns/jp/jnp/jl/jge/jle/jg + if (r_offset >= 2 + && view[r_offset - 2] == 0x0f + && view[r_offset - 1] >= 0x80 + && view[r_offset - 1] <= 0x8f) + return false; + } + + // Be conservative and treat all others as function pointers. + return true; + } } return false; } @@ -3310,18 +3355,21 @@ Target_x86_64::Scan::local_reloc_may_be_function_pointer( Symbol_table* , Layout* , Target_x86_64* , - Sized_relobj_file* , - unsigned int , + Sized_relobj_file* src_obj, + unsigned int src_indx, Output_section* , - const elfcpp::Rela& , + const elfcpp::Rela& reloc, unsigned int r_type, const elfcpp::Sym&) { // 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)); + if (parameters->options().shared()) + return true; + + return possible_function_pointer_reloc(src_obj, src_indx, + reloc.get_r_offset(), r_type); } // For safe ICF, scan a relocation for a global symbol to check if it @@ -3334,20 +3382,23 @@ Target_x86_64::Scan::global_reloc_may_be_function_pointer( Symbol_table*, Layout* , Target_x86_64* , - Sized_relobj_file* , - unsigned int , + Sized_relobj_file* src_obj, + unsigned int src_indx, Output_section* , - const elfcpp::Rela& , + const elfcpp::Rela& reloc, 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)); + if (parameters->options().shared() + && (gsym->visibility() == elfcpp::STV_INTERNAL + || gsym->visibility() == elfcpp::STV_PROTECTED + || gsym->visibility() == elfcpp::STV_HIDDEN)) + return true; + + return possible_function_pointer_reloc(src_obj, src_indx, + reloc.get_r_offset(), r_type); } // Scan a relocation for a global symbol. -- cgit v1.1