|
|
//===- 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
|