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.
1081 lines
37 KiB
1081 lines
37 KiB
//===- ARMELFAttributeData.h ----------------------------------------------===//
|
|
//
|
|
// The MCLinker Project
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
#include "ARMELFAttributeData.h"
|
|
|
|
#include "mcld/LinkerConfig.h"
|
|
#include "mcld/MC/Input.h"
|
|
#include "mcld/Support/LEB128.h"
|
|
#include "mcld/Support/MsgHandling.h"
|
|
#include <llvm/ADT/STLExtras.h>
|
|
|
|
namespace mcld {
|
|
|
|
const ELFAttributeValue* ARMELFAttributeData::getAttributeValue(
|
|
TagType pTag) const {
|
|
if (pTag <= Tag_Max) {
|
|
const ELFAttributeValue& attr_value = m_Attrs[pTag];
|
|
|
|
if (attr_value.isInitialized()) {
|
|
return &attr_value;
|
|
} else {
|
|
// Don't return uninitialized attribute value.
|
|
return NULL;
|
|
}
|
|
} else {
|
|
UnknownAttrsMap::const_iterator attr_it = m_UnknownAttrs.find(pTag);
|
|
|
|
if (attr_it == m_UnknownAttrs.end()) {
|
|
return NULL;
|
|
} else {
|
|
return &attr_it->second;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::pair<ELFAttributeValue*, bool>
|
|
ARMELFAttributeData::getOrCreateAttributeValue(TagType pTag) {
|
|
ELFAttributeValue* attr_value = NULL;
|
|
|
|
if (pTag <= Tag_Max) {
|
|
attr_value = &m_Attrs[pTag];
|
|
} else {
|
|
// An unknown tag encounterred.
|
|
attr_value = &m_UnknownAttrs[pTag];
|
|
}
|
|
|
|
assert(attr_value != NULL);
|
|
|
|
// Setup the value type.
|
|
if (!attr_value->isUninitialized()) {
|
|
return std::make_pair(attr_value, false);
|
|
} else {
|
|
attr_value->setType(GetAttributeValueType(pTag));
|
|
return std::make_pair(attr_value, true);
|
|
}
|
|
}
|
|
|
|
unsigned int ARMELFAttributeData::GetAttributeValueType(TagType pTag) {
|
|
// See ARM [ABI-addenda], 2.2.6.
|
|
switch (pTag) {
|
|
case Tag_compatibility: {
|
|
return (ELFAttributeValue::Int | ELFAttributeValue::String);
|
|
}
|
|
case Tag_nodefaults: {
|
|
return (ELFAttributeValue::Int | ELFAttributeValue::NoDefault);
|
|
}
|
|
case Tag_CPU_raw_name:
|
|
case Tag_CPU_name: {
|
|
return ELFAttributeValue::String;
|
|
}
|
|
default: {
|
|
if (pTag < 32)
|
|
return ELFAttributeValue::Int;
|
|
else
|
|
return ((pTag & 1) ? ELFAttributeValue::String
|
|
: ELFAttributeValue::Int);
|
|
}
|
|
}
|
|
// unreachable
|
|
}
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// Helper Functions for merge()
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
/*
|
|
* Helper function to decode value in Tag_also_compatible_with.
|
|
*
|
|
* @ref ARM [ABI-addenda], 2.3.7.3
|
|
*/
|
|
static int decode_secondary_compatibility_attribute(
|
|
const ELFAttributeValue& pValue) {
|
|
// The encoding of Tag_also_compatible_with is:
|
|
//
|
|
// Tag_also_compatible_with (=65), NTSB: data
|
|
//
|
|
// The data can be either an ULEB128-encoded number followed by a NULL byte or
|
|
// a NULL-terminated string. Currently, only the following byte sequence in
|
|
// data are currently defined:
|
|
//
|
|
// Tag_CPU_arch (=6) [The arch] 0
|
|
assert((pValue.type() == ELFAttributeValue::String) &&
|
|
"Value of Tag_also_compatible_with must be a string!");
|
|
|
|
const std::string& data = pValue.getStringValue();
|
|
|
|
// Though the integer is in LEB128 format, but they occupy only 1 byte in
|
|
// currently defined value.
|
|
if (data.length() < 2)
|
|
// Must have a byte for Tag_CPU_arch (=6)
|
|
// a byte for specifying the CPU architecture (CPU_Arch_ARM_*)
|
|
//
|
|
// Currently, the 2nd byte can only be v4T (=2) or v6-M (=11).
|
|
return -1;
|
|
|
|
if ((static_cast<uint8_t>(data[0]) == ARMELFAttributeData::Tag_CPU_arch) &&
|
|
((data[1] == ARMELFAttributeData::CPU_Arch_ARM_V4T) ||
|
|
(data[1] == ARMELFAttributeData::CPU_Arch_ARM_V6_M)))
|
|
return static_cast<uint32_t>(data[1]);
|
|
|
|
// Tag_also_compatible_with can be safely ignored.
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* This helper array keeps the ordering of the values in attributes such as
|
|
* Tag_ABI_align_needed which are sored as 1 > 2 > 0.
|
|
*/
|
|
static const int value_ordering_120[] = {0, 2, 1};
|
|
|
|
} // anonymous namespace
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// End Helper Functions for merge()
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
bool ARMELFAttributeData::merge(const LinkerConfig& pConfig,
|
|
const Input& pInput,
|
|
TagType pTag,
|
|
const ELFAttributeValue& pInAttr) {
|
|
// Pre-condition
|
|
// 1. The out_attr must be initailized and has value of the same type as
|
|
// pInAttr.
|
|
// 2. The value helf by out_attr and pInAttr must be different.
|
|
ELFAttributeValue& out_attr = m_Attrs[pTag];
|
|
|
|
// Attribute in the output must have value assigned.
|
|
assert(out_attr.isInitialized() && "No output attribute to be merged!");
|
|
|
|
switch (pTag) {
|
|
case Tag_CPU_arch: {
|
|
// Need value of Tag_also_compatible_with in the input for merge.
|
|
if (pInAttr.getIntValue() <= CPU_Arch_Max) {
|
|
m_CPUArch = pInAttr.getIntValue();
|
|
} else {
|
|
error(diag::error_unknown_cpu_arch) << pInput.name();
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case Tag_CPU_name: {
|
|
// need value of Tag_CPU_arch in the input for merge
|
|
m_CPUName = pInAttr.getStringValue();
|
|
break;
|
|
}
|
|
case Tag_CPU_raw_name: {
|
|
// need value of Tag_CPU_arch in the input for merge
|
|
m_CPURawName = pInAttr.getStringValue();
|
|
break;
|
|
}
|
|
case Tag_FP_arch: {
|
|
// need value of Tag_HardFP_use in the input for merge
|
|
m_FPArch = pInAttr.getIntValue();
|
|
break;
|
|
}
|
|
case Tag_ABI_HardFP_use: {
|
|
// need value of Tag_FP_arch in the input for merge
|
|
m_HardFPUse = pInAttr.getIntValue();
|
|
break;
|
|
}
|
|
case Tag_also_compatible_with: {
|
|
// need value of Tag_CPU_arch in the input for merge
|
|
m_SecondaryCPUArch = decode_secondary_compatibility_attribute(pInAttr);
|
|
break;
|
|
}
|
|
case Tag_ABI_VFP_args: {
|
|
// need value of Tag_ABI_FP_number_model in the input for merge
|
|
m_VFPArgs = pInAttr.getIntValue();
|
|
break;
|
|
}
|
|
// The value of these tags are integers and after merge, only the greatest
|
|
// value held by pInAttr and out_attr goes into output.
|
|
case Tag_ARM_ISA_use:
|
|
case Tag_THUMB_ISA_use:
|
|
case Tag_WMMX_arch:
|
|
case Tag_Advanced_SIMD_arch:
|
|
case Tag_ABI_FP_rounding:
|
|
case Tag_ABI_FP_exceptions:
|
|
case Tag_ABI_FP_user_exceptions:
|
|
case Tag_ABI_FP_number_model:
|
|
case Tag_FP_HP_extension:
|
|
case Tag_CPU_unaligned_access:
|
|
case Tag_T2EE_use: {
|
|
assert((out_attr.type() == ELFAttributeValue::Int) &&
|
|
(pInAttr.type() == ELFAttributeValue::Int) &&
|
|
"should have integer parameeter!");
|
|
if (pInAttr.getIntValue() > out_attr.getIntValue())
|
|
out_attr.setIntValue(pInAttr.getIntValue());
|
|
break;
|
|
}
|
|
// The value of these tags are integers and after merge, only the smallest
|
|
// value held by pInAttr and out_attr goes into output.
|
|
case Tag_ABI_align_preserved:
|
|
case Tag_ABI_PCS_RO_data: {
|
|
assert((out_attr.type() == ELFAttributeValue::Int) &&
|
|
(pInAttr.type() == ELFAttributeValue::Int) &&
|
|
"should have integer parameeter!");
|
|
if (pInAttr.getIntValue() < out_attr.getIntValue())
|
|
out_attr.setIntValue(pInAttr.getIntValue());
|
|
break;
|
|
}
|
|
// The values of these attributes are sorted as 1 > 2 > 0. And the greater
|
|
// value becomes output.
|
|
case Tag_ABI_align_needed:
|
|
case Tag_ABI_FP_denormal:
|
|
case Tag_ABI_PCS_GOT_use: {
|
|
const int in_val = pInAttr.getIntValue();
|
|
const int out_val = out_attr.getIntValue();
|
|
|
|
if (in_val <= 2) {
|
|
if (out_val <= 2) {
|
|
// Use value_ordering_120 to determine the ordering.
|
|
if (value_ordering_120[in_val] > value_ordering_120[out_val]) {
|
|
out_attr.setIntValue(in_val);
|
|
}
|
|
}
|
|
} else {
|
|
// input value > 2, for future-proofing
|
|
if (in_val > out_val) {
|
|
out_attr.setIntValue(in_val);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// These tags use the first value ever seen.
|
|
case Tag_ABI_optimization_goals:
|
|
case Tag_ABI_FP_optimization_goals: {
|
|
break;
|
|
}
|
|
// Tag_CPU_arch_profile
|
|
case Tag_CPU_arch_profile: {
|
|
if (pInAttr.getIntValue() == Arch_Profile_None)
|
|
return true;
|
|
|
|
switch (out_attr.getIntValue()) {
|
|
case Arch_Profile_None: {
|
|
out_attr.setIntValue(pInAttr.getIntValue());
|
|
break;
|
|
}
|
|
case Arch_Profile_RealOrApp: {
|
|
if (pInAttr.getIntValue() != Arch_Profile_Microcontroller)
|
|
out_attr.setIntValue(pInAttr.getIntValue());
|
|
else
|
|
warning(diag::warn_mismatch_cpu_arch_profile)
|
|
<< pInAttr.getIntValue() << pInput.name();
|
|
break;
|
|
}
|
|
default: {
|
|
// out_attr is Arch_Profile_Application or Arch_Profile_Realtime or
|
|
// Arch_Profile_Microcontroller.
|
|
if ((pInAttr.getIntValue() == Arch_Profile_RealOrApp) &&
|
|
(out_attr.getIntValue() != Arch_Profile_Microcontroller)) {
|
|
// do nothing
|
|
} else {
|
|
if (pConfig.options().warnMismatch())
|
|
warning(diag::warn_mismatch_cpu_arch_profile)
|
|
<< pInAttr.getIntValue() << pInput.name();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// Tag_MPextension_use and Tag_MPextension_use_legacy
|
|
case Tag_MPextension_use:
|
|
case Tag_MPextension_use_legacy: {
|
|
if (m_MPextensionUse < 0) {
|
|
m_MPextensionUse = pInAttr.getIntValue();
|
|
} else {
|
|
if (static_cast<unsigned>(m_MPextensionUse) != pInAttr.getIntValue()) {
|
|
warning(diag::error_mismatch_mpextension_use) << pInput.name();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// Tag_DIV_use
|
|
case Tag_DIV_use: {
|
|
if (pInAttr.getIntValue() == 2) {
|
|
// 2 means the code was permitted to use SDIV/UDIV in anyway.
|
|
out_attr.setIntValue(2);
|
|
} else {
|
|
// Merge until settling down Tag_CPU_arch.
|
|
m_DIVUse = pInAttr.getIntValue();
|
|
}
|
|
break;
|
|
}
|
|
// Tag_ABI_enum_size
|
|
case Tag_ABI_enum_size: {
|
|
if ((out_attr.getIntValue() == Enum_Unused) ||
|
|
(out_attr.getIntValue() == Enum_Containerized_As_Possible))
|
|
out_attr.setIntValue(pInAttr.getIntValue());
|
|
else if (pInAttr.getIntValue() != Enum_Containerized_As_Possible &&
|
|
pConfig.options().warnMismatch())
|
|
warning(diag::warn_mismatch_enum_size)
|
|
<< pInput.name() << pInAttr.getIntValue() << out_attr.getIntValue();
|
|
break;
|
|
}
|
|
// Tag_ABI_FP_16bit_format
|
|
case Tag_ABI_FP_16bit_format: {
|
|
// 0: doesn't use any 16-bit FP number
|
|
// 1: use IEEE 754 format 16-bit FP number
|
|
// 2: use VFPv3/Advanced SIMD "alternative format" 16-bit FP number
|
|
if (pInAttr.getIntValue() != 0) {
|
|
if (out_attr.getIntValue() == 0) {
|
|
out_attr.setIntValue(pInAttr.getIntValue());
|
|
} else {
|
|
if (pConfig.options().warnMismatch())
|
|
warning(diag::warn_mismatch_fp16_format) << pInput.name();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// Tag_nodefaults
|
|
case Tag_nodefaults: {
|
|
// There's nothing to do for this tag. It doesn't have an actual value.
|
|
break;
|
|
}
|
|
// Tag_conformance
|
|
case Tag_conformance: {
|
|
// Throw away the value if the attribute value doesn't match.
|
|
if (out_attr.getStringValue() != pInAttr.getStringValue())
|
|
out_attr.setStringValue("");
|
|
break;
|
|
}
|
|
// Tag_Virtualization_use
|
|
case Tag_Virtualization_use: {
|
|
// 0: No use of any virtualization extension
|
|
// 1: TrustZone
|
|
// 2: Virtualization extension such as HVC and ERET
|
|
// 3: TrustZone and virtualization extension are permitted
|
|
if (pInAttr.getIntValue() != 0) {
|
|
if (out_attr.getIntValue() == 0) {
|
|
out_attr.setIntValue(pInAttr.getIntValue());
|
|
} else {
|
|
if ((out_attr.getIntValue() <= 3) && (pInAttr.getIntValue() <= 3)) {
|
|
// Promote to 3
|
|
out_attr.setIntValue(3);
|
|
} else {
|
|
warning(diag::warn_unrecognized_virtualization_use)
|
|
<< pInput.name() << pInAttr.getIntValue();
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// Tag_ABI_WMMX_args
|
|
case Tag_ABI_WMMX_args: {
|
|
// There's no way to merge this value (i.e., objects contain different
|
|
// value in this tag are definitely incompatible.)
|
|
if (pConfig.options().warnMismatch())
|
|
warning(diag::warn_mismatch_abi_wmmx_args) << pInput.name();
|
|
break;
|
|
}
|
|
// Tag_PCS_config
|
|
case Tag_PCS_config: {
|
|
// 0 means no standard configuration used or no information recorded.
|
|
if (pInAttr.getIntValue() != 0) {
|
|
if (out_attr.getIntValue() == 0)
|
|
out_attr.setIntValue(pInAttr.getIntValue());
|
|
else {
|
|
// Different values in these attribute are conflict
|
|
if (pConfig.options().warnMismatch())
|
|
warning(diag::warn_mismatch_pcs_config) << pInput.name();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// Tag_ABI_PCS_R9_use
|
|
case Tag_ABI_PCS_R9_use: {
|
|
if (pInAttr.getIntValue() != R9_Unused) {
|
|
if (out_attr.getIntValue() == R9_Unused)
|
|
out_attr.setIntValue(pInAttr.getIntValue());
|
|
else {
|
|
if (pConfig.options().warnMismatch())
|
|
warning(diag::warn_mismatch_r9_use) << pInput.name();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
// Tag_ABI_PCS_RW_data
|
|
case Tag_ABI_PCS_RW_data: {
|
|
if (pInAttr.getIntValue() == RW_data_SB_Relative) {
|
|
// Require using R9 as SB (global Static Base register).
|
|
if ((out_attr.getIntValue() != R9_Unused) &&
|
|
(out_attr.getIntValue() != R9_SB) &&
|
|
pConfig.options().warnMismatch())
|
|
warning(diag::warn_mismatch_r9_use) << pInput.name();
|
|
}
|
|
// Choose the smaller value
|
|
if (pInAttr.getIntValue() < out_attr.getIntValue())
|
|
out_attr.setIntValue(pInAttr.getIntValue());
|
|
break;
|
|
}
|
|
// Tag_ABI_PCS_wchar_t
|
|
case Tag_ABI_PCS_wchar_t: {
|
|
// 0: no use of wchar_t
|
|
// 2: sizeof(wchar_t) = 2
|
|
// 4: sizeof(wchar_t) = 4
|
|
if (pInAttr.getIntValue() != 0) {
|
|
if (out_attr.getIntValue() == 0)
|
|
out_attr.setIntValue(pInAttr.getIntValue());
|
|
else {
|
|
if (pConfig.options().warnMismatch())
|
|
warning(diag::warn_mismatch_wchar_size) << pInput.name()
|
|
<< pInAttr.getIntValue()
|
|
<< out_attr.getIntValue();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
// Handle unknown attributes:
|
|
//
|
|
// Since we don't know how to merge the value of unknown attribute, we
|
|
// have to ignore it. There're two rules related to the processing (See
|
|
// ARM [ABI-addenda] 2.2.6, Coding extensibility and compatibility.):
|
|
//
|
|
// 1. For tag N where N >= 128, tag N has the same properties as
|
|
// tag N % 128.
|
|
// 2. Tag 64-127 can be safely ignored.
|
|
// 3. Tag 0-63 must be comprehended, therefore we cannot ignore.
|
|
if (pConfig.options().warnMismatch()) {
|
|
if ((pTag & 127) < 64) {
|
|
warning(diag::warn_unknown_mandatory_attribute) << pTag
|
|
<< pInput.name();
|
|
} else {
|
|
warning(diag::warn_unknown_attribute) << pTag << pInput.name();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// Helper Functions for postMerge()
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
namespace {
|
|
|
|
/*
|
|
* Helper function to encode value in Tag_also_compatible_with.
|
|
*
|
|
* @ref ARM [ABI-addenda], 2.3.7.3
|
|
*/
|
|
static void encode_secondary_compatibility_attribute(ELFAttributeValue& pValue,
|
|
int pArch) {
|
|
if ((pArch < 0) || (pArch > ARMELFAttributeData::CPU_Arch_Max)) {
|
|
pValue.setStringValue("");
|
|
} else {
|
|
char new_value[] = {
|
|
ARMELFAttributeData::Tag_CPU_arch, static_cast<char>(pArch), 0};
|
|
pValue.setStringValue(std::string(new_value, sizeof(new_value)));
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Combine the main and secondary CPU arch value
|
|
*/
|
|
static int calculate_cpu_arch(int cpu_arch, int secondary_arch) {
|
|
// short-circuit
|
|
if ((secondary_arch < 0) ||
|
|
((cpu_arch + secondary_arch) != (ARMELFAttributeData::CPU_Arch_ARM_V4T +
|
|
ARMELFAttributeData::CPU_Arch_ARM_V6_M)))
|
|
return cpu_arch;
|
|
|
|
if ((cpu_arch == ARMELFAttributeData::CPU_Arch_ARM_V4T) &&
|
|
(secondary_arch == ARMELFAttributeData::CPU_Arch_ARM_V6_M))
|
|
return ARMELFAttributeData::CPU_Arch_ARM_V4T_Plus_V6_M;
|
|
else if ((cpu_arch == ARMELFAttributeData::CPU_Arch_ARM_V6_M) &&
|
|
(secondary_arch == ARMELFAttributeData::CPU_Arch_ARM_V4T))
|
|
return ARMELFAttributeData::CPU_Arch_ARM_V4T_Plus_V6_M;
|
|
else
|
|
return cpu_arch;
|
|
}
|
|
|
|
/*
|
|
* Given a CPU arch X and a CPU arch Y in which Y is newer than X, the value in
|
|
* cpu_compatibility_table[X][Y] is the CPU arch required to run ISA both from X
|
|
* and Y. 0 in the table means unreachable and -1 means conflict architecture
|
|
* profile.
|
|
*/
|
|
#define CPU(C) ARMELFAttributeData::CPU_Arch_ARM_ ## C
|
|
static const int cpu_compatibility_table[][CPU(V4T_Plus_V6_M) + 1] = {
|
|
/* old\new ARM v6T2 ARM v6K ARM v7 ARM v6-M ARM v6S-M ARM v7E-M ARMv8, ARM v4t + v6-M */ // NOLINT
|
|
/* Pre v4 */ { CPU(V6T2), CPU(V6K), CPU(V7), -1, -1, -1, -1, -1 }, // NOLINT
|
|
/* ARM v4 */ { CPU(V6T2), CPU(V6K), CPU(V7), -1, -1, -1, -1, -1 }, // NOLINT
|
|
/* ARM v4T */ { CPU(V6T2), CPU(V6K), CPU(V7), CPU(V6K), CPU(V6K), CPU(V7E_M), CPU(V8), CPU(V4T) }, // NOLINT
|
|
/* ARM v5T */ { CPU(V6T2), CPU(V6K), CPU(V7), CPU(V6K), CPU(V6K), CPU(V7E_M), CPU(V8), CPU(V5T) }, // NOLINT
|
|
/* ARM v5TE */ { CPU(V6T2), CPU(V6K), CPU(V7), CPU(V6K), CPU(V6K), CPU(V7E_M), CPU(V8), CPU(V5TE) }, // NOLINT
|
|
/* ARM v5TEJ */ { CPU(V6T2), CPU(V6K), CPU(V7), CPU(V6K), CPU(V6K), CPU(V7E_M), CPU(V8), CPU(V5TEJ) }, // NOLINT
|
|
/* ARM v6 */ { CPU(V6T2), CPU(V6K), CPU(V7), CPU(V6K), CPU(V6K), CPU(V7E_M), CPU(V8), CPU(V6) }, // NOLINT
|
|
/* ARM v6KZ */ { CPU(V7), CPU(V6KZ), CPU(V7), CPU(V6KZ), CPU(V6KZ), CPU(V7E_M), CPU(V8), CPU(V6KZ) }, // NOLINT
|
|
/* ARM v6T2 */ { CPU(V6T2), CPU(V7), CPU(V7), CPU(V7), CPU(V7), CPU(V7E_M), CPU(V8), CPU(V6T2) }, // NOLINT
|
|
/* ARM v6K */ { 0, CPU(V6K), CPU(V7), CPU(V6K), CPU(V6K), CPU(V7E_M), CPU(V8), CPU(V6K) }, // NOLINT
|
|
/* ARM v7 */ { 0, 0, CPU(V7), CPU(V7), CPU(V7), CPU(V7E_M), CPU(V8), CPU(V7) }, // NOLINT
|
|
/* ARM v6-M */ { 0, 0, 0, CPU(V6_M), CPU(V6S_M), CPU(V7E_M), CPU(V8), CPU(V6_M) }, // NOLINT
|
|
/* ARM v6S-M */ { 0, 0, 0, 0, CPU(V6S_M), CPU(V7E_M), CPU(V8), CPU(V6S_M) }, // NOLINT
|
|
/* ARM v7E-M */ { 0, 0, 0, 0, 0, CPU(V7E_M), CPU(V8), CPU(V7E_M) }, // NOLINT
|
|
/* ARM v8 */ { 0, 0, 0, 0, 0, 0, CPU(V8), CPU(V8) }, // NOLINT
|
|
/* v4T + v6-M */ { 0, 0, 0, 0, 0, 0, 0, CPU(V4T_Plus_V6_M) } // NOLINT
|
|
};
|
|
|
|
/*
|
|
* Helper function to determine the merge of two different CPU arch.
|
|
*/
|
|
static int merge_cpu_arch(int out_cpu_arch, int in_cpu_arch) {
|
|
if (out_cpu_arch > CPU(V4T_Plus_V6_M))
|
|
return in_cpu_arch;
|
|
|
|
int new_cpu_arch, old_cpu_arch;
|
|
if (out_cpu_arch > in_cpu_arch) {
|
|
new_cpu_arch = out_cpu_arch;
|
|
old_cpu_arch = in_cpu_arch;
|
|
} else {
|
|
new_cpu_arch = in_cpu_arch;
|
|
old_cpu_arch = out_cpu_arch;
|
|
}
|
|
|
|
// No need to check the compatibility since the CPU architectures before
|
|
// V6KZ add features monotonically.
|
|
if (new_cpu_arch <= CPU(V6KZ))
|
|
return new_cpu_arch;
|
|
|
|
return cpu_compatibility_table[old_cpu_arch][new_cpu_arch - CPU(V6T2)];
|
|
}
|
|
#undef CPU
|
|
|
|
/*
|
|
* Generic CPU name is used when Tag_CPU_name is unable to guess during the
|
|
* merge of Tag_CPU_arch.
|
|
*/
|
|
static const char* generic_cpu_name_table[] = {
|
|
/* Pre v4 */ "Pre v4",
|
|
/* Pre v4 */ "ARM v4",
|
|
/* ARM v4T */ "ARM v4T",
|
|
/* ARM v5T */ "ARM v5T",
|
|
/* ARM v5TE */ "ARM v5TE",
|
|
/* ARM v5TEJ */ "ARM v5TEJ",
|
|
/* ARM v6 */ "ARM v6",
|
|
/* ARM v6KZ */ "ARM v6KZ",
|
|
/* ARM v6T2 */ "ARM v6T2",
|
|
/* ARM v6K */ "ARM v6K",
|
|
/* ARM v7 */ "ARM v7",
|
|
/* ARM v6-M */ "ARM v6-M",
|
|
/* ARM v6S-M */ "ARM v6S-M",
|
|
/* ARM v7E-M */ "ARM v7E-M",
|
|
/* ARM v8 */ "ARM v8",
|
|
};
|
|
|
|
static const char* get_generic_cpu_name(int cpu_arch) {
|
|
assert(static_cast<size_t>(cpu_arch) <
|
|
(sizeof(generic_cpu_name_table) / sizeof(generic_cpu_name_table[0])));
|
|
return generic_cpu_name_table[cpu_arch];
|
|
}
|
|
|
|
/*
|
|
* Helper functions & data used in the merge of two different FP arch.
|
|
*/
|
|
static const struct fp_config_data {
|
|
int version;
|
|
int regs;
|
|
} fp_configs[] = {
|
|
{0, 0},
|
|
{1, 16},
|
|
{2, 16},
|
|
{3, 32},
|
|
{3, 16},
|
|
{4, 32},
|
|
{4, 16},
|
|
{8, 32},
|
|
{8, 16},
|
|
};
|
|
|
|
static const size_t num_fp_configs =
|
|
sizeof(fp_configs) / sizeof(fp_config_data);
|
|
|
|
// Given h(x, y) = (x * (y >> 4) + (y >> 5))
|
|
//
|
|
// fp_config_hash_table[ h(0, 0) = 0 ] = 0
|
|
// fp_config_hash_table[ h(1, 16) = 1 ] = 1
|
|
// fp_config_hash_table[ h(2, 16) = 2 ] = 2
|
|
// fp_config_hash_table[ h(3, 32) = 7 ] = 3
|
|
// fp_config_hash_table[ h(3, 16) = 3 ] = 4
|
|
// fp_config_hash_table[ h(4, 32) = 9 ] = 5
|
|
// fp_config_hash_table[ h(4, 16) = 4 ] = 6
|
|
// fp_config_hash_table[ h(8, 32) = 17 ] = 7
|
|
// fp_config_hash_table[ h(8, 16) = 8 ] = 8
|
|
//
|
|
// h(0, 0) = 0
|
|
static const uint8_t fp_config_hash_table[] = {
|
|
#define UND static_cast<uint8_t>(-1)
|
|
/* 0 */ 0,
|
|
/* 1 */ 1,
|
|
/* 2 */ 2,
|
|
/* 3 */ 4,
|
|
/* 4 */ 6,
|
|
/* 5 */ UND,
|
|
/* 6 */ UND,
|
|
/* 7 */ 3,
|
|
/* 8 */ 8,
|
|
/* 9 */ 5,
|
|
/* 10 */ UND,
|
|
/* 11 */ UND,
|
|
/* 12 */ UND,
|
|
/* 13 */ UND,
|
|
/* 14 */ UND,
|
|
/* 15 */ UND,
|
|
/* 16 */ UND,
|
|
/* 17 */ 7,
|
|
#undef UND
|
|
};
|
|
|
|
static int calculate_fp_config_hash(const struct fp_config_data& pConfig) {
|
|
int x = pConfig.version;
|
|
int y = pConfig.regs;
|
|
return (x * (y >> 4) + (y >> 5));
|
|
}
|
|
|
|
static int get_fp_arch_of_config(const struct fp_config_data& pConfig) {
|
|
int hash = calculate_fp_config_hash(pConfig);
|
|
assert(static_cast<size_t>(hash) <
|
|
llvm::array_lengthof(fp_config_hash_table));
|
|
return fp_config_hash_table[hash];
|
|
}
|
|
|
|
static bool is_allowed_use_of_div(int cpu_arch,
|
|
int cpu_arch_profile,
|
|
int div_use) {
|
|
// 0: The code was permitted to use SDIV and UDIV in the Thumb ISA on v7-R or
|
|
// v7-M.
|
|
// 1: The code was not permitted to use SDIV and UDIV.
|
|
// 2: The code was explicitly permitted to use SDIV and UDIV.
|
|
switch (div_use) {
|
|
case 0: {
|
|
if ((cpu_arch == ARMELFAttributeData::CPU_Arch_ARM_V7) &&
|
|
((cpu_arch_profile == 'R') || (cpu_arch_profile == 'M'))) {
|
|
return true;
|
|
} else {
|
|
return (cpu_arch >= ARMELFAttributeData::CPU_Arch_ARM_V7E_M);
|
|
}
|
|
}
|
|
case 1: {
|
|
return false;
|
|
}
|
|
case 2:
|
|
// For future proofing
|
|
default: { return true; }
|
|
}
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
//===--------------------------------------------------------------------===//
|
|
// End Helper Functions for postMerge()
|
|
//===--------------------------------------------------------------------===//
|
|
|
|
bool ARMELFAttributeData::postMerge(const LinkerConfig& pConfig,
|
|
const Input& pInput) {
|
|
// Process Tag_CPU_arch, Tag_CPU_name, Tag_CPU_raw_name, and
|
|
// Tag_also_compatible_with.
|
|
ELFAttributeValue& out_cpu_arch_attr = m_Attrs[Tag_CPU_arch];
|
|
ELFAttributeValue& out_secondary_compatibility_attr =
|
|
m_Attrs[Tag_also_compatible_with];
|
|
|
|
if ((m_CurrentCPUArch < 0) && out_cpu_arch_attr.isInitialized()) {
|
|
// Current input initializes the value of Tag_CPU_arch. Validate it.
|
|
int out_cpu_arch = out_cpu_arch_attr.getIntValue();
|
|
|
|
if (out_cpu_arch > CPU_Arch_Max) {
|
|
error(diag::error_unknown_cpu_arch) << pInput.name();
|
|
return false;
|
|
}
|
|
|
|
// Initialize m_CurrentCPUArch.
|
|
int out_secondary_arch = -1;
|
|
if (out_secondary_compatibility_attr.isInitialized())
|
|
out_secondary_arch = decode_secondary_compatibility_attribute(
|
|
out_secondary_compatibility_attr);
|
|
|
|
m_CurrentCPUArch = calculate_cpu_arch(out_cpu_arch, out_secondary_arch);
|
|
}
|
|
|
|
if (m_CPUArch >= 0) {
|
|
assert(out_cpu_arch_attr.isInitialized() && "CPU arch has never set!");
|
|
assert(m_CurrentCPUArch >= 0);
|
|
|
|
int in_cpu_arch = calculate_cpu_arch(m_CPUArch, m_SecondaryCPUArch);
|
|
int result_cpu_arch = merge_cpu_arch(m_CurrentCPUArch, in_cpu_arch);
|
|
|
|
if (result_cpu_arch < 0) {
|
|
warning(diag::warn_mismatch_cpu_arch_profile) << in_cpu_arch
|
|
<< pInput.name();
|
|
} else {
|
|
if (result_cpu_arch != m_CurrentCPUArch) {
|
|
// Value of Tag_CPU_arch are going to changea.
|
|
m_CurrentCPUArch = result_cpu_arch;
|
|
|
|
// Write the result value to the output.
|
|
if (result_cpu_arch == CPU_Arch_ARM_V4T_Plus_V6_M) {
|
|
out_cpu_arch_attr.setIntValue(CPU_Arch_ARM_V4T);
|
|
encode_secondary_compatibility_attribute(
|
|
out_secondary_compatibility_attr, CPU_Arch_ARM_V6_M);
|
|
} else {
|
|
out_cpu_arch_attr.setIntValue(result_cpu_arch);
|
|
encode_secondary_compatibility_attribute(
|
|
out_secondary_compatibility_attr, -1);
|
|
}
|
|
|
|
ELFAttributeValue& out_cpu_name = m_Attrs[Tag_CPU_name];
|
|
ELFAttributeValue& out_cpu_raw_name = m_Attrs[Tag_CPU_raw_name];
|
|
|
|
if (m_CurrentCPUArch != in_cpu_arch) {
|
|
// Unable to guess the Tag_CPU_name. Use the generic name.
|
|
if (out_cpu_name.isInitialized()) {
|
|
out_cpu_name.setStringValue(get_generic_cpu_name(m_CurrentCPUArch));
|
|
}
|
|
|
|
// Tag_CPU_raw_name becomes unknown. Set to default value to disable
|
|
// it.
|
|
out_cpu_raw_name.setStringValue("");
|
|
} else {
|
|
// Use the value of Tag_CPU_name and Tag_CPU_raw_name from the input.
|
|
if (!m_CPUName.empty()) {
|
|
ELFAttributeValue& out_cpu_name = m_Attrs[Tag_CPU_name];
|
|
assert(out_cpu_name.isInitialized() && "CPU name has never set!");
|
|
out_cpu_name.setStringValue(m_CPUName);
|
|
}
|
|
|
|
if (!m_CPURawName.empty()) {
|
|
ELFAttributeValue& out_cpu_raw_name = m_Attrs[Tag_CPU_raw_name];
|
|
assert(out_cpu_raw_name.isInitialized() &&
|
|
"CPU raw name has never set!");
|
|
out_cpu_raw_name.setStringValue(m_CPURawName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // (m_CPUArch >= 0)
|
|
|
|
// Process Tag_ABI_VFP_args.
|
|
if (m_VFPArgs >= 0) {
|
|
ELFAttributeValue& out_attr = m_Attrs[Tag_ABI_VFP_args];
|
|
ELFAttributeValue& out_float_number_model_attr =
|
|
m_Attrs[Tag_ABI_FP_number_model];
|
|
|
|
assert(out_attr.isInitialized() && "VFP args has never set!");
|
|
|
|
// If the output is not permitted to use floating number, this attribute
|
|
// is ignored (migrate the value from input directly.)
|
|
if (out_float_number_model_attr.isUninitialized() ||
|
|
(out_float_number_model_attr.getIntValue() == 0)) {
|
|
// Inherit requirement from input.
|
|
out_attr.setIntValue(m_VFPArgs);
|
|
} else {
|
|
if (pConfig.options().warnMismatch())
|
|
warning(diag::warn_mismatch_vfp_args) << pInput.name();
|
|
}
|
|
}
|
|
|
|
// Process Tag_FP_arch.
|
|
ELFAttributeValue& out_fp_arch_attr = m_Attrs[Tag_FP_arch];
|
|
if (m_FPArch >= 0) {
|
|
assert(out_fp_arch_attr.isInitialized() && "FP arch has never set!");
|
|
|
|
// Tag_FP_arch
|
|
// 0: instructions requiring FP hardware are not permitted
|
|
// 1: VFP1
|
|
// 2: VFP2
|
|
// 3: VFP3 D32
|
|
// 4: VFP3 D16
|
|
// 5: VFP4 D32
|
|
// 6: VFP4 D16
|
|
// 7: ARM v8-A D32
|
|
// 8: ARM v8-A D16
|
|
if (out_fp_arch_attr.getIntValue() == 0) {
|
|
// Output has no constraints on FP hardware. Copy the requirement from
|
|
// input.
|
|
out_fp_arch_attr.setIntValue(m_FPArch);
|
|
} else if (m_FPArch == 0) {
|
|
// Input has no constraints on FP hardware. Do nothing.
|
|
} else {
|
|
// If here, both output and input contain non-zero value of Tag_FP_arch.
|
|
|
|
// Version greater than num_fp_configs is not defined. Choose the greater
|
|
// one for future-proofing.
|
|
if (static_cast<unsigned>(m_FPArch) > num_fp_configs) {
|
|
if (static_cast<unsigned>(m_FPArch) > out_fp_arch_attr.getIntValue()) {
|
|
out_fp_arch_attr.setIntValue(m_FPArch);
|
|
}
|
|
} else {
|
|
if (out_fp_arch_attr.getIntValue() < num_fp_configs) {
|
|
const struct fp_config_data& input_fp_config = fp_configs[m_FPArch];
|
|
|
|
const struct fp_config_data& output_fp_config =
|
|
fp_configs[out_fp_arch_attr.getIntValue()];
|
|
|
|
const struct fp_config_data result_fp_config = {
|
|
/*version*/ ((output_fp_config.version > input_fp_config.version)
|
|
? output_fp_config.version
|
|
: input_fp_config.version),
|
|
/* regs */ ((output_fp_config.regs > input_fp_config.regs)
|
|
? output_fp_config.regs
|
|
: input_fp_config.regs),
|
|
};
|
|
// Find the attribute value corresponding the result_fp_config
|
|
out_fp_arch_attr.setIntValue(get_fp_arch_of_config(result_fp_config));
|
|
}
|
|
}
|
|
}
|
|
} // (m_FPArch >= 0)
|
|
|
|
// Process Tag_ABI_HardFP_use.
|
|
ELFAttributeValue& out_hardfp_use_attr = m_Attrs[Tag_ABI_HardFP_use];
|
|
|
|
if (!m_HardFPUseInitialized && out_hardfp_use_attr.isInitialized()) {
|
|
m_HardFPUse = out_hardfp_use_attr.getIntValue();
|
|
m_HardFPUseInitialized = true;
|
|
}
|
|
|
|
if (m_HardFPUse >= 0) {
|
|
// Tag_ABI_HardFP_use depends on the meaning of Tag_FP_arch when it's 0.
|
|
assert(out_hardfp_use_attr.isInitialized() && "HardFP use has never set!");
|
|
|
|
if (out_fp_arch_attr.isUninitialized() ||
|
|
(out_fp_arch_attr.getIntValue() == 0)) {
|
|
// Has no constraints on FP hardware.
|
|
out_hardfp_use_attr.setIntValue(m_HardFPUse);
|
|
} else {
|
|
// Both output and input contain non-zero value of Tag_FP_arch and we have
|
|
// different Tag_ABI_HaedFP_Use settings other than 0.
|
|
if ((out_fp_arch_attr.getIntValue() > 0) && (m_HardFPUse > 0))
|
|
// Promote to 3 (The user permitted this entity to use both SP and DP
|
|
// VFP instruction.)
|
|
out_hardfp_use_attr.setIntValue(3);
|
|
}
|
|
}
|
|
|
|
// Move the value of Tag_MPextension_use_legacy to Tag_MPextension_use.
|
|
ELFAttributeValue& out_mpextension_use_legacy =
|
|
m_Attrs[Tag_MPextension_use_legacy];
|
|
|
|
ELFAttributeValue& out_mpextension_use = m_Attrs[Tag_MPextension_use];
|
|
|
|
// If Tag_MPextension_use_legacy has value, it must be introduced by current
|
|
// input since it is reset every time after the merge completed.
|
|
if (out_mpextension_use_legacy.isInitialized()) {
|
|
if (out_mpextension_use.isInitialized()) {
|
|
if (m_MPextensionUse < 0) {
|
|
// The value of Tag_MPextension_use is introduced by the current input.
|
|
// Check whether it is consistent with the one set in legacy.
|
|
m_MPextensionUse = out_mpextension_use.getIntValue();
|
|
} else {
|
|
// Current input introduces value of Tag_MPextension_use in
|
|
// m_MPextensionUse.
|
|
}
|
|
|
|
// Check the consistency between m_MPextensionUse and the value of
|
|
// Tag_MPextension_use_legacy.
|
|
if (static_cast<unsigned>(m_MPextensionUse) !=
|
|
out_mpextension_use_legacy.getIntValue()) {
|
|
error(diag::error_mismatch_mpextension_use) << pInput.name();
|
|
return false;
|
|
}
|
|
} else {
|
|
if (m_MPextensionUse < 0) {
|
|
// Tag_MPextension_use is not set. Initialize it and move the value.
|
|
out_mpextension_use.setType(ELFAttributeValue::Int);
|
|
out_mpextension_use.setIntValue(out_mpextension_use.getIntValue());
|
|
} else {
|
|
// Unreachable case since the value to unitialized attribute is directly
|
|
// assigned in ELFAttribute::Subsection::merge().
|
|
assert(false && "Tag_MPextension_use is uninitialized but have value?");
|
|
}
|
|
}
|
|
|
|
// Reset the attribute to uninitialized so it won't be included in the
|
|
// output.
|
|
out_mpextension_use_legacy.setType(ELFAttributeValue::Uninitialized);
|
|
}
|
|
|
|
// Process Tag_MPextension_use.
|
|
if (m_MPextensionUse > 0) {
|
|
assert(out_mpextension_use.isInitialized());
|
|
|
|
if (static_cast<unsigned>(m_MPextensionUse) >
|
|
out_mpextension_use.getIntValue()) {
|
|
out_mpextension_use.setIntValue(m_MPextensionUse);
|
|
}
|
|
}
|
|
|
|
// Process Tag_DIV_use.
|
|
ELFAttributeValue& out_div_use_attr = m_Attrs[Tag_DIV_use];
|
|
|
|
if (!m_DIVUseInitialized && out_div_use_attr.isInitialized()) {
|
|
// Perform the merge by reverting value of Tag_DIV_use and setup m_DIVUse.
|
|
m_DIVUse = out_div_use_attr.getIntValue();
|
|
out_div_use_attr.setIntValue(0);
|
|
m_DIVUseInitialized = true;
|
|
}
|
|
|
|
if (m_DIVUse >= 0) {
|
|
assert(out_div_use_attr.isInitialized());
|
|
|
|
const ELFAttributeValue& out_cpu_arch_profile_attr =
|
|
m_Attrs[Tag_CPU_arch_profile];
|
|
|
|
int out_cpu_arch_profile = Arch_Profile_None;
|
|
if (out_cpu_arch_profile_attr.isInitialized()) {
|
|
out_cpu_arch_profile = out_cpu_arch_profile_attr.getIntValue();
|
|
}
|
|
|
|
if (m_DIVUse == 1) {
|
|
// Input (=1) was not permitted to use SDIV and UDIV. See whether current
|
|
// output was explicitly permitted the use.
|
|
if (!is_allowed_use_of_div(m_CurrentCPUArch,
|
|
out_cpu_arch_profile,
|
|
out_div_use_attr.getIntValue())) {
|
|
out_div_use_attr.setIntValue(1);
|
|
}
|
|
} else {
|
|
if (out_div_use_attr.getIntValue() != 1) {
|
|
// Output does not explicitly forbid the use of SDIV/UDIV. See whether
|
|
// the input attribute can allow it under current CPU architecture
|
|
// profile.
|
|
if (is_allowed_use_of_div(
|
|
m_CurrentCPUArch, out_cpu_arch_profile, m_DIVUse)) {
|
|
out_div_use_attr.setIntValue(m_DIVUse);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
size_t ARMELFAttributeData::sizeOutput() const {
|
|
size_t result = 0;
|
|
|
|
// Size contributed by known attributes
|
|
for (unsigned i = 0; i <= Tag_Max; ++i) {
|
|
TagType tag = static_cast<TagType>(i);
|
|
const ELFAttributeValue& value = m_Attrs[tag];
|
|
|
|
if (value.shouldEmit()) {
|
|
result += leb128::size(static_cast<uint32_t>(tag));
|
|
result += value.getSize();
|
|
}
|
|
}
|
|
|
|
// Size contributed by unknown attributes
|
|
for (UnknownAttrsMap::const_iterator unknown_attr_it = m_UnknownAttrs.begin(),
|
|
unknown_attr_end = m_UnknownAttrs.end();
|
|
unknown_attr_it != unknown_attr_end;
|
|
++unknown_attr_it) {
|
|
TagType tag = unknown_attr_it->first;
|
|
const ELFAttributeValue& value = unknown_attr_it->second;
|
|
|
|
if (value.shouldEmit()) {
|
|
result += leb128::size(static_cast<uint32_t>(tag));
|
|
result += value.getSize();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
size_t ARMELFAttributeData::emit(char* pBuf) const {
|
|
char* buffer = pBuf;
|
|
|
|
// Tag_conformance "should be emitted first in a file-scope sub-subsection of
|
|
// the first public subsection of the attribute section."
|
|
//
|
|
// See ARM [ABI-addenda], 2.3.7.4 Conformance tag
|
|
const ELFAttributeValue& attr_conformance = m_Attrs[Tag_conformance];
|
|
|
|
if (attr_conformance.shouldEmit()) {
|
|
if (!ELFAttributeData::WriteAttribute(
|
|
Tag_conformance, attr_conformance, buffer)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Tag_nodefaults "should be emitted before any other tag in an attribute
|
|
// subsection other that the conformance tag"
|
|
//
|
|
// See ARM [ABI-addenda], 2.3.7.5 No defaults tag
|
|
const ELFAttributeValue& attr_nodefaults = m_Attrs[Tag_nodefaults];
|
|
|
|
if (attr_nodefaults.shouldEmit()) {
|
|
if (!ELFAttributeData::WriteAttribute(
|
|
Tag_nodefaults, attr_nodefaults, buffer)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Tag_conformance (=67)
|
|
// Tag_nodefaults (=64)
|
|
for (unsigned i = 0; i < Tag_nodefaults; ++i) {
|
|
TagType tag = static_cast<TagType>(i);
|
|
const ELFAttributeValue& value = m_Attrs[tag];
|
|
|
|
if (value.shouldEmit() &&
|
|
!ELFAttributeData::WriteAttribute(tag, value, buffer)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
for (unsigned i = (Tag_nodefaults + 1); i <= Tag_Max; ++i) {
|
|
TagType tag = static_cast<TagType>(i);
|
|
const ELFAttributeValue& value = m_Attrs[tag];
|
|
|
|
if (value.shouldEmit() && (i != Tag_conformance) &&
|
|
!ELFAttributeData::WriteAttribute(tag, value, buffer)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
for (UnknownAttrsMap::const_iterator unknown_attr_it = m_UnknownAttrs.begin(),
|
|
unknown_attr_end = m_UnknownAttrs.end();
|
|
unknown_attr_it != unknown_attr_end;
|
|
++unknown_attr_it) {
|
|
TagType tag = unknown_attr_it->first;
|
|
const ELFAttributeValue& value = unknown_attr_it->second;
|
|
|
|
if (value.shouldEmit() &&
|
|
!ELFAttributeData::WriteAttribute(tag, value, buffer)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return (buffer - pBuf);
|
|
}
|
|
|
|
bool ARMELFAttributeData::usingThumb() const {
|
|
int arch = m_Attrs[Tag_CPU_arch].getIntValue();
|
|
if ((arch == CPU_Arch_ARM_V6_M) || (arch == CPU_Arch_ARM_V6S_M))
|
|
return true;
|
|
if ((arch != CPU_Arch_ARM_V7) && (arch != CPU_Arch_ARM_V7E_M))
|
|
return false;
|
|
|
|
arch = m_Attrs[Tag_CPU_arch_profile].getIntValue();
|
|
return arch == Arch_Profile_Microcontroller;
|
|
}
|
|
|
|
bool ARMELFAttributeData::usingThumb2() const {
|
|
int arch = m_Attrs[Tag_CPU_arch].getIntValue();
|
|
return (arch == CPU_Arch_ARM_V6T2) || (arch == CPU_Arch_ARM_V7);
|
|
}
|
|
|
|
} // namespace mcld
|