diff options
Diffstat (limited to 'gold/x86_64.cc')
-rw-r--r-- | gold/x86_64.cc | 85 |
1 files changed, 68 insertions, 17 deletions
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<size, false> // 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<size, false> 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<size, false>* src_obj, + unsigned int src_indx, + unsigned int r_offset, + unsigned int r_type); bool reloc_needs_plt_for_ifunc(Sized_relobj_file<size, false>*, @@ -3277,7 +3283,11 @@ Target_x86_64<size>::Scan::unsupported_reloc_global( // Returns true if this relocation type could be that of a function pointer. template<int size> inline bool -Target_x86_64<size>::Scan::possible_function_pointer_reloc(unsigned int r_type) +Target_x86_64<size>::Scan::possible_function_pointer_reloc( + Sized_relobj_file<size, false>* src_obj, + unsigned int src_indx, + unsigned int r_offset, + unsigned int r_type) { switch (r_type) { @@ -3296,6 +3306,41 @@ Target_x86_64<size>::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<size>::Scan::local_reloc_may_be_function_pointer( Symbol_table* , Layout* , Target_x86_64<size>* , - Sized_relobj_file<size, false>* , - unsigned int , + Sized_relobj_file<size, false>* src_obj, + unsigned int src_indx, Output_section* , - const elfcpp::Rela<size, false>& , + const elfcpp::Rela<size, false>& reloc, unsigned int r_type, const elfcpp::Sym<size, 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)); + 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<size>::Scan::global_reloc_may_be_function_pointer( Symbol_table*, Layout* , Target_x86_64<size>* , - Sized_relobj_file<size, false>* , - unsigned int , + Sized_relobj_file<size, false>* src_obj, + unsigned int src_indx, Output_section* , - const elfcpp::Rela<size, false>& , + const elfcpp::Rela<size, false>& 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. |