// layout.cc -- lay out output file sections for gold
-// Copyright (C) 2006-2018 Free Software Foundation, Inc.
+// Copyright (C) 2006-2020 Free Software Foundation, Inc.
// Written by Ian Lance Taylor <iant@google.com>.
// This file is part of gold.
unique_segment_for_sections_specified_(false),
incremental_inputs_(NULL),
record_output_section_data_from_script_(false),
+ lto_slim_object_(false),
script_output_section_data_list_(),
segment_states_(NULL),
relaxation_debug_check_(NULL),
input_section_position_(),
input_section_glob_(),
incremental_base_(NULL),
- free_list_()
+ free_list_(),
+ gnu_properties_()
{
// 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.
".text.unlikely",
".text.exit",
".text.startup",
- ".text.hot"
+ ".text.hot",
+ ".text.sorted"
};
for (size_t i = 0;
}
}
-// Remove .eh_frame information for a PLT. FDEs using the CIE must
-// be removed in reverse order to the order they were added.
+// Remove all post-map .eh_frame information for a PLT.
void
Layout::remove_eh_frame_for_plt(Output_data* plt, const unsigned char* cie_data,
- size_t cie_length, const unsigned char* fde_data,
- size_t fde_length)
+ size_t cie_length)
{
if (parameters->incremental())
{
// FIXME: Maybe this could work some day....
return;
}
- this->eh_frame_data_->remove_ehframe_for_plt(plt, cie_data, cie_length,
- fde_data, fde_length);
+ this->eh_frame_data_->remove_ehframe_for_plt(plt, cie_data, cie_length);
}
// Scan a .debug_info or .debug_types section, and add summary
}
}
+// Read a value with given size and endianness.
+
+static inline uint64_t
+read_sized_value(size_t size, const unsigned char* buf, bool is_big_endian,
+ const Object* object)
+{
+ uint64_t val = 0;
+ if (size == 4)
+ {
+ if (is_big_endian)
+ val = elfcpp::Swap<32, true>::readval(buf);
+ else
+ val = elfcpp::Swap<32, false>::readval(buf);
+ }
+ else if (size == 8)
+ {
+ if (is_big_endian)
+ val = elfcpp::Swap<64, true>::readval(buf);
+ else
+ val = elfcpp::Swap<64, false>::readval(buf);
+ }
+ else
+ {
+ gold_warning(_("%s: in .note.gnu.property section, "
+ "pr_datasz must be 4 or 8"),
+ object->name().c_str());
+ }
+ return val;
+}
+
+// Write a value with given size and endianness.
+
+static inline void
+write_sized_value(uint64_t value, size_t size, unsigned char* buf,
+ bool is_big_endian)
+{
+ if (size == 4)
+ {
+ if (is_big_endian)
+ elfcpp::Swap<32, true>::writeval(buf, static_cast<uint32_t>(value));
+ else
+ elfcpp::Swap<32, false>::writeval(buf, static_cast<uint32_t>(value));
+ }
+ else if (size == 8)
+ {
+ if (is_big_endian)
+ elfcpp::Swap<64, true>::writeval(buf, value);
+ else
+ elfcpp::Swap<64, false>::writeval(buf, value);
+ }
+ else
+ {
+ // We will have already complained about this.
+ }
+}
+
+// Handle the .note.gnu.property section at layout time.
+
+void
+Layout::layout_gnu_property(unsigned int note_type,
+ unsigned int pr_type,
+ size_t pr_datasz,
+ const unsigned char* pr_data,
+ const Object* object)
+{
+ // 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())
+ {
+ 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;
+ }
+ 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.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)
+ {
+#ifdef HAVE_TARGET_64_BIG
+ parameters->sized_target<64, true>()->merge_gnu_properties(object);
+#else
+ gold_unreachable();
+#endif
+ }
+ else
+ {
+#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.
void
Layout::create_notes()
{
+ this->create_gnu_properties_note();
this->create_gold_note();
this->create_stack_segment();
this->create_build_id();
return os;
}
+// 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;
+
+ const unsigned int size = parameters->target().get_size();
+ const bool is_big_endian = parameters->target().is_big_endian();
+
+ // Compute the total size of the properties array.
+ size_t descsz = 0;
+ for (Gnu_properties::const_iterator prop = this->gnu_properties_.begin();
+ prop != this->gnu_properties_.end();
+ ++prop)
+ {
+ descsz = align_address(descsz + 8 + prop->second.pr_datasz, size / 8);
+ }
+
+ // Create the note section.
+ size_t trailing_padding;
+ Output_section* os = this->create_note("GNU", elfcpp::NT_GNU_PROPERTY_TYPE_0,
+ ".note.gnu.property", descsz,
+ true, &trailing_padding);
+ if (os == NULL)
+ return;
+ gold_assert(trailing_padding == 0);
+
+ // Allocate and fill the properties array.
+ unsigned char* desc = new unsigned char[descsz];
+ unsigned char* p = desc;
+ for (Gnu_properties::const_iterator prop = this->gnu_properties_.begin();
+ prop != this->gnu_properties_.end();
+ ++prop)
+ {
+ size_t datasz = prop->second.pr_datasz;
+ size_t aligned_datasz = align_address(prop->second.pr_datasz, size / 8);
+ write_sized_value(prop->first, 4, p, is_big_endian);
+ write_sized_value(datasz, 4, p + 4, is_big_endian);
+ memcpy(p + 8, prop->second.pr_data, datasz);
+ if (aligned_datasz > datasz)
+ memset(p + 8 + datasz, 0, aligned_datasz - datasz);
+ p += 8 + aligned_datasz;
+ }
+ Output_section_data* posd = new Output_data_const(desc, descsz, 4);
+ os->add_output_section_data(posd);
+}
+
// For an executable or shared library, create a note to record the
// version of gold used to create the binary.
MAPPING_INIT(".gnu.linkonce.armextab.", ".ARM.extab"),
MAPPING_INIT(".ARM.exidx", ".ARM.exidx"),
MAPPING_INIT(".gnu.linkonce.armexidx.", ".ARM.exidx"),
+ MAPPING_INIT(".gnu.build.attributes.", ".gnu.build.attributes"),
};
// Mapping for ".text" section prefixes with -z,keep-text-section-prefix.