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