// attributes.cc -- object attributes for gold // Copyright 2009 Free Software Foundation, Inc. // Written by Doug Kwan . // 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 #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 uleb128_size = get_length_as_unsigned_LEB_128(tag); if (Object_attribute::attribute_type_has_int_value(this->type_)) uleb128_size += get_length_as_unsigned_LEB_128(this->int_value_); if (Object_attribute::attribute_type_has_string_value(this->type_)) uleb128_size += this->string_value_.size() + 1; return uleb128_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* buffer) const { // No need to write default attributes. if (this->is_default_attribute()) return; // Write tag. write_unsigned_LEB_128(buffer, convert_types(tag)); // Write integer value. if (Object_attribute::attribute_type_has_int_value(this->type_)) write_unsigned_LEB_128(buffer, convert_types(this->int_value_)); // Write string value. if (Object_attribute::attribute_type_has_string_value(this->type_)) { const unsigned char* start = reinterpret_cast(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); // NUL 0x1 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 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* buffer) const { // Write subsection size. size_t voa_size = this->size(); uint32_t voa_size_as_u32 = convert_types(voa_size); insert_into_vector<32>(buffer, voa_size_as_u32); // Write vendor name. const unsigned char* vendor_start = reinterpret_cast(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(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' return data_size != 0 ? data_size + 1 : 0; } // Construct an Attributes_section_data object by parsing section contents // specified by VIEW and VIEW_SIZE. Attributes_section_data::Attributes_section_data( const unsigned char* view, section_size_type view_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') { view_size--; while (view_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 > view_size) section_size = view_size; view_size -= section_size; const char* section_name = reinterpret_cast(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(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(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(val); string_arg = reinterpret_cast(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(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(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* 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) { const section_size_type oview_size = convert_to_section_size_type(this->data_size()); unsigned char* const oview = of->get_output_view(this->offset(), oview_size); std::vector 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.