From a2575bec2413d361c6ecfc4aecb2a31c86123f95 Mon Sep 17 00:00:00 2001 From: Cary Coutant Date: Fri, 22 Jun 2018 23:36:50 -0700 Subject: [PATCH] Update support for .note.gnu.property sections. The original patch did not give the target enough hooks to discover that an input object file does not have a particular property. For the GNU_PROPERTY_X86_FEATURE_1_AND property, for example, where a missing property should be assumed to be all zeroes, and ANDed with other object modules, this is essential. We now store the target-specific properties locally in the Target structure as native uint32_t fields, then AND the per-object feature bits with the program's feature bits when we're finished processing each input object file. The target-specific properties are then added back to the output note section during finalization. gold/ PR gold/22914 * layout.cc (read_sized_value): Fix spelling of section name. (Layout::layout_gnu_property): Call Sized_target::record_gnu_property for target-specific properties; don't store them with target-independent properties yet. (Layout::merge_gnu_properties): New method. (Layout::add_gnu_property): New method. (Layout::create_gnu_properties_note): Call target to finalize target-specific properties. Fix spelling of output section name. * layout.h (Layout::merge_gnu_properties): New method. (Layout::add_gnu_property): New method. * object.cc (Sized_relobj_file::do_layout): Call Layout::merge_gnu_properties. * target.h (Target::merge_gnu_property): Remove. (Target::finalize_gnu_properties): New method. (Target::do_merge_gnu_property): Move to Sized_target and rename. (Target::do_finalize_gnu_properties): New virtual method. (Sized_target::record_gnu_property): Moved and renamed from Target::do_merge_gnu_property. (Sized_target::merge_gnu_properties): New virtual method. * x86_64.cc (Target_x86_64::isa_1_used_, isa_1_needed_) (feature_1_, object_feature_1_, seen_first_object_): New data members. (Target_x86_64::do_merge_gnu_property): Rename to ... (Target_x86_64::record_gnu_property): ... this. Save target-specific properties in Target class object. (Target_x86_64::merge_gnu_properties): New method. (add_property): New static inline function. (Target_x86_64::do_finalize_gnu_properties): New method. * testsuite/Makefile.am (gnu_property_test): Remove C source file; link directly without compiler driver. * testsuite/Makefile.in: Regenerate. * testsuite/gnu_property_a.S: Add _start. --- gold/ChangeLog | 35 ++++++ gold/layout.cc | 186 +++++++++++++++++++++++++------- gold/layout.h | 13 ++- gold/object.cc | 5 +- gold/target.h | 35 +++--- gold/testsuite/Makefile.am | 4 +- gold/testsuite/Makefile.in | 4 +- gold/testsuite/gnu_property_a.S | 5 + gold/x86_64.cc | 116 ++++++++++++++++---- 9 files changed, 320 insertions(+), 83 deletions(-) diff --git a/gold/ChangeLog b/gold/ChangeLog index 10105137bd..f4c6f0aaf3 100644 --- a/gold/ChangeLog +++ b/gold/ChangeLog @@ -1,3 +1,38 @@ +2018-06-22 Cary Coutant + + PR gold/22914 + * layout.cc (read_sized_value): Fix spelling of section name. + (Layout::layout_gnu_property): Call Sized_target::record_gnu_property + for target-specific properties; + don't store them with target-independent properties yet. + (Layout::merge_gnu_properties): New method. + (Layout::add_gnu_property): New method. + (Layout::create_gnu_properties_note): Call target to finalize + target-specific properties. Fix spelling of output section name. + * layout.h (Layout::merge_gnu_properties): New method. + (Layout::add_gnu_property): New method. + * object.cc (Sized_relobj_file::do_layout): Call + Layout::merge_gnu_properties. + * target.h (Target::merge_gnu_property): Remove. + (Target::finalize_gnu_properties): New method. + (Target::do_merge_gnu_property): Move to Sized_target and rename. + (Target::do_finalize_gnu_properties): New virtual method. + (Sized_target::record_gnu_property): Moved and renamed from + Target::do_merge_gnu_property. + (Sized_target::merge_gnu_properties): New virtual method. + * x86_64.cc (Target_x86_64::isa_1_used_, isa_1_needed_) + (feature_1_, object_feature_1_, seen_first_object_): New data members. + (Target_x86_64::do_merge_gnu_property): Rename to ... + (Target_x86_64::record_gnu_property): ... this. Save target-specific + properties in Target class object. + (Target_x86_64::merge_gnu_properties): New method. + (add_property): New static inline function. + (Target_x86_64::do_finalize_gnu_properties): New method. + * testsuite/Makefile.am (gnu_property_test): Remove C source file; + link directly without compiler driver. + * testsuite/Makefile.in: Regenerate. + * testsuite/gnu_property_a.S: Add _start. + 2018-06-22 Cary Coutant * incremental.cc (Sized_incremental_binary::setup_readers): Use diff --git a/gold/layout.cc b/gold/layout.cc index 9ce38c3dde..0df7ed37f0 100644 --- a/gold/layout.cc +++ b/gold/layout.cc @@ -2217,7 +2217,7 @@ read_sized_value(size_t size, const unsigned char* buf, bool is_big_endian, } else { - gold_warning(_("%s: in .note.gnu.properties section, " + gold_warning(_("%s: in .note.gnu.property section, " "pr_datasz must be 4 or 8"), object->name().c_str()); } @@ -2262,6 +2262,63 @@ Layout::layout_gnu_property(unsigned int note_type, // We currently support only the one note type. gold_assert(note_type == elfcpp::NT_GNU_PROPERTY_TYPE_0); + if (pr_type >= elfcpp::GNU_PROPERTY_LOPROC + && pr_type < elfcpp::GNU_PROPERTY_HIPROC) + { + // Target-dependent property value; call the target to record. + const int size = parameters->target().get_size(); + const bool is_big_endian = parameters->target().is_big_endian(); + if (size == 32) + { + if (is_big_endian) + { +#ifdef HAVE_TARGET_32_BIG + parameters->sized_target<32, true>()-> + record_gnu_property(note_type, pr_type, pr_datasz, pr_data, + object); +#else + gold_unreachable(); +#endif + } + else + { +#ifdef HAVE_TARGET_32_LITTLE + parameters->sized_target<32, false>()-> + record_gnu_property(note_type, pr_type, pr_datasz, pr_data, + object); +#else + gold_unreachable(); +#endif + } + } + else if (size == 64) + { + if (is_big_endian) + { +#ifdef HAVE_TARGET_64_BIG + parameters->sized_target<64, true>()-> + record_gnu_property(note_type, pr_type, pr_datasz, pr_data, + object); +#else + gold_unreachable(); +#endif + } + else + { +#ifdef HAVE_TARGET_64_LITTLE + parameters->sized_target<64, false>()-> + record_gnu_property(note_type, pr_type, pr_datasz, pr_data, + object); +#else + gold_unreachable(); +#endif + } + } + else + gold_unreachable(); + return; + } + Gnu_properties::iterator pprop = this->gnu_properties_.find(pr_type); if (pprop == this->gnu_properties_.end()) { @@ -2273,46 +2330,99 @@ Layout::layout_gnu_property(unsigned int note_type, } else { - if (pr_type >= elfcpp::GNU_PROPERTY_LOPROC - && pr_type < elfcpp::GNU_PROPERTY_HIPROC) + const bool is_big_endian = parameters->target().is_big_endian(); + switch (pr_type) + { + case elfcpp::GNU_PROPERTY_STACK_SIZE: + // Record the maximum value seen. + { + uint64_t val1 = read_sized_value(pprop->second.pr_datasz, + pprop->second.pr_data, + is_big_endian, object); + uint64_t val2 = read_sized_value(pr_datasz, pr_data, + is_big_endian, object); + if (val2 > val1) + write_sized_value(val2, pprop->second.pr_datasz, + pprop->second.pr_data, is_big_endian); + } + break; + case elfcpp::GNU_PROPERTY_NO_COPY_ON_PROTECTED: + // No data to merge. + break; + default: + gold_warning(_("%s: unknown program property type %d " + "in .note.gnu.property section"), + object->name().c_str(), pr_type); + } + } +} + +// Merge per-object properties with program properties. +// This lets the target identify objects that are missing certain +// properties, in cases where properties must be ANDed together. + +void +Layout::merge_gnu_properties(const Object* object) +{ + const int size = parameters->target().get_size(); + const bool is_big_endian = parameters->target().is_big_endian(); + if (size == 32) + { + if (is_big_endian) + { +#ifdef HAVE_TARGET_32_BIG + parameters->sized_target<32, true>()->merge_gnu_properties(object); +#else + gold_unreachable(); +#endif + } + else + { +#ifdef HAVE_TARGET_32_LITTLE + parameters->sized_target<32, false>()->merge_gnu_properties(object); +#else + gold_unreachable(); +#endif + } + } + else if (size == 64) + { + if (is_big_endian) { - // Target-dependent property value; call the target to merge. - parameters->target().merge_gnu_property(note_type, - pr_type, - pr_datasz, - pr_data, - pprop->second.pr_datasz, - pprop->second.pr_data, - object); +#ifdef HAVE_TARGET_64_BIG + parameters->sized_target<64, true>()->merge_gnu_properties(object); +#else + gold_unreachable(); +#endif } else { - const bool is_big_endian = parameters->target().is_big_endian(); - switch (pr_type) - { - case elfcpp::GNU_PROPERTY_STACK_SIZE: - // Record the maximum value seen. - { - uint64_t val1 = read_sized_value(pprop->second.pr_datasz, - pprop->second.pr_data, - is_big_endian, object); - uint64_t val2 = read_sized_value(pr_datasz, pr_data, - is_big_endian, object); - if (val2 > val1) - write_sized_value(val2, pprop->second.pr_datasz, - pprop->second.pr_data, is_big_endian); - } - break; - case elfcpp::GNU_PROPERTY_NO_COPY_ON_PROTECTED: - // No data to merge. - break; - default: - gold_warning(_("%s: unknown program property type %d " - "in .note.gnu.properties section"), - object->name().c_str(), pr_type); - } +#ifdef HAVE_TARGET_64_LITTLE + parameters->sized_target<64, false>()->merge_gnu_properties(object); +#else + gold_unreachable(); +#endif } } + else + gold_unreachable(); +} + +// Add a target-specific property for the output .note.gnu.property section. + +void +Layout::add_gnu_property(unsigned int note_type, + unsigned int pr_type, + size_t pr_datasz, + const unsigned char* pr_data) +{ + gold_assert(note_type == elfcpp::NT_GNU_PROPERTY_TYPE_0); + + Gnu_property prop; + prop.pr_datasz = pr_datasz; + prop.pr_data = new unsigned char[pr_datasz]; + memcpy(prop.pr_data, pr_data, pr_datasz); + this->gnu_properties_[pr_type] = prop; } // Create automatic note sections. @@ -3144,12 +3254,14 @@ Layout::create_note(const char* name, int note_type, return os; } -// Create a .note.gnu.properties section to record program properties +// Create a .note.gnu.property section to record program properties // accumulated from the input files. void Layout::create_gnu_properties_note() { + parameters->target().finalize_gnu_properties(this); + if (this->gnu_properties_.empty()) return; @@ -3168,7 +3280,7 @@ Layout::create_gnu_properties_note() // Create the note section. size_t trailing_padding; Output_section* os = this->create_note("GNU", elfcpp::NT_GNU_PROPERTY_TYPE_0, - ".note.gnu.properties", descsz, + ".note.gnu.property", descsz, true, &trailing_padding); if (os == NULL) return; diff --git a/gold/layout.h b/gold/layout.h index d71d291b7c..8305b2af76 100644 --- a/gold/layout.h +++ b/gold/layout.h @@ -701,6 +701,17 @@ class Layout const unsigned char* pr_data, const Object* object); + // Merge per-object properties with program properties. + void + merge_gnu_properties(const Object* object); + + // Add a target-specific property for the output .note.gnu.property section. + void + add_gnu_property(unsigned int note_type, + unsigned int pr_type, + size_t pr_datasz, + const unsigned char* pr_data); + // Add an Output_section_data to the layout. This is used for // special sections like the GOT section. ORDER is where the // section should wind up in the output segment. IS_RELRO is true @@ -1359,7 +1370,7 @@ class Layout std::vector section_infos_; }; - // Program properties from .note.gnu.properties sections. + // Program properties from .note.gnu.property sections. struct Gnu_property { size_t pr_datasz; diff --git a/gold/object.cc b/gold/object.cc index 8c874fedc8..374340fa16 100644 --- a/gold/object.cc +++ b/gold/object.cc @@ -1862,7 +1862,10 @@ Sized_relobj_file::do_layout(Symbol_table* symtab, } if (!is_pass_two) - layout->layout_gnu_stack(seen_gnu_stack, gnu_stack_flags, this); + { + layout->merge_gnu_properties(this); + layout->layout_gnu_stack(seen_gnu_stack, gnu_stack_flags, this); + } // Handle the .eh_frame sections after the other sections. gold_assert(!is_pass_one || eh_frame_sections.empty()); diff --git a/gold/target.h b/gold/target.h index 1f8f757dbf..bb312067b5 100644 --- a/gold/target.h +++ b/gold/target.h @@ -509,22 +509,10 @@ class Target should_include_section(elfcpp::Elf_Word sh_type) const { return this->do_should_include_section(sh_type); } - // Merge a target-specific program property in the .note.gnu.properties - // section. + // Finalize the target-specific properties in the .note.gnu.property section. void - merge_gnu_property(int note_type, - int pr_type, - size_t new_pr_datasz, - const unsigned char* new_pr_data, - size_t old_pr_datasz, - unsigned char* old_pr_data, - const Object* object) const - { - return this->do_merge_gnu_property(note_type, pr_type, - new_pr_datasz, new_pr_data, - old_pr_datasz, old_pr_data, - object); - } + finalize_gnu_properties(Layout* layout) const + { this->do_finalize_gnu_properties(layout); } protected: // This struct holds the constant information for a child class. We @@ -832,11 +820,9 @@ class Target do_should_include_section(elfcpp::Elf_Word) const { return true; } - // Merge a target-specific program property in the .note.gnu.properties - // section. + // Finalize the target-specific properties in the .note.gnu.property section. virtual void - do_merge_gnu_property(int, int, size_t, const unsigned char*, - size_t, unsigned char*, const Object*) const + do_finalize_gnu_properties(Layout*) const { } private: @@ -1158,6 +1144,17 @@ class Sized_target : public Target return elfcpp::elf_r_sym(rel.get_r_info()); } + // Record a target-specific program property in the .note.gnu.property + // section. + virtual void + record_gnu_property(int, int, size_t, const unsigned char*, const Object*) + { } + + // Merge the target-specific program properties from the current object. + virtual void + merge_gnu_properties(const Object*) + { } + protected: Sized_target(const Target::Target_info* pti) : Target(pti) diff --git a/gold/testsuite/Makefile.am b/gold/testsuite/Makefile.am index f439011d37..5f7d981e2e 100644 --- a/gold/testsuite/Makefile.am +++ b/gold/testsuite/Makefile.am @@ -3260,8 +3260,8 @@ check_SCRIPTS += gnu_property_test.sh check_DATA += gnu_property_test.stdout gnu_property_test.stdout: gnu_property_test $(TEST_READELF) -n $< >$@ -gnu_property_test: gcctestdir/ld gnu_property_main.o gnu_property_a.o gnu_property_b.o gnu_property_c.o - $(LINK) -Bgcctestdir/ -o $@ gnu_property_main.o gnu_property_a.o gnu_property_b.o gnu_property_c.o +gnu_property_test: gcctestdir/ld gnu_property_a.o gnu_property_b.o gnu_property_c.o + gcctestdir/ld -o $@ gnu_property_a.o gnu_property_b.o gnu_property_c.o gnu_property_main.o: gnu_property_main.c $(COMPILE) -c -o $@ $< gnu_property_a.o: gnu_property_a.S diff --git a/gold/testsuite/Makefile.in b/gold/testsuite/Makefile.in index ee44c95ebf..b4729bcc76 100644 --- a/gold/testsuite/Makefile.in +++ b/gold/testsuite/Makefile.in @@ -9465,8 +9465,8 @@ uninstall-am: @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(CXXCOMPILE) -c -Bgcctestdir/ -Wa,-madd-bnd-prefix -o $@ $< @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@gnu_property_test.stdout: gnu_property_test @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(TEST_READELF) -n $< >$@ -@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@gnu_property_test: gcctestdir/ld gnu_property_main.o gnu_property_a.o gnu_property_b.o gnu_property_c.o -@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(LINK) -Bgcctestdir/ -o $@ gnu_property_main.o gnu_property_a.o gnu_property_b.o gnu_property_c.o +@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@gnu_property_test: gcctestdir/ld gnu_property_a.o gnu_property_b.o gnu_property_c.o +@DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ gcctestdir/ld -o $@ gnu_property_a.o gnu_property_b.o gnu_property_c.o @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@gnu_property_main.o: gnu_property_main.c @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@ $(COMPILE) -c -o $@ $< @DEFAULT_TARGET_X86_64_TRUE@@GCC_TRUE@@NATIVE_LINKER_TRUE@gnu_property_a.o: gnu_property_a.S diff --git a/gold/testsuite/gnu_property_a.S b/gold/testsuite/gnu_property_a.S index 2fa073dc2f..463bc8e52f 100644 --- a/gold/testsuite/gnu_property_a.S +++ b/gold/testsuite/gnu_property_a.S @@ -11,6 +11,11 @@ # define ALIGN 2 #endif + .text + .globl _start +_start: + ret + .section ".note.gnu.property", "a" .p2align ALIGN diff --git a/gold/x86_64.cc b/gold/x86_64.cc index e6fa47ea8f..a27f84d723 100644 --- a/gold/x86_64.cc +++ b/gold/x86_64.cc @@ -590,7 +590,8 @@ class Target_x86_64 : public Sized_target got_tlsdesc_(NULL), global_offset_table_(NULL), rela_dyn_(NULL), rela_irelative_(NULL), copy_relocs_(elfcpp::R_X86_64_COPY), got_mod_index_offset_(-1U), tlsdesc_reloc_info_(), - tls_base_symbol_defined_(false) + tls_base_symbol_defined_(false), isa_1_used_(0), isa_1_needed_(0), + feature_1_(0), object_feature_1_(0), seen_first_object_(false) { } // Hook for a new output section. @@ -1188,11 +1189,19 @@ class Target_x86_64 : public Sized_target this->rela_dyn_section(layout)); } - // Merge a target-specific program property in the .note.gnu.properties + // Record a target-specific program property in the .note.gnu.property // section. void - do_merge_gnu_property(int, int, size_t, const unsigned char*, - size_t, unsigned char*, const Object*) const; + record_gnu_property(int, int, size_t, const unsigned char*, const Object*); + + // Merge the target-specific program properties from the current object. + void + merge_gnu_properties(const Object*); + + // Finalize the target-specific program properties and add them back to + // the layout. + void + do_finalize_gnu_properties(Layout*) const; // Information about this specific target which we pass to the // general Target structure. @@ -1251,6 +1260,17 @@ class Target_x86_64 : public Sized_target std::vector tlsdesc_reloc_info_; // True if the _TLS_MODULE_BASE_ symbol has been defined. bool tls_base_symbol_defined_; + // Target-specific program properties, from .note.gnu.property section. + // Each bit represents a specific feature. + uint32_t isa_1_used_; + uint32_t isa_1_needed_; + uint32_t feature_1_; + // Target-specific properties from the current object. + // These bits get ANDed into FEATURE_1_ after all properties for the object + // have been processed. + uint32_t object_feature_1_; + // Whether we have seen our first object, for use in initializing FEATURE_1_. + bool seen_first_object_; }; template<> @@ -1439,37 +1459,91 @@ Target_x86_64::rela_irelative_section(Layout* layout) return this->rela_irelative_; } -// Merge a target-specific program property in the .note.gnu.properties +// Record a target-specific program property from the .note.gnu.property // section. template void -Target_x86_64::do_merge_gnu_property( +Target_x86_64::record_gnu_property( int, int pr_type, - size_t new_pr_datasz, const unsigned char* new_pr_data, - size_t old_pr_datasz, unsigned char* old_pr_data, - const Object*) const + size_t pr_datasz, const unsigned char* pr_data, + const Object* object) { - size_t min_datasz = (new_pr_datasz > old_pr_datasz - ? old_pr_datasz - : new_pr_datasz); + uint32_t val; + switch (pr_type) { case elfcpp::GNU_PROPERTY_X86_ISA_1_USED: case elfcpp::GNU_PROPERTY_X86_ISA_1_NEEDED: - { - for (size_t i = 0; i < min_datasz; ++i) - old_pr_data[i] |= new_pr_data[i]; - } - break; case elfcpp::GNU_PROPERTY_X86_FEATURE_1_AND: - { - for (size_t i = 0; i < min_datasz; ++i) - old_pr_data[i] &= new_pr_data[i]; - } + if (pr_datasz != 4) + { + gold_warning(_("%s: corrupt .note.gnu.property section " + "(pr_datasz for property %d is not 4)"), + object->name().c_str(), pr_type); + return; + } + val = elfcpp::Swap<32, false>::readval(pr_data); break; default: + gold_warning(_("%s: unknown program property type 0x%x " + "in .note.gnu.property section"), + object->name().c_str(), pr_type); break; } + + switch (pr_type) + { + case elfcpp::GNU_PROPERTY_X86_ISA_1_USED: + this->isa_1_used_ |= val; + break; + case elfcpp::GNU_PROPERTY_X86_ISA_1_NEEDED: + this->isa_1_needed_ |= val; + break; + case elfcpp::GNU_PROPERTY_X86_FEATURE_1_AND: + // If we see multiple feature props in one object, OR them together. + this->object_feature_1_ |= val; + break; + } +} + +// Merge the target-specific program properties from the current object. +template +void +Target_x86_64::merge_gnu_properties(const Object*) +{ + if (this->seen_first_object_) + this->feature_1_ &= this->object_feature_1_; + else + { + this->feature_1_ = this->object_feature_1_; + this->seen_first_object_ = true; + } + this->object_feature_1_ = 0; +} + +static inline void +add_property(Layout* layout, unsigned int pr_type, uint32_t val) +{ + unsigned char buf[4]; + elfcpp::Swap<32, false>::writeval(buf, val); + layout->add_gnu_property(elfcpp::NT_GNU_PROPERTY_TYPE_0, pr_type, 4, buf); +} + +// Finalize the target-specific program properties and add them back to +// the layout. +template +void +Target_x86_64::do_finalize_gnu_properties(Layout* layout) const +{ + if (this->isa_1_used_ != 0) + add_property(layout, elfcpp::GNU_PROPERTY_X86_ISA_1_USED, + this->isa_1_used_); + if (this->isa_1_needed_ != 0) + add_property(layout, elfcpp::GNU_PROPERTY_X86_ISA_1_NEEDED, + this->isa_1_needed_); + if (this->feature_1_ != 0) + add_property(layout, elfcpp::GNU_PROPERTY_X86_FEATURE_1_AND, + this->feature_1_); } // Write the first three reserved words of the .got.plt section. -- 2.34.1