You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

361 lines
12 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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