//===- 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 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 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(data[0]) == ARMELFAttributeData::Tag_CPU_arch) && ((data[1] == ARMELFAttributeData::CPU_Arch_ARM_V4T) || (data[1] == ARMELFAttributeData::CPU_Arch_ARM_V6_M))) return static_cast(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(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(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(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(-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(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(m_FPArch) > num_fp_configs) { if (static_cast(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(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(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(i); const ELFAttributeValue& value = m_Attrs[tag]; if (value.shouldEmit()) { result += leb128::size(static_cast(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(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(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(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