blob: 04976bb0a8b6daa32c3a9474e69cac00b6935081 [file] [log] [blame]
//===- ELFAttribute.cpp ---------------------------------------------------===//
//
// The MCLinker Project
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "mcld/Target/ELFAttribute.h"
#include "mcld/ADT/SizeTraits.h"
#include "mcld/Fragment/RegionFragment.h"
#include "mcld/LD/LDSection.h"
#include "mcld/LD/SectionData.h"
#include "mcld/LinkerConfig.h"
#include "mcld/MC/Input.h"
#include "mcld/Support/LEB128.h"
#include "mcld/Support/MemoryArea.h"
#include "mcld/Support/MsgHandling.h"
#include "mcld/Target/ELFAttributeValue.h"
#include "mcld/Target/GNULDBackend.h"
#include <llvm/ADT/STLExtras.h>
#include <llvm/Support/Host.h>
#include <cstring>
namespace mcld {
//===----------------------------------------------------------------------===//
// ELFAttribute
//===----------------------------------------------------------------------===//
ELFAttribute::~ELFAttribute() {
llvm::DeleteContainerPointers(m_Subsections);
return;
}
bool ELFAttribute::merge(const Input& pInput, LDSection& pInputAttrSectHdr) {
// Skip corrupt subsection
if (pInputAttrSectHdr.size() < MinimalELFAttributeSectionSize)
return true;
// Obtain the region containing the attribute data. Expect exactly one
// RegionFragment in the section data.
const SectionData* sect_data = pInputAttrSectHdr.getSectionData();
// FIXME: Why is 2?
if ((sect_data->size() != 2) ||
(!llvm::isa<RegionFragment>(sect_data->front()))) {
return true;
}
const RegionFragment& region_frag =
llvm::cast<RegionFragment>(sect_data->front());
llvm::StringRef region = region_frag.getRegion();
// Parse the ELF attribute section header. ARM [ABI-addenda], 2.2.3.
//
// <format-version: ‘A’>
// [ <uint32: subsection-length> NTBS: vendor-name
// <bytes: vendor-data>
// ]*
const char* attribute_data = region.begin();
// format-version
if (attribute_data[0] != FormatVersion) {
warning(diag::warn_unsupported_attribute_section_format)
<< pInput.name() << attribute_data[0];
return true;
}
size_t subsection_offset = FormatVersionFieldSize;
// Iterate all subsections containing in this attribute section.
do {
const char* subsection_data = region.begin() + subsection_offset;
// subsection-length
uint32_t subsection_length =
*reinterpret_cast<const uint32_t*>(subsection_data);
if (llvm::sys::IsLittleEndianHost != m_Config.targets().isLittleEndian())
bswap32(subsection_length);
// vendor-name
const char* vendor_name = subsection_data + SubsectionLengthFieldSize;
const size_t vendor_name_length = ::strlen(vendor_name) + 1 /* '\0' */;
// Check the length.
if ((vendor_name_length <= 1) ||
(subsection_length <= (SubsectionLengthFieldSize + vendor_name_length)))
return true;
// Select the attribute subsection.
Subsection* subsection = getSubsection(vendor_name);
// Only process the subsections whose vendor can be recognized.
if (subsection == NULL) {
warning(diag::warn_unrecognized_vendor_subsection) << vendor_name
<< pInput.name();
} else {
// vendor-data
size_t vendor_data_offset =
subsection_offset + SubsectionLengthFieldSize + vendor_name_length;
size_t vendor_data_size =
subsection_length - SubsectionLengthFieldSize - vendor_name_length;
ConstAddress vendor_data =
reinterpret_cast<ConstAddress>(region.begin()) + vendor_data_offset;
// Merge the vendor data in the subsection.
if (!subsection->merge(pInput, vendor_data, vendor_data_size))
return false;
}
subsection_offset += subsection_length;
} while ((subsection_offset + SubsectionLengthFieldSize) <
pInputAttrSectHdr.size());
return true;
}
size_t ELFAttribute::sizeOutput() const {
size_t total_size = FormatVersionFieldSize;
for (llvm::SmallVectorImpl<Subsection*>::const_iterator
subsec_it = m_Subsections.begin(),
subsec_end = m_Subsections.end();
subsec_it != subsec_end;
++subsec_it) {
total_size += (*subsec_it)->sizeOutput();
}
return total_size;
}
size_t ELFAttribute::emit(MemoryRegion& pRegion) const {
// ARM [ABI-addenda], 2.2.3
uint64_t total_size = 0;
// Write format-version.
char* buffer = reinterpret_cast<char*>(pRegion.begin());
buffer[0] = FormatVersion;
total_size += FormatVersionFieldSize;
for (llvm::SmallVectorImpl<Subsection*>::const_iterator
subsec_it = m_Subsections.begin(),
subsec_end = m_Subsections.end();
subsec_it != subsec_end;
++subsec_it) {
// Write out subsection.
total_size += (*subsec_it)->emit(buffer + total_size);
}
return total_size;
}
void ELFAttribute::registerAttributeData(ELFAttributeData& pAttrData) {
assert((getSubsection(pAttrData.getVendorName()) == NULL) &&
"Multiple attribute data for a vendor!");
m_Subsections.push_back(new Subsection(*this, pAttrData));
return;
}
ELFAttribute::Subsection* ELFAttribute::getSubsection(
llvm::StringRef pVendorName) const {
// Search m_Subsections linearly.
for (llvm::SmallVectorImpl<Subsection*>::const_iterator
subsec_it = m_Subsections.begin(),
subsec_end = m_Subsections.end();
subsec_it != subsec_end;
++subsec_it) {
Subsection* const subsection = *subsec_it;
if (subsection->isMyAttribute(pVendorName)) {
return subsection;
}
}
// Not found
return NULL;
}
//===----------------------------------------------------------------------===//
// ELFAttribute::Subsection
//===----------------------------------------------------------------------===//
bool ELFAttribute::Subsection::merge(const Input& pInput,
ConstAddress pData,
size_t pSize) {
const bool need_swap = (llvm::sys::IsLittleEndianHost !=
m_Parent.config().targets().isLittleEndian());
// Read attribute sub-subsection from vendor data.
//
// ARM [ABI-addenda], 2.2.4:
//
// [ Tag_File (=1) <uint32: byte-size> <attribute>*
// | Tag_Section (=2) <uint32: byte-size> <section number>* 0 <attribute>*
// | Tag_symbol (=3) <unit32: byte-size> <symbol number>* 0 <attribute>*
// ] +
const char* subsubsection_data = reinterpret_cast<const char*>(pData);
size_t remaining_size = pSize;
if (!m_AttrData.preMerge(pInput)) {
return false;
}
while (remaining_size > ELFAttribute::MinimalELFAttributeSubsectionSize) {
// The tag of sub-subsection is encoded in ULEB128.
size_t tag_size;
uint64_t tag = leb128::decode<uint64_t>(subsubsection_data, tag_size);
if ((tag_size + 4 /* byte-size */) >= remaining_size)
break;
size_t subsubsection_length =
*reinterpret_cast<const uint32_t*>(subsubsection_data + tag_size);
if (need_swap)
bswap32(subsubsection_length);
if (subsubsection_length > remaining_size) {
// The subsubsection is corrupted. Try our best to process it.
subsubsection_length = remaining_size;
}
switch (tag) {
case ELFAttributeData::Tag_File: {
ELFAttributeData::TagType tag;
ELFAttributeValue in_attr;
// The offset from the start of sub-subsection that <attribute> located
size_t attribute_offset = tag_size + 4 /* byte-size */;
const char* attr_buf = subsubsection_data + attribute_offset;
size_t attr_size = subsubsection_length - attribute_offset;
// Read attributes from the stream.
do {
if (!ELFAttributeData::ReadTag(tag, attr_buf, attr_size))
break;
ELFAttributeValue* out_attr;
bool is_newly_created;
std::tie(out_attr, is_newly_created) =
m_AttrData.getOrCreateAttributeValue(tag);
assert(out_attr != NULL);
if (is_newly_created) {
// Directly read the attribute value to the out_attr.
if (!ELFAttributeData::ReadValue(*out_attr, attr_buf, attr_size))
break;
} else {
// The attribute has been defined previously. Read the attribute
// to a temporary storage in_attr and perform the merge.
in_attr.reset();
in_attr.setType(out_attr->type());
// Read the attribute value.
if (!ELFAttributeData::ReadValue(in_attr, attr_buf, attr_size))
break;
// Merge if the read attribute value is different than current one
// in output.
if ((in_attr != *out_attr) &&
!m_AttrData.merge(m_Parent.config(), pInput, tag, in_attr)) {
// Fail to merge the attribute.
return false;
}
}
} while (attr_size > 0);
break;
}
// Skip sub-subsection tagged with Tag_Section and Tag_Symbol. They are
// deprecated since ARM [ABI-addenda] r2.09.
case ELFAttributeData::Tag_Section:
case ELFAttributeData::Tag_Symbol:
// Skip any unknown tags.
default: { break; }
}
// Update subsubsection_data and remaining_size for next.
subsubsection_data += subsubsection_length;
remaining_size -= subsubsection_length;
} // while (remaining_size > ELFAttribute::MinimalELFAttributeSubsectionSize)
return m_AttrData.postMerge(m_Parent.config(), pInput);
}
size_t ELFAttribute::Subsection::sizeOutput() const {
// ARM [ABI-addenda], 2.2.3 and 2.2.4
return ELFAttribute::SubsectionLengthFieldSize +
m_AttrData.getVendorName().length() /* vendor-name */ +
1 /* NULL-terminator for vendor-name */ + 1 /* Tag_File */ +
sizeof(uint32_t) /* length of sub-subsection */ +
m_AttrData.sizeOutput();
}
size_t ELFAttribute::Subsection::emit(char* pBuf) const {
// ARM [ABI-addenda], 2.2.3 and 2.2.4
const bool need_swap = (llvm::sys::IsLittleEndianHost !=
m_Parent.config().targets().isLittleEndian());
char* buffer = pBuf;
// The subsection-length and byte-size field in sub-subsection will be patched
// later after writing out all attribute data.
char* subsection_length_hole = NULL;
char* subsubsection_length_hole = NULL;
// Reserve space for subsection-length.
subsection_length_hole = buffer;
buffer += 4;
// Write vendor-name.
const std::string& vendor_name = m_AttrData.getVendorName();
::memcpy(buffer, vendor_name.c_str(), vendor_name.length());
buffer += vendor_name.length();
// Write NULL-terminator for vendor-name.
*buffer++ = '\0';
// Write Tag_File (0x01).
*buffer++ = '\x01';
// Reserve space for byte-size for sub-subsection.
subsubsection_length_hole = buffer;
buffer += sizeof(uint32_t);
// Write attribute data.
uint32_t subsubsection_length = m_AttrData.emit(buffer);
// Calculate value of subsection-length.
uint32_t subsection_length = (buffer - pBuf) + subsubsection_length;
// ARM [ABI-addenda] 2.2.4
//
// The byte-size in sub-subsection includes Tag_File (1-byte) and the size
// field of itself (4-byte).
subsubsection_length += 1 /* Tag_File */ + 4 /* size of byte-size */;
// Patch subsubsection_length_hole.
assert(subsubsection_length_hole != NULL);
if (need_swap)
bswap32(subsubsection_length);
::memcpy(subsubsection_length_hole, &subsubsection_length, sizeof(uint32_t));
// Write subsection-length in subsection_length_hole.
if (need_swap)
bswap32(subsection_length);
assert(subsection_length_hole != NULL);
::memcpy(subsection_length_hole, &subsection_length, sizeof(uint32_t));
return subsection_length;
}
} // namespace mcld