diff options
Diffstat (limited to 'gold/attributes.cc')
-rw-r--r-- | gold/attributes.cc | 458 |
1 files changed, 458 insertions, 0 deletions
diff --git a/gold/attributes.cc b/gold/attributes.cc new file mode 100644 index 0000000..6b2260d --- /dev/null +++ b/gold/attributes.cc @@ -0,0 +1,458 @@ +// attributes.cc -- object attributes for gold + +// Copyright 2009 Free Software Foundation, Inc. +// Written by Doug Kwan <dougkwan@google.com>. +// This file contains code adapted from BFD. + +// 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. + +#include "gold.h" + +#include <limits> + +#include "attributes.h" +#include "elfcpp.h" +#include "target.h" +#include "parameters.h" +#include "int_encoding.h" + +namespace gold +{ + +// Object_attribute methods. + +// Return size of attribute encode in ULEB128. + +size_t +Object_attribute::size(int tag) const +{ + // Attributes with default values are not written out. + if (this->is_default_attribute()) + return 0; + + size_t size = get_length_as_unsigned_LEB_128(tag); + if (Object_attribute::attribute_type_has_int_value(this->type_)) + size += get_length_as_unsigned_LEB_128(this->int_value_); + if (Object_attribute::attribute_type_has_string_value(this->type_)) + size += this->string_value_.size() + 1; + return size; +} + +// Whether this has the default value (0/""). + +bool +Object_attribute::is_default_attribute() const +{ + if (Object_attribute::attribute_type_has_int_value(this->type_) + && this->int_value_ != 0) + return false; + if (Object_attribute::attribute_type_has_string_value(this->type_) + && !this->string_value_.empty()) + return false; + if (Object_attribute::attribute_type_has_no_default(this->type_)) + return false; + + return true; +} + +// Whether this matches another Object_attribute OA in merging. +// Two Object_attributes match if they have the same values. + +bool +Object_attribute::matches(const Object_attribute& oa) const +{ + return ((this->int_value_ != oa.int_value_) + && (this->string_value_ == oa.string_value_)); +} + +// Write this with TAG to a BUFFER. + +void +Object_attribute::write( + int tag, + std::vector<unsigned char>* buffer) const +{ + // No need to write default attributes. + if (this->is_default_attribute()) + return; + + // Write tag. + write_unsigned_LEB_128(buffer, convert_types<uint64_t, int>(tag)); + + // Write integer value. + if (Object_attribute::attribute_type_has_int_value(this->type_)) + write_unsigned_LEB_128(buffer, + convert_types<uint64_t, int>(this->int_value_)); + + // Write string value. + if (Object_attribute::attribute_type_has_string_value(this->type_)) + { + const unsigned char* start = + reinterpret_cast<const unsigned char*>(this->string_value_.c_str()); + const unsigned char* end = start + this->string_value_.size() + 1; + buffer->insert(buffer->end(), start, end); + } +} + +// Vendor_object_attributes methods. + +// Copying constructor. + +Vendor_object_attributes::Vendor_object_attributes( + const Vendor_object_attributes& voa) +{ + this->vendor_ = voa.vendor_; + + for (int i = 0; i < NUM_KNOWN_ATTRIBUTES; ++i) + this->known_attributes_[i] = voa.known_attributes_[i]; + + // We do not handle attribute deletion. So this must be empty. + gold_assert(this->other_attributes_.empty()); + + for (Other_attributes::const_iterator p = voa.other_attributes_.begin(); + p != voa.other_attributes_.end(); + ++p) + this->other_attributes_[p->first] = new Object_attribute(*(p->second)); +} + +// Size of this in number of bytes. + +size_t +Vendor_object_attributes::size() const +{ + if (this->name() == NULL) + return 0; + + size_t data_size = 0; + for (int i = 4; i < NUM_KNOWN_ATTRIBUTES; ++i) + data_size += this->known_attributes_[i].size(i); + + for (Other_attributes::const_iterator p = this->other_attributes_.begin(); + p != this->other_attributes_.end(); + ++p) + data_size += p->second->size(p->first); + + // <size> <vendor_name> NUL 0x1 <size> + return ((data_size != 0 + || this->vendor_ == Object_attribute::OBJ_ATTR_PROC) + ? data_size + strlen(this->name()) + 2 + 2 * 4 + : 0); +} + +// Return a new attribute associated with TAG. + +Object_attribute* +Vendor_object_attributes::new_attribute(int tag) +{ + int type = Object_attribute::arg_type(this->vendor_, tag); + + if (tag < NUM_KNOWN_ATTRIBUTES) + { + this->known_attributes_[tag].set_type(type); + return &this->known_attributes_[tag]; + } + else + { + Object_attribute* attr = new Object_attribute(); + + // This should be the first time we insert this. + std::pair<Other_attributes::iterator, bool> ins = + this->other_attributes_.insert(std::make_pair(tag, attr)); + gold_assert(ins.second); + + attr->set_type(type); + return attr; + } +} + +// Return an attribute associated with TAG. + +Object_attribute* +Vendor_object_attributes::get_attribute(int tag) +{ + if (tag < NUM_KNOWN_ATTRIBUTES) + return &this->known_attributes_[tag]; + else + { + Other_attributes::iterator p = + this->other_attributes_.find(tag); + return p != this->other_attributes_.end() ? p->second : NULL; + } +} + +const Object_attribute* +Vendor_object_attributes::get_attribute(int tag) const +{ + if (tag < NUM_KNOWN_ATTRIBUTES) + return &this->known_attributes_[tag]; + else + { + Other_attributes::const_iterator p = + this->other_attributes_.find(tag); + return p != this->other_attributes_.end() ? p->second : NULL; + } +} + +// Write attributes to BUFFER. + +void +Vendor_object_attributes::write(std::vector<unsigned char>* buffer) const +{ + // Write subsection size. + size_t voa_size = this->size(); + uint32_t voa_size_as_u32 = convert_types<uint32_t, size_t>(voa_size); + insert_into_vector<32>(buffer, voa_size_as_u32); + + // Write vendor name. + const unsigned char* vendor_start = + reinterpret_cast<const unsigned char*>(this->name()); + size_t vendor_length = strlen(this->name()) + 1; + const unsigned char* vendor_end = vendor_start + vendor_length; + buffer->insert(buffer->end(), vendor_start, vendor_end); + + // Write file tag. + buffer->push_back(Object_attribute::Tag_File); + + // Write attributes size. + uint32_t attributes_size_as_u32 = + convert_types<uint32_t, size_t>(voa_size - 4 - vendor_length); + insert_into_vector<32>(buffer, attributes_size_as_u32); + + // Write known attributes, skipping any defaults. + for (int i = 4; i < NUM_KNOWN_ATTRIBUTES; ++i) + { + // A target may write known attributes in a special order. + // Call target hook to remap tags. Attributes_order is the identity + // function if no re-ordering is required. + int tag = parameters->target().attributes_order(i); + this->known_attributes_[tag].write(tag, buffer); + } + + // Write other attributes. + for (Other_attributes::const_iterator q = this->other_attributes_.begin(); + q != this->other_attributes_.end(); + ++q) + q->second->write(q->first, buffer); +} + +// Attributes_section_data methods. + +// Compute encoded size of this. + +size_t +Attributes_section_data::size() const +{ + size_t data_size = 0; + for(int vendor = OBJ_ATTR_FIRST; vendor <= OBJ_ATTR_LAST; ++vendor) + data_size += this->vendor_object_attributes_[vendor]->size(); + + // 'A' <sections for each vendor> + return data_size != 0 ? data_size + 1 : 0; +} + +// Construct an Attributes_section_data object by parsing section contents +// specified by VIEW and SIZE. + +Attributes_section_data::Attributes_section_data( + const unsigned char* view, + section_size_type size) +{ + for (int vendor = OBJ_ATTR_FIRST; vendor <= OBJ_ATTR_LAST; ++vendor) + this->vendor_object_attributes_[vendor] = + new Vendor_object_attributes(vendor); + + const unsigned char *p = view; + p = view; + if (*(p++) == 'A') + { + size--; + while (size > 0) + { + // Size of vendor attributes section. + section_size_type section_size = + convert_to_section_size_type(read_from_pointer<32>(&p)); + + if (section_size > size) + section_size = size; + size -= section_size; + + const char* section_name = reinterpret_cast<const char*>(p); + section_size_type section_name_size = strlen(section_name) + 1; + section_size -= section_name_size + 4; + + int vendor; + const char *std_section = parameters->target().attributes_vendor(); + if (std_section != NULL && strcmp(section_name, std_section) == 0) + vendor = Object_attribute::OBJ_ATTR_PROC; + else if (strcmp(section_name, "gnu") == 0) + vendor = Object_attribute::OBJ_ATTR_GNU; + else + { + // Other vendor section. Ignore it. + p += section_name_size + section_size; + continue; + } + p += section_name_size; + + while (section_size > 0) + { + const unsigned char* subsection_start = p; + + // Read vendor subsection index and size. + size_t uleb128_len; + uint64_t val = read_unsigned_LEB_128(p, &uleb128_len); + p += uleb128_len; + + int tag = convert_types<int, uint64_t>(val); + section_size_type subsection_size = + convert_to_section_size_type(read_from_pointer<32>(&p)); + section_size -= subsection_size; + subsection_size -= (p - subsection_start); + + const unsigned char* end = p + subsection_size; + switch (tag) + { + case Object_attribute::Tag_File: + while (p < end) + { + val = read_unsigned_LEB_128(p, &uleb128_len); + p += uleb128_len; + tag = convert_types<int, uint64_t>(val); + Vendor_object_attributes* pvoa = + this->vendor_object_attributes_[vendor]; + Object_attribute* attr = pvoa->new_attribute(tag); + const char* string_arg; + unsigned int int_arg; + + int type = Object_attribute::arg_type(vendor, tag); + switch (type + & (Object_attribute::ATTR_TYPE_FLAG_INT_VAL + | Object_attribute::ATTR_TYPE_FLAG_STR_VAL)) + { + case (Object_attribute::ATTR_TYPE_FLAG_INT_VAL + | Object_attribute::ATTR_TYPE_FLAG_STR_VAL): + val = read_unsigned_LEB_128(p, &uleb128_len); + p += uleb128_len; + int_arg = convert_types<unsigned int, uint64_t>(val); + string_arg = reinterpret_cast<const char *>(p); + attr->set_int_value(int_arg); + p += strlen(string_arg) + 1; + break; + case Object_attribute::ATTR_TYPE_FLAG_STR_VAL: + string_arg = reinterpret_cast<const char *>(p); + attr->set_string_value(string_arg); + p += strlen(string_arg) + 1; + break; + case Object_attribute::ATTR_TYPE_FLAG_INT_VAL: + val = read_unsigned_LEB_128(p, &uleb128_len); + p += uleb128_len; + int_arg = convert_types<unsigned int, uint64_t>(val); + attr->set_int_value(int_arg); + break; + default: + gold_unreachable(); + } + } + break; + case Object_attribute::Tag_Section: + case Object_attribute::Tag_Symbol: + // Don't have anywhere convenient to attach these. + // Fall through for now. + default: + // Ignore things we don't know about. + p += subsection_size; + subsection_size = 0; + break; + } + } + } + } +} + +// Merge target-independent attributes from another Attribute_section_data +// ASD from an object called NAME into this. + +void +Attributes_section_data::merge( + const char* name, + const Attributes_section_data* pasd) +{ + // The only common attribute is currently Tag_compatibility, + // accepted in both processor and "gnu" sections. + for (int vendor = OBJ_ATTR_FIRST; vendor <= OBJ_ATTR_LAST; ++vendor) + { + // Handle Tag_compatibility. The tags are only compatible if the flags + // are identical and, if the flags are '1', the strings are identical. + // If the flags are non-zero, then we can only use the string "gnu". + const Object_attribute* in_attr = + &pasd->known_attributes(vendor)[Object_attribute::Tag_compatibility]; + Object_attribute* out_attr = + &this->known_attributes(vendor)[Object_attribute::Tag_compatibility]; + + if (in_attr->int_value() > 0 + && in_attr->string_value() != "gnu") + { + gold_error(_("%s: must be processed by '%s' toolchain"), + name, in_attr->string_value().c_str()); + return; + } + + if (in_attr->int_value() != out_attr->int_value() + || in_attr->string_value() != out_attr->string_value()) + { + gold_error(_("%s: object tag '%d, %s' is " + "incompatible with tag '%d, %s'"), + name, in_attr->int_value(), + in_attr->string_value().c_str(), + out_attr->int_value(), + out_attr->string_value().c_str()); + } + } +} + +// Write to a buffer. + +void +Attributes_section_data::write(std::vector<unsigned char>* buffer) const +{ + buffer->push_back('A'); + for (int vendor = OBJ_ATTR_FIRST; vendor <= OBJ_ATTR_LAST; ++vendor) + if (this->vendor_object_attributes_[vendor]->size() != 0) + this->vendor_object_attributes_[vendor]->write(buffer); +} + +// Methods for Output_attributes_section_data. + +// Write attributes section data to file OF. + +void +Output_attributes_section_data::do_write(Output_file* of) +{ + off_t offset = this->offset(); + const section_size_type oview_size = + convert_to_section_size_type(this->data_size()); + unsigned char* const oview = of->get_output_view(offset, oview_size); + + std::vector<unsigned char> buffer; + this->attributes_section_data_.write(&buffer); + gold_assert(convert_to_section_size_type(buffer.size()) == oview_size); + memcpy(oview, buffer.data(), buffer.size()); + of->write_output_view(this->offset(), oview_size, oview); +} + +} // End namespace gold. |