Index: head/contrib/llvm/tools/lldb/include/lldb/Expression/IRExecutionUnit.h =================================================================== --- head/contrib/llvm/tools/lldb/include/lldb/Expression/IRExecutionUnit.h (revision 262120) +++ head/contrib/llvm/tools/lldb/include/lldb/Expression/IRExecutionUnit.h (revision 262121) @@ -1,526 +1,502 @@ //===-- IRExecutionUnit.h ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef lldb_IRExecutionUnit_h_ #define lldb_IRExecutionUnit_h_ // C Includes // C++ Includes #include #include #include #include // Other libraries and framework includes -#include "llvm/ADT/StringRef.h" #include "llvm/IR/Module.h" // Project includes #include "lldb/lldb-forward.h" #include "lldb/lldb-private.h" #include "lldb/Core/ClangForward.h" #include "lldb/Core/DataBufferHeap.h" #include "llvm/ExecutionEngine/JITMemoryManager.h" #include "lldb/Expression/ClangExpression.h" #include "lldb/Expression/ClangExpressionParser.h" #include "lldb/Expression/IRMemoryMap.h" #include "lldb/Host/Mutex.h" namespace llvm { class Module; class ExecutionEngine; } namespace lldb_private { class Error; //---------------------------------------------------------------------- /// @class IRExecutionUnit IRExecutionUnit.h "lldb/Expression/IRExecutionUnit.h" /// @brief Contains the IR and, optionally, JIT-compiled code for a module. /// /// This class encapsulates the compiled version of an expression, in IR /// form (for interpretation purposes) and in raw machine code form (for /// execution in the target). /// /// This object wraps an IR module that comes from the expression parser, /// and knows how to use the JIT to make it into executable code. It can /// then be used as input to the IR interpreter, or the address of the /// executable code can be passed to a thread plan to run in the target. /// /// This class creates a subclass of LLVM's JITMemoryManager, because that is /// how the JIT emits code. Because LLDB needs to move JIT-compiled code /// into the target process, the IRExecutionUnit knows how to copy the /// emitted code into the target process. //---------------------------------------------------------------------- class IRExecutionUnit : public IRMemoryMap { public: //------------------------------------------------------------------ /// Constructor //------------------------------------------------------------------ IRExecutionUnit (std::unique_ptr &context_ap, std::unique_ptr &module_ap, ConstString &name, const lldb::TargetSP &target_sp, std::vector &cpu_features); //------------------------------------------------------------------ /// Destructor //------------------------------------------------------------------ ~IRExecutionUnit(); llvm::Module *GetModule() { return m_module; } llvm::Function *GetFunction() { if (m_module) return m_module->getFunction (m_name.AsCString()); else return NULL; } void GetRunnableInfo(Error &error, lldb::addr_t &func_addr, lldb::addr_t &func_end); //------------------------------------------------------------------ /// Accessors for IRForTarget and other clients that may want binary /// data placed on their behalf. The binary data is owned by the /// IRExecutionUnit unless the client explicitly chooses to free it. //------------------------------------------------------------------ lldb::addr_t WriteNow(const uint8_t *bytes, size_t size, Error &error); void FreeNow(lldb::addr_t allocation); private: //------------------------------------------------------------------ /// Look up the object in m_address_map that contains a given address, /// find where it was copied to, and return the remote address at the /// same offset into the copied entity /// /// @param[in] local_address /// The address in the debugger. /// /// @return /// The address in the target process. //------------------------------------------------------------------ lldb::addr_t GetRemoteAddressForLocal (lldb::addr_t local_address); //------------------------------------------------------------------ /// Look up the object in m_address_map that contains a given address, /// find where it was copied to, and return its address range in the /// target process /// /// @param[in] local_address /// The address in the debugger. /// /// @return /// The range of the containing object in the target process. //------------------------------------------------------------------ typedef std::pair AddrRange; AddrRange GetRemoteRangeForLocal (lldb::addr_t local_address); //------------------------------------------------------------------ /// Commit all allocations to the process and record where they were stored. /// /// @param[in] process /// The process to allocate memory in. /// /// @return /// True <=> all allocations were performed successfully. /// This method will attempt to free allocated memory if the /// operation fails. //------------------------------------------------------------------ bool CommitAllocations (lldb::ProcessSP &process_sp); //------------------------------------------------------------------ /// Report all committed allocations to the execution engine. /// /// @param[in] engine /// The execution engine to notify. //------------------------------------------------------------------ void ReportAllocations (llvm::ExecutionEngine &engine); //------------------------------------------------------------------ /// Write the contents of all allocations to the process. /// /// @param[in] local_address /// The process containing the allocations. /// /// @return /// True <=> all allocations were performed successfully. //------------------------------------------------------------------ bool WriteData (lldb::ProcessSP &process_sp); Error DisassembleFunction (Stream &stream, lldb::ProcessSP &process_sp); class MemoryManager : public llvm::JITMemoryManager { public: MemoryManager (IRExecutionUnit &parent); //------------------------------------------------------------------ /// Passthrough interface stub //------------------------------------------------------------------ virtual void setMemoryWritable (); //------------------------------------------------------------------ /// Passthrough interface stub //------------------------------------------------------------------ virtual void setMemoryExecutable (); //------------------------------------------------------------------ /// Passthrough interface stub //------------------------------------------------------------------ virtual void setPoisonMemory (bool poison) { m_default_mm_ap->setPoisonMemory (poison); } //------------------------------------------------------------------ /// Passthrough interface stub //------------------------------------------------------------------ virtual void AllocateGOT() { m_default_mm_ap->AllocateGOT(); } //------------------------------------------------------------------ /// Passthrough interface stub //------------------------------------------------------------------ virtual uint8_t *getGOTBase() const { return m_default_mm_ap->getGOTBase(); } //------------------------------------------------------------------ /// Passthrough interface stub //------------------------------------------------------------------ virtual uint8_t *startFunctionBody(const llvm::Function *F, uintptr_t &ActualSize); //------------------------------------------------------------------ /// Allocate room for a dyld stub for a lazy-referenced function, /// and add it to the m_stubs map /// /// @param[in] F /// The function being referenced. /// /// @param[in] StubSize /// The size of the stub. /// /// @param[in] Alignment /// The required alignment of the stub. /// /// @return /// Allocated space for the stub. //------------------------------------------------------------------ virtual uint8_t *allocateStub(const llvm::GlobalValue* F, unsigned StubSize, unsigned Alignment); //------------------------------------------------------------------ /// Complete the body of a function, and add it to the m_functions map /// /// @param[in] F /// The function being completed. /// /// @param[in] FunctionStart /// The first instruction of the function. /// /// @param[in] FunctionEnd /// The last byte of the last instruction of the function. //------------------------------------------------------------------ virtual void endFunctionBody(const llvm::Function *F, uint8_t *FunctionStart, uint8_t *FunctionEnd); //------------------------------------------------------------------ /// Allocate space for an unspecified purpose, and add it to the /// m_spaceBlocks map /// /// @param[in] Size /// The size of the area. /// /// @param[in] Alignment /// The required alignment of the area. /// /// @return /// Allocated space. //------------------------------------------------------------------ virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment); //------------------------------------------------------------------ /// Allocate space for executable code, and add it to the /// m_spaceBlocks map /// /// @param[in] Size /// The size of the area. /// /// @param[in] Alignment /// The required alignment of the area. /// /// @param[in] SectionID /// A unique identifier for the section. /// /// @return /// Allocated space. //------------------------------------------------------------------ virtual uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID); + unsigned SectionID, + llvm::StringRef SectionName); //------------------------------------------------------------------ /// Allocate space for data, and add it to the m_spaceBlocks map /// /// @param[in] Size /// The size of the area. /// /// @param[in] Alignment /// The required alignment of the area. /// /// @param[in] SectionID /// A unique identifier for the section. /// /// @param[in] IsReadOnly /// Flag indicating the section is read-only. /// /// @return /// Allocated space. //------------------------------------------------------------------ virtual uint8_t *allocateDataSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID, bool IsReadOnly); + unsigned SectionID, + llvm::StringRef SectionName, + bool IsReadOnly); //------------------------------------------------------------------ /// Allocate space for a global variable, and add it to the /// m_spaceBlocks map /// /// @param[in] Size /// The size of the variable. /// /// @param[in] Alignment /// The required alignment of the variable. /// /// @return /// Allocated space for the global. //------------------------------------------------------------------ virtual uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment); //------------------------------------------------------------------ /// Called when object loading is complete and section page /// permissions can be applied. Currently unimplemented for LLDB. /// /// @param[out] ErrMsg /// The error that prevented the page protection from succeeding. /// /// @return /// True in case of failure, false in case of success. //------------------------------------------------------------------ - bool applyPermissions(std::string *ErrMsg) { return false; } + virtual bool finalizeMemory(std::string *ErrMsg) { + // TODO: Ensure that the instruction cache is flushed because + // relocations are updated by dy-load. See: + // sys::Memory::InvalidateInstructionCache + // llvm::SectionMemoryManager + return false; + } //------------------------------------------------------------------ /// Passthrough interface stub //------------------------------------------------------------------ virtual void deallocateFunctionBody(void *Body); //------------------------------------------------------------------ /// Passthrough interface stub //------------------------------------------------------------------ - virtual uint8_t* startExceptionTable(const llvm::Function* F, - uintptr_t &ActualSize); - - //------------------------------------------------------------------ - /// Complete the exception table for a function, and add it to the - /// m_exception_tables map - /// - /// @param[in] F - /// The function whose exception table is being written. - /// - /// @param[in] TableStart - /// The first byte of the exception table. - /// - /// @param[in] TableEnd - /// The last byte of the exception table. - /// - /// @param[in] FrameRegister - /// I don't know what this does, but it's passed through. - //------------------------------------------------------------------ - virtual void endExceptionTable(const llvm::Function *F, - uint8_t *TableStart, - uint8_t *TableEnd, - uint8_t* FrameRegister); - - //------------------------------------------------------------------ - /// Passthrough interface stub - //------------------------------------------------------------------ - virtual void deallocateExceptionTable(void *ET); - - //------------------------------------------------------------------ - /// Passthrough interface stub - //------------------------------------------------------------------ virtual size_t GetDefaultCodeSlabSize() { return m_default_mm_ap->GetDefaultCodeSlabSize(); } //------------------------------------------------------------------ /// Passthrough interface stub //------------------------------------------------------------------ virtual size_t GetDefaultDataSlabSize() { return m_default_mm_ap->GetDefaultDataSlabSize(); } virtual size_t GetDefaultStubSlabSize() { return m_default_mm_ap->GetDefaultStubSlabSize(); } //------------------------------------------------------------------ /// Passthrough interface stub //------------------------------------------------------------------ virtual unsigned GetNumCodeSlabs() { return m_default_mm_ap->GetNumCodeSlabs(); } //------------------------------------------------------------------ /// Passthrough interface stub //------------------------------------------------------------------ virtual unsigned GetNumDataSlabs() { return m_default_mm_ap->GetNumDataSlabs(); } //------------------------------------------------------------------ /// Passthrough interface stub //------------------------------------------------------------------ virtual unsigned GetNumStubSlabs() { return m_default_mm_ap->GetNumStubSlabs(); } virtual void registerEHFrames(uint8_t *Addr, uint64_t LoadAddr, size_t Size) { - return m_default_mm_ap->registerEHFrames(llvm::StringRef((const char *)Addr, Size)); + return m_default_mm_ap->registerEHFrames(Addr, LoadAddr, Size); } //------------------------------------------------------------------ /// Passthrough interface stub //------------------------------------------------------------------ virtual void *getPointerToNamedFunction(const std::string &Name, bool AbortOnFailure = true) { return m_default_mm_ap->getPointerToNamedFunction(Name, AbortOnFailure); } private: std::unique_ptr m_default_mm_ap; ///< The memory allocator to use in actually creating space. All calls are passed through to it. IRExecutionUnit &m_parent; ///< The execution unit this is a proxy for. }; //---------------------------------------------------------------------- /// @class JittedFunction IRExecutionUnit.h "lldb/Expression/IRExecutionUnit.h" /// @brief Encapsulates a single function that has been generated by the JIT. /// /// Functions that have been generated by the JIT are first resident in the /// local process, and then placed in the target process. JittedFunction /// represents a function possibly resident in both. //---------------------------------------------------------------------- struct JittedFunction { std::string m_name; ///< The function's name lldb::addr_t m_local_addr; ///< The address of the function in LLDB's memory lldb::addr_t m_remote_addr; ///< The address of the function in the target's memory //------------------------------------------------------------------ /// Constructor /// /// Initializes class variabes. /// /// @param[in] name /// The name of the function. /// /// @param[in] local_addr /// The address of the function in LLDB, or LLDB_INVALID_ADDRESS if /// it is not present in LLDB's memory. /// /// @param[in] remote_addr /// The address of the function in the target, or LLDB_INVALID_ADDRESS /// if it is not present in the target's memory. //------------------------------------------------------------------ JittedFunction (const char *name, lldb::addr_t local_addr = LLDB_INVALID_ADDRESS, lldb::addr_t remote_addr = LLDB_INVALID_ADDRESS) : m_name (name), m_local_addr (local_addr), m_remote_addr (remote_addr) { } }; static const unsigned eSectionIDInvalid = (unsigned)-1; //---------------------------------------------------------------------- /// @class AllocationRecord IRExecutionUnit.h "lldb/Expression/IRExecutionUnit.h" /// @brief Enacpsulates a single allocation request made by the JIT. /// /// Allocations made by the JIT are first queued up and then applied in /// bulk to the underlying process. //---------------------------------------------------------------------- struct AllocationRecord { lldb::addr_t m_process_address; uintptr_t m_host_address; uint32_t m_permissions; size_t m_size; unsigned m_alignment; unsigned m_section_id; AllocationRecord (uintptr_t host_address, uint32_t permissions, size_t size, unsigned alignment, unsigned section_id = eSectionIDInvalid) : m_process_address(LLDB_INVALID_ADDRESS), m_host_address(host_address), m_permissions(permissions), m_size(size), m_alignment(alignment), m_section_id(section_id) { } void dump (Log *log); }; typedef std::vector RecordVector; RecordVector m_records; std::unique_ptr m_context_ap; std::unique_ptr m_execution_engine_ap; std::unique_ptr m_module_ap; ///< Holder for the module until it's been handed off llvm::Module *m_module; ///< Owned by the execution engine std::vector m_cpu_features; llvm::SmallVector m_jitted_functions; ///< A vector of all functions that have been JITted into machine code const ConstString m_name; std::atomic m_did_jit; lldb::addr_t m_function_load_addr; lldb::addr_t m_function_end_load_addr; }; } // namespace lldb_private #endif // lldb_IRExecutionUnit_h_ Index: head/contrib/llvm/tools/lldb/source/Core/ArchSpec.cpp =================================================================== --- head/contrib/llvm/tools/lldb/source/Core/ArchSpec.cpp (revision 262120) +++ head/contrib/llvm/tools/lldb/source/Core/ArchSpec.cpp (revision 262121) @@ -1,928 +1,928 @@ //===-- ArchSpec.cpp --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Core/ArchSpec.h" #include #include #include #include "llvm/Support/COFF.h" #include "llvm/Support/ELF.h" #include "llvm/Support/Host.h" #include "llvm/Support/MachO.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Host/Endian.h" #include "lldb/Host/Host.h" #include "lldb/Target/Platform.h" using namespace lldb; using namespace lldb_private; #define ARCH_SPEC_SEPARATOR_CHAR '-' static bool cores_match (const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_inverse, bool enforce_exact_match); namespace lldb_private { struct CoreDefinition { ByteOrder default_byte_order; uint32_t addr_byte_size; uint32_t min_opcode_byte_size; uint32_t max_opcode_byte_size; llvm::Triple::ArchType machine; ArchSpec::Core core; const char *name; }; } // This core information can be looked using the ArchSpec::Core as the index static const CoreDefinition g_core_definitions[ArchSpec::kNumCores] = { { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_generic , "arm" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv4 , "armv4" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv4t , "armv4t" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv5 , "armv5" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv5e , "armv5e" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv5t , "armv5t" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv6 , "armv6" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv6m , "armv6m" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7 , "armv7" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7f , "armv7f" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7s , "armv7s" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7k , "armv7k" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7m , "armv7m" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7em , "armv7em" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_xscale , "xscale" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumb , "thumb" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv4t , "thumbv4t" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv5 , "thumbv5" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv5e , "thumbv5e" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv6 , "thumbv6" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv6m , "thumbv6m" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7 , "thumbv7" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7f , "thumbv7f" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7s , "thumbv7s" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7k , "thumbv7k" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7m , "thumbv7m" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7em , "thumbv7em" }, { eByteOrderBig , 8, 4, 4, llvm::Triple::mips64 , ArchSpec::eCore_mips64 , "mips64" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_generic , "ppc" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc601 , "ppc601" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc602 , "ppc602" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc603 , "ppc603" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc603e , "ppc603e" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc603ev , "ppc603ev" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc604 , "ppc604" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc604e , "ppc604e" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc620 , "ppc620" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc750 , "ppc750" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc7400 , "ppc7400" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc7450 , "ppc7450" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc970 , "ppc970" }, { eByteOrderBig , 8, 4, 4, llvm::Triple::ppc64 , ArchSpec::eCore_ppc64_generic , "ppc64" }, { eByteOrderBig , 8, 4, 4, llvm::Triple::ppc64 , ArchSpec::eCore_ppc64_ppc970_64 , "ppc970-64" }, { eByteOrderLittle, 4, 4, 4, llvm::Triple::sparc , ArchSpec::eCore_sparc_generic , "sparc" }, { eByteOrderLittle, 8, 4, 4, llvm::Triple::sparcv9, ArchSpec::eCore_sparc9_generic , "sparcv9" }, { eByteOrderLittle, 4, 1, 15, llvm::Triple::x86 , ArchSpec::eCore_x86_32_i386 , "i386" }, { eByteOrderLittle, 4, 1, 15, llvm::Triple::x86 , ArchSpec::eCore_x86_32_i486 , "i486" }, { eByteOrderLittle, 4, 1, 15, llvm::Triple::x86 , ArchSpec::eCore_x86_32_i486sx , "i486sx" }, { eByteOrderLittle, 8, 1, 15, llvm::Triple::x86_64 , ArchSpec::eCore_x86_64_x86_64 , "x86_64" }, { eByteOrderLittle, 4, 4, 4 , llvm::Triple::UnknownArch , ArchSpec::eCore_uknownMach32 , "unknown-mach-32" }, { eByteOrderLittle, 8, 4, 4 , llvm::Triple::UnknownArch , ArchSpec::eCore_uknownMach64 , "unknown-mach-64" } }; struct ArchDefinitionEntry { ArchSpec::Core core; uint32_t cpu; uint32_t sub; uint32_t cpu_mask; uint32_t sub_mask; }; struct ArchDefinition { ArchitectureType type; size_t num_entries; const ArchDefinitionEntry *entries; const char *name; }; size_t ArchSpec::AutoComplete (const char *name, StringList &matches) { uint32_t i; if (name && name[0]) { for (i = 0; i < ArchSpec::kNumCores; ++i) { if (NameMatches(g_core_definitions[i].name, eNameMatchStartsWith, name)) matches.AppendString (g_core_definitions[i].name); } } else { for (i = 0; i < ArchSpec::kNumCores; ++i) matches.AppendString (g_core_definitions[i].name); } return matches.GetSize(); } #define CPU_ANY (UINT32_MAX) //===----------------------------------------------------------------------===// // A table that gets searched linearly for matches. This table is used to // convert cpu type and subtypes to architecture names, and to convert // architecture names to cpu types and subtypes. The ordering is important and // allows the precedence to be set when the table is built. #define SUBTYPE_MASK 0x00FFFFFFu static const ArchDefinitionEntry g_macho_arch_entries[] = { - { ArchSpec::eCore_arm_generic , llvm::MachO::CPUTypeARM , CPU_ANY, UINT32_MAX , UINT32_MAX }, - { ArchSpec::eCore_arm_generic , llvm::MachO::CPUTypeARM , 0 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv4 , llvm::MachO::CPUTypeARM , 5 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv4t , llvm::MachO::CPUTypeARM , 5 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv6 , llvm::MachO::CPUTypeARM , 6 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv6m , llvm::MachO::CPUTypeARM , 14 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv5 , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv5e , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv5t , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_xscale , llvm::MachO::CPUTypeARM , 8 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv7 , llvm::MachO::CPUTypeARM , 9 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv7f , llvm::MachO::CPUTypeARM , 10 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv7s , llvm::MachO::CPUTypeARM , 11 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv7k , llvm::MachO::CPUTypeARM , 12 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv7m , llvm::MachO::CPUTypeARM , 15 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv7em , llvm::MachO::CPUTypeARM , 16 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumb , llvm::MachO::CPUTypeARM , 0 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv4t , llvm::MachO::CPUTypeARM , 5 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv5 , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv5e , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv6 , llvm::MachO::CPUTypeARM , 6 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv6m , llvm::MachO::CPUTypeARM , 14 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv7 , llvm::MachO::CPUTypeARM , 9 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv7f , llvm::MachO::CPUTypeARM , 10 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv7s , llvm::MachO::CPUTypeARM , 11 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv7k , llvm::MachO::CPUTypeARM , 12 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv7m , llvm::MachO::CPUTypeARM , 15 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv7em , llvm::MachO::CPUTypeARM , 16 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_generic , llvm::MachO::CPUTypePowerPC , CPU_ANY, UINT32_MAX , UINT32_MAX }, - { ArchSpec::eCore_ppc_generic , llvm::MachO::CPUTypePowerPC , 0 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc601 , llvm::MachO::CPUTypePowerPC , 1 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc602 , llvm::MachO::CPUTypePowerPC , 2 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc603 , llvm::MachO::CPUTypePowerPC , 3 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc603e , llvm::MachO::CPUTypePowerPC , 4 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc603ev , llvm::MachO::CPUTypePowerPC , 5 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc604 , llvm::MachO::CPUTypePowerPC , 6 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc604e , llvm::MachO::CPUTypePowerPC , 7 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc620 , llvm::MachO::CPUTypePowerPC , 8 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc750 , llvm::MachO::CPUTypePowerPC , 9 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc7400 , llvm::MachO::CPUTypePowerPC , 10 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc7450 , llvm::MachO::CPUTypePowerPC , 11 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc970 , llvm::MachO::CPUTypePowerPC , 100 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc64_generic , llvm::MachO::CPUTypePowerPC64 , 0 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc64_ppc970_64 , llvm::MachO::CPUTypePowerPC64 , 100 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPUTypeI386 , 3 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_32_i486 , llvm::MachO::CPUTypeI386 , 4 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_32_i486sx , llvm::MachO::CPUTypeI386 , 0x84 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPUTypeI386 , CPU_ANY, UINT32_MAX , UINT32_MAX }, - { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPUTypeX86_64 , 3 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPUTypeX86_64 , 4 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPUTypeX86_64 , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_arm_generic , llvm::MachO::CPU_TYPE_ARM , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_arm_generic , llvm::MachO::CPU_TYPE_ARM , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv4 , llvm::MachO::CPU_TYPE_ARM , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv4t , llvm::MachO::CPU_TYPE_ARM , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv6 , llvm::MachO::CPU_TYPE_ARM , 6 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv6m , llvm::MachO::CPU_TYPE_ARM , 14 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv5 , llvm::MachO::CPU_TYPE_ARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv5e , llvm::MachO::CPU_TYPE_ARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv5t , llvm::MachO::CPU_TYPE_ARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_xscale , llvm::MachO::CPU_TYPE_ARM , 8 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7 , llvm::MachO::CPU_TYPE_ARM , 9 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7f , llvm::MachO::CPU_TYPE_ARM , 10 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7s , llvm::MachO::CPU_TYPE_ARM , 11 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7k , llvm::MachO::CPU_TYPE_ARM , 12 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7m , llvm::MachO::CPU_TYPE_ARM , 15 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7em , llvm::MachO::CPU_TYPE_ARM , 16 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumb , llvm::MachO::CPU_TYPE_ARM , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv4t , llvm::MachO::CPU_TYPE_ARM , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv5 , llvm::MachO::CPU_TYPE_ARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv5e , llvm::MachO::CPU_TYPE_ARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv6 , llvm::MachO::CPU_TYPE_ARM , 6 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv6m , llvm::MachO::CPU_TYPE_ARM , 14 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7 , llvm::MachO::CPU_TYPE_ARM , 9 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7f , llvm::MachO::CPU_TYPE_ARM , 10 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7s , llvm::MachO::CPU_TYPE_ARM , 11 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7k , llvm::MachO::CPU_TYPE_ARM , 12 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7m , llvm::MachO::CPU_TYPE_ARM , 15 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7em , llvm::MachO::CPU_TYPE_ARM , 16 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_generic , llvm::MachO::CPU_TYPE_POWERPC , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_ppc_generic , llvm::MachO::CPU_TYPE_POWERPC , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc601 , llvm::MachO::CPU_TYPE_POWERPC , 1 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc602 , llvm::MachO::CPU_TYPE_POWERPC , 2 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc603 , llvm::MachO::CPU_TYPE_POWERPC , 3 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc603e , llvm::MachO::CPU_TYPE_POWERPC , 4 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc603ev , llvm::MachO::CPU_TYPE_POWERPC , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc604 , llvm::MachO::CPU_TYPE_POWERPC , 6 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc604e , llvm::MachO::CPU_TYPE_POWERPC , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc620 , llvm::MachO::CPU_TYPE_POWERPC , 8 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc750 , llvm::MachO::CPU_TYPE_POWERPC , 9 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc7400 , llvm::MachO::CPU_TYPE_POWERPC , 10 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc7450 , llvm::MachO::CPU_TYPE_POWERPC , 11 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc970 , llvm::MachO::CPU_TYPE_POWERPC , 100 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc64_generic , llvm::MachO::CPU_TYPE_POWERPC64 , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc64_ppc970_64 , llvm::MachO::CPU_TYPE_POWERPC64 , 100 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPU_TYPE_I386 , 3 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i486 , llvm::MachO::CPU_TYPE_I386 , 4 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i486sx , llvm::MachO::CPU_TYPE_I386 , 0x84 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPU_TYPE_I386 , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPU_TYPE_X86_64 , 3 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPU_TYPE_X86_64 , 4 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPU_TYPE_X86_64 , CPU_ANY, UINT32_MAX , UINT32_MAX }, // Catch any unknown mach architectures so we can always use the object and symbol mach-o files - { ArchSpec::eCore_uknownMach32 , 0 , 0 , 0xFF000000u, 0x00000000u }, - { ArchSpec::eCore_uknownMach64 , llvm::MachO::CPUArchABI64 , 0 , 0xFF000000u, 0x00000000u } + { ArchSpec::eCore_uknownMach32 , 0 , 0 , 0xFF000000u, 0x00000000u }, + { ArchSpec::eCore_uknownMach64 , llvm::MachO::CPU_ARCH_ABI64 , 0 , 0xFF000000u, 0x00000000u } }; static const ArchDefinition g_macho_arch_def = { eArchTypeMachO, sizeof(g_macho_arch_entries)/sizeof(g_macho_arch_entries[0]), g_macho_arch_entries, "mach-o" }; //===----------------------------------------------------------------------===// // A table that gets searched linearly for matches. This table is used to // convert cpu type and subtypes to architecture names, and to convert // architecture names to cpu types and subtypes. The ordering is important and // allows the precedence to be set when the table is built. static const ArchDefinitionEntry g_elf_arch_entries[] = { { ArchSpec::eCore_sparc_generic , llvm::ELF::EM_SPARC , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Sparc { ArchSpec::eCore_x86_32_i386 , llvm::ELF::EM_386 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Intel 80386 { ArchSpec::eCore_x86_32_i486 , llvm::ELF::EM_486 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Intel 486 (deprecated) { ArchSpec::eCore_ppc_generic , llvm::ELF::EM_PPC , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // PowerPC { ArchSpec::eCore_ppc64_generic , llvm::ELF::EM_PPC64 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // PowerPC64 { ArchSpec::eCore_arm_generic , llvm::ELF::EM_ARM , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // ARM { ArchSpec::eCore_sparc9_generic , llvm::ELF::EM_SPARCV9, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // SPARC V9 { ArchSpec::eCore_x86_64_x86_64 , llvm::ELF::EM_X86_64 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // AMD64 { ArchSpec::eCore_mips64 , llvm::ELF::EM_MIPS , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu } // MIPS }; static const ArchDefinition g_elf_arch_def = { eArchTypeELF, sizeof(g_elf_arch_entries)/sizeof(g_elf_arch_entries[0]), g_elf_arch_entries, "elf", }; static const ArchDefinitionEntry g_coff_arch_entries[] = { { ArchSpec::eCore_x86_32_i386 , llvm::COFF::IMAGE_FILE_MACHINE_I386 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Intel 80386 { ArchSpec::eCore_ppc_generic , llvm::COFF::IMAGE_FILE_MACHINE_POWERPC , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // PowerPC { ArchSpec::eCore_ppc_generic , llvm::COFF::IMAGE_FILE_MACHINE_POWERPCFP, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // PowerPC (with FPU) { ArchSpec::eCore_arm_generic , llvm::COFF::IMAGE_FILE_MACHINE_ARM , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // ARM { ArchSpec::eCore_arm_armv7 , llvm::COFF::IMAGE_FILE_MACHINE_ARMV7 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // ARMv7 { ArchSpec::eCore_thumb , llvm::COFF::IMAGE_FILE_MACHINE_THUMB , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // ARMv7 { ArchSpec::eCore_x86_64_x86_64, llvm::COFF::IMAGE_FILE_MACHINE_AMD64 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu } // AMD64 }; static const ArchDefinition g_coff_arch_def = { eArchTypeCOFF, sizeof(g_coff_arch_entries)/sizeof(g_coff_arch_entries[0]), g_coff_arch_entries, "pe-coff", }; //===----------------------------------------------------------------------===// // Table of all ArchDefinitions static const ArchDefinition *g_arch_definitions[] = { &g_macho_arch_def, &g_elf_arch_def, &g_coff_arch_def }; static const size_t k_num_arch_definitions = sizeof(g_arch_definitions) / sizeof(g_arch_definitions[0]); //===----------------------------------------------------------------------===// // Static helper functions. // Get the architecture definition for a given object type. static const ArchDefinition * FindArchDefinition (ArchitectureType arch_type) { for (unsigned int i = 0; i < k_num_arch_definitions; ++i) { const ArchDefinition *def = g_arch_definitions[i]; if (def->type == arch_type) return def; } return NULL; } // Get an architecture definition by name. static const CoreDefinition * FindCoreDefinition (llvm::StringRef name) { for (unsigned int i = 0; i < ArchSpec::kNumCores; ++i) { if (name.equals_lower(g_core_definitions[i].name)) return &g_core_definitions[i]; } return NULL; } static inline const CoreDefinition * FindCoreDefinition (ArchSpec::Core core) { if (core >= 0 && core < ArchSpec::kNumCores) return &g_core_definitions[core]; return NULL; } // Get a definition entry by cpu type and subtype. static const ArchDefinitionEntry * FindArchDefinitionEntry (const ArchDefinition *def, uint32_t cpu, uint32_t sub) { if (def == NULL) return NULL; const ArchDefinitionEntry *entries = def->entries; for (size_t i = 0; i < def->num_entries; ++i) { if (entries[i].cpu == (cpu & entries[i].cpu_mask)) if (entries[i].sub == (sub & entries[i].sub_mask)) return &entries[i]; } return NULL; } static const ArchDefinitionEntry * FindArchDefinitionEntry (const ArchDefinition *def, ArchSpec::Core core) { if (def == NULL) return NULL; const ArchDefinitionEntry *entries = def->entries; for (size_t i = 0; i < def->num_entries; ++i) { if (entries[i].core == core) return &entries[i]; } return NULL; } //===----------------------------------------------------------------------===// // Constructors and destructors. ArchSpec::ArchSpec() : m_triple (), m_core (kCore_invalid), m_byte_order (eByteOrderInvalid) { } ArchSpec::ArchSpec (const char *triple_cstr, Platform *platform) : m_triple (), m_core (kCore_invalid), m_byte_order (eByteOrderInvalid) { if (triple_cstr) SetTriple(triple_cstr, platform); } ArchSpec::ArchSpec (const char *triple_cstr) : m_triple (), m_core (kCore_invalid), m_byte_order (eByteOrderInvalid) { if (triple_cstr) SetTriple(triple_cstr); } ArchSpec::ArchSpec(const llvm::Triple &triple) : m_triple (), m_core (kCore_invalid), m_byte_order (eByteOrderInvalid) { SetTriple(triple); } ArchSpec::ArchSpec (ArchitectureType arch_type, uint32_t cpu, uint32_t subtype) : m_triple (), m_core (kCore_invalid), m_byte_order (eByteOrderInvalid) { SetArchitecture (arch_type, cpu, subtype); } ArchSpec::~ArchSpec() { } //===----------------------------------------------------------------------===// // Assignment and initialization. const ArchSpec& ArchSpec::operator= (const ArchSpec& rhs) { if (this != &rhs) { m_triple = rhs.m_triple; m_core = rhs.m_core; m_byte_order = rhs.m_byte_order; } return *this; } void ArchSpec::Clear() { m_triple = llvm::Triple(); m_core = kCore_invalid; m_byte_order = eByteOrderInvalid; } //===----------------------------------------------------------------------===// // Predicates. const char * ArchSpec::GetArchitectureName () const { const CoreDefinition *core_def = FindCoreDefinition (m_core); if (core_def) return core_def->name; return "unknown"; } uint32_t ArchSpec::GetMachOCPUType () const { const CoreDefinition *core_def = FindCoreDefinition (m_core); if (core_def) { const ArchDefinitionEntry *arch_def = FindArchDefinitionEntry (&g_macho_arch_def, core_def->core); if (arch_def) { return arch_def->cpu; } } return LLDB_INVALID_CPUTYPE; } uint32_t ArchSpec::GetMachOCPUSubType () const { const CoreDefinition *core_def = FindCoreDefinition (m_core); if (core_def) { const ArchDefinitionEntry *arch_def = FindArchDefinitionEntry (&g_macho_arch_def, core_def->core); if (arch_def) { return arch_def->sub; } } return LLDB_INVALID_CPUTYPE; } llvm::Triple::ArchType ArchSpec::GetMachine () const { const CoreDefinition *core_def = FindCoreDefinition (m_core); if (core_def) return core_def->machine; return llvm::Triple::UnknownArch; } uint32_t ArchSpec::GetAddressByteSize() const { const CoreDefinition *core_def = FindCoreDefinition (m_core); if (core_def) return core_def->addr_byte_size; return 0; } ByteOrder ArchSpec::GetDefaultEndian () const { const CoreDefinition *core_def = FindCoreDefinition (m_core); if (core_def) return core_def->default_byte_order; return eByteOrderInvalid; } lldb::ByteOrder ArchSpec::GetByteOrder () const { if (m_byte_order == eByteOrderInvalid) return GetDefaultEndian(); return m_byte_order; } //===----------------------------------------------------------------------===// // Mutators. bool ArchSpec::SetTriple (const llvm::Triple &triple) { m_triple = triple; llvm::StringRef arch_name (m_triple.getArchName()); const CoreDefinition *core_def = FindCoreDefinition (arch_name); if (core_def) { m_core = core_def->core; // Set the byte order to the default byte order for an architecture. // This can be modified if needed for cases when cores handle both // big and little endian m_byte_order = core_def->default_byte_order; } else { Clear(); } return IsValid(); } static bool ParseMachCPUDashSubtypeTriple (const char *triple_cstr, ArchSpec &arch) { // Accept "12-10" or "12.10" as cpu type/subtype if (isdigit(triple_cstr[0])) { char *end = NULL; errno = 0; uint32_t cpu = (uint32_t)::strtoul (triple_cstr, &end, 0); if (errno == 0 && cpu != 0 && end && ((*end == '-') || (*end == '.'))) { errno = 0; uint32_t sub = (uint32_t)::strtoul (end + 1, &end, 0); if (errno == 0 && end && ((*end == '-') || (*end == '.') || (*end == '\0'))) { if (arch.SetArchitecture (eArchTypeMachO, cpu, sub)) { if (*end == '-') { llvm::StringRef vendor_os (end + 1); size_t dash_pos = vendor_os.find('-'); if (dash_pos != llvm::StringRef::npos) { llvm::StringRef vendor_str(vendor_os.substr(0, dash_pos)); arch.GetTriple().setVendorName(vendor_str); const size_t vendor_start_pos = dash_pos+1; dash_pos = vendor_os.find('-', vendor_start_pos); if (dash_pos == llvm::StringRef::npos) { if (vendor_start_pos < vendor_os.size()) arch.GetTriple().setOSName(vendor_os.substr(vendor_start_pos)); } else { arch.GetTriple().setOSName(vendor_os.substr(vendor_start_pos, dash_pos - vendor_start_pos)); } } } return true; } } } } return false; } bool ArchSpec::SetTriple (const char *triple_cstr) { if (triple_cstr && triple_cstr[0]) { if (ParseMachCPUDashSubtypeTriple (triple_cstr, *this)) return true; llvm::StringRef triple_stref (triple_cstr); if (triple_stref.startswith (LLDB_ARCH_DEFAULT)) { // Special case for the current host default architectures... if (triple_stref.equals (LLDB_ARCH_DEFAULT_32BIT)) *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture32); else if (triple_stref.equals (LLDB_ARCH_DEFAULT_64BIT)) *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture64); else if (triple_stref.equals (LLDB_ARCH_DEFAULT)) *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture); } else { std::string normalized_triple_sstr (llvm::Triple::normalize(triple_stref)); triple_stref = normalized_triple_sstr; SetTriple (llvm::Triple (triple_stref)); } } else Clear(); return IsValid(); } bool ArchSpec::SetTriple (const char *triple_cstr, Platform *platform) { if (triple_cstr && triple_cstr[0]) { if (ParseMachCPUDashSubtypeTriple (triple_cstr, *this)) return true; llvm::StringRef triple_stref (triple_cstr); if (triple_stref.startswith (LLDB_ARCH_DEFAULT)) { // Special case for the current host default architectures... if (triple_stref.equals (LLDB_ARCH_DEFAULT_32BIT)) *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture32); else if (triple_stref.equals (LLDB_ARCH_DEFAULT_64BIT)) *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture64); else if (triple_stref.equals (LLDB_ARCH_DEFAULT)) *this = Host::GetArchitecture (Host::eSystemDefaultArchitecture); } else { ArchSpec raw_arch (triple_cstr); std::string normalized_triple_sstr (llvm::Triple::normalize(triple_stref)); triple_stref = normalized_triple_sstr; llvm::Triple normalized_triple (triple_stref); const bool os_specified = normalized_triple.getOSName().size() > 0; const bool vendor_specified = normalized_triple.getVendorName().size() > 0; const bool env_specified = normalized_triple.getEnvironmentName().size() > 0; // If we got an arch only, then default the vendor, os, environment // to match the platform if one is supplied if (!(os_specified || vendor_specified || env_specified)) { if (platform) { // If we were given a platform, use the platform's system // architecture. If this is not available (might not be // connected) use the first supported architecture. ArchSpec compatible_arch; if (platform->IsCompatibleArchitecture (raw_arch, false, &compatible_arch)) { if (compatible_arch.IsValid()) { const llvm::Triple &compatible_triple = compatible_arch.GetTriple(); if (!vendor_specified) normalized_triple.setVendor(compatible_triple.getVendor()); if (!os_specified) normalized_triple.setOS(compatible_triple.getOS()); if (!env_specified && compatible_triple.getEnvironmentName().size()) normalized_triple.setEnvironment(compatible_triple.getEnvironment()); } } else { *this = raw_arch; return IsValid(); } } else { // No platform specified, fall back to the host system for // the default vendor, os, and environment. llvm::Triple host_triple(llvm::sys::getDefaultTargetTriple()); if (!vendor_specified) normalized_triple.setVendor(host_triple.getVendor()); if (!vendor_specified) normalized_triple.setOS(host_triple.getOS()); if (!env_specified && host_triple.getEnvironmentName().size()) normalized_triple.setEnvironment(host_triple.getEnvironment()); } } SetTriple (normalized_triple); } } else Clear(); return IsValid(); } bool ArchSpec::SetArchitecture (ArchitectureType arch_type, uint32_t cpu, uint32_t sub) { m_core = kCore_invalid; bool update_triple = true; const ArchDefinition *arch_def = FindArchDefinition(arch_type); if (arch_def) { const ArchDefinitionEntry *arch_def_entry = FindArchDefinitionEntry (arch_def, cpu, sub); if (arch_def_entry) { const CoreDefinition *core_def = FindCoreDefinition (arch_def_entry->core); if (core_def) { m_core = core_def->core; update_triple = false; // Always use the architecture name because it might be more descriptive // than the architecture enum ("armv7" -> llvm::Triple::arm). m_triple.setArchName(llvm::StringRef(core_def->name)); if (arch_type == eArchTypeMachO) { m_triple.setVendor (llvm::Triple::Apple); switch (core_def->machine) { case llvm::Triple::arm: case llvm::Triple::thumb: m_triple.setOS (llvm::Triple::IOS); break; case llvm::Triple::x86: case llvm::Triple::x86_64: default: m_triple.setOS (llvm::Triple::MacOSX); break; } } else { m_triple.setVendor (llvm::Triple::UnknownVendor); m_triple.setOS (llvm::Triple::UnknownOS); } // Fall back onto setting the machine type if the arch by name failed... if (m_triple.getArch () == llvm::Triple::UnknownArch) m_triple.setArch (core_def->machine); } } } CoreUpdated(update_triple); return IsValid(); } uint32_t ArchSpec::GetMinimumOpcodeByteSize() const { const CoreDefinition *core_def = FindCoreDefinition (m_core); if (core_def) return core_def->min_opcode_byte_size; return 0; } uint32_t ArchSpec::GetMaximumOpcodeByteSize() const { const CoreDefinition *core_def = FindCoreDefinition (m_core); if (core_def) return core_def->max_opcode_byte_size; return 0; } bool ArchSpec::IsExactMatch (const ArchSpec& rhs) const { return IsEqualTo (rhs, true); } bool ArchSpec::IsCompatibleMatch (const ArchSpec& rhs) const { return IsEqualTo (rhs, false); } bool ArchSpec::IsEqualTo (const ArchSpec& rhs, bool exact_match) const { if (GetByteOrder() != rhs.GetByteOrder()) return false; const ArchSpec::Core lhs_core = GetCore (); const ArchSpec::Core rhs_core = rhs.GetCore (); const bool core_match = cores_match (lhs_core, rhs_core, true, exact_match); if (core_match) { const llvm::Triple &lhs_triple = GetTriple(); const llvm::Triple &rhs_triple = rhs.GetTriple(); const llvm::Triple::VendorType lhs_triple_vendor = lhs_triple.getVendor(); const llvm::Triple::VendorType rhs_triple_vendor = rhs_triple.getVendor(); if (lhs_triple_vendor != rhs_triple_vendor) { if (exact_match) { const bool rhs_vendor_specified = rhs.TripleVendorWasSpecified(); const bool lhs_vendor_specified = TripleVendorWasSpecified(); // Both architectures had the vendor specified, so if they aren't // equal then we return false if (rhs_vendor_specified && lhs_vendor_specified) return false; } // Only fail if both vendor types are not unknown if (lhs_triple_vendor != llvm::Triple::UnknownVendor && rhs_triple_vendor != llvm::Triple::UnknownVendor) return false; } const llvm::Triple::OSType lhs_triple_os = lhs_triple.getOS(); const llvm::Triple::OSType rhs_triple_os = rhs_triple.getOS(); if (lhs_triple_os != rhs_triple_os) { if (exact_match) { const bool rhs_os_specified = rhs.TripleOSWasSpecified(); const bool lhs_os_specified = TripleOSWasSpecified(); // Both architectures had the OS specified, so if they aren't // equal then we return false if (rhs_os_specified && lhs_os_specified) return false; } // Only fail if both os types are not unknown if (lhs_triple_os != llvm::Triple::UnknownOS && rhs_triple_os != llvm::Triple::UnknownOS) return false; } const llvm::Triple::EnvironmentType lhs_triple_env = lhs_triple.getEnvironment(); const llvm::Triple::EnvironmentType rhs_triple_env = rhs_triple.getEnvironment(); if (lhs_triple_env != rhs_triple_env) { // Only fail if both environment types are not unknown if (lhs_triple_env != llvm::Triple::UnknownEnvironment && rhs_triple_env != llvm::Triple::UnknownEnvironment) return false; } return true; } return false; } //===----------------------------------------------------------------------===// // Helper methods. void ArchSpec::CoreUpdated (bool update_triple) { const CoreDefinition *core_def = FindCoreDefinition (m_core); if (core_def) { if (update_triple) m_triple = llvm::Triple(core_def->name, "unknown", "unknown"); m_byte_order = core_def->default_byte_order; } else { if (update_triple) m_triple = llvm::Triple(); m_byte_order = eByteOrderInvalid; } } //===----------------------------------------------------------------------===// // Operators. static bool cores_match (const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_inverse, bool enforce_exact_match) { if (core1 == core2) return true; switch (core1) { case ArchSpec::kCore_any: return true; case ArchSpec::kCore_arm_any: if (core2 >= ArchSpec::kCore_arm_first && core2 <= ArchSpec::kCore_arm_last) return true; if (core2 >= ArchSpec::kCore_thumb_first && core2 <= ArchSpec::kCore_thumb_last) return true; if (core2 == ArchSpec::kCore_arm_any) return true; break; case ArchSpec::kCore_x86_32_any: if ((core2 >= ArchSpec::kCore_x86_32_first && core2 <= ArchSpec::kCore_x86_32_last) || (core2 == ArchSpec::kCore_x86_32_any)) return true; break; case ArchSpec::kCore_ppc_any: if ((core2 >= ArchSpec::kCore_ppc_first && core2 <= ArchSpec::kCore_ppc_last) || (core2 == ArchSpec::kCore_ppc_any)) return true; break; case ArchSpec::kCore_ppc64_any: if ((core2 >= ArchSpec::kCore_ppc64_first && core2 <= ArchSpec::kCore_ppc64_last) || (core2 == ArchSpec::kCore_ppc64_any)) return true; break; case ArchSpec::eCore_arm_armv6m: if (!enforce_exact_match) { try_inverse = false; if (core2 == ArchSpec::eCore_arm_armv7) return true; } break; case ArchSpec::eCore_arm_armv7m: case ArchSpec::eCore_arm_armv7em: case ArchSpec::eCore_arm_armv7f: case ArchSpec::eCore_arm_armv7k: case ArchSpec::eCore_arm_armv7s: if (!enforce_exact_match) { try_inverse = false; if (core2 == ArchSpec::eCore_arm_armv7) return true; } break; default: break; } if (try_inverse) return cores_match (core2, core1, false, enforce_exact_match); return false; } bool lldb_private::operator<(const ArchSpec& lhs, const ArchSpec& rhs) { const ArchSpec::Core lhs_core = lhs.GetCore (); const ArchSpec::Core rhs_core = rhs.GetCore (); return lhs_core < rhs_core; } Index: head/contrib/llvm/tools/lldb/source/Expression/ClangExpressionParser.cpp =================================================================== --- head/contrib/llvm/tools/lldb/source/Expression/ClangExpressionParser.cpp (revision 262120) +++ head/contrib/llvm/tools/lldb/source/Expression/ClangExpressionParser.cpp (revision 262121) @@ -1,593 +1,590 @@ //===-- ClangExpressionParser.cpp -------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/lldb-python.h" #include "lldb/Expression/ClangExpressionParser.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/Stream.h" #include "lldb/Core/StreamString.h" #include "lldb/Expression/ClangASTSource.h" #include "lldb/Expression/ClangExpression.h" #include "lldb/Expression/ClangExpressionDeclMap.h" #include "lldb/Expression/IRExecutionUnit.h" #include "lldb/Expression/IRDynamicChecks.h" #include "lldb/Expression/IRInterpreter.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ExternalASTSource.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/Version.h" #include "clang/CodeGen/CodeGenAction.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/Driver/CC1Options.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/CompilerInvocation.h" #include "clang/Frontend/FrontendActions.h" #include "clang/Frontend/FrontendDiagnostic.h" #include "clang/Frontend/FrontendPluginRegistry.h" #include "clang/Frontend/TextDiagnosticBuffer.h" #include "clang/Frontend/TextDiagnosticPrinter.h" #include "clang/Lex/Preprocessor.h" #include "clang/Parse/ParseAST.h" #include "clang/Rewrite/Frontend/FrontendActions.h" #include "clang/Sema/SemaConsumer.h" #include "clang/StaticAnalyzer/Frontend/FrontendActions.h" #include "llvm/ADT/StringRef.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/Support/Debug.h" -#include "llvm/Support/PathV1.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/TargetSelect.h" #if defined (USE_STANDARD_JIT) #include "llvm/ExecutionEngine/JIT.h" #else #include "llvm/ExecutionEngine/MCJIT.h" #endif #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/DynamicLibrary.h" #include "llvm/Support/Host.h" #include "llvm/Support/Signals.h" using namespace clang; using namespace llvm; using namespace lldb_private; //===----------------------------------------------------------------------===// // Utility Methods for Clang //===----------------------------------------------------------------------===// std::string GetBuiltinIncludePath(const char *Argv0) { - llvm::sys::Path P = - llvm::sys::Path::GetMainExecutable(Argv0, - (void*)(intptr_t) GetBuiltinIncludePath); - - if (!P.isEmpty()) { - P.eraseComponent(); // Remove /clang from foo/bin/clang - P.eraseComponent(); // Remove /bin from foo/bin - + SmallString<128> P(llvm::sys::fs::getMainExecutable( + Argv0, (void *)(intptr_t) GetBuiltinIncludePath)); + + if (!P.empty()) { + llvm::sys::path::remove_filename(P); // Remove /clang from foo/bin/clang + llvm::sys::path::remove_filename(P); // Remove /bin from foo/bin + // Get foo/lib/clang//include - P.appendComponent("lib"); - P.appendComponent("clang"); - P.appendComponent(CLANG_VERSION_STRING); - P.appendComponent("include"); + llvm::sys::path::append(P, "lib", "clang", CLANG_VERSION_STRING, + "include"); } return P.str(); } //===----------------------------------------------------------------------===// // Main driver for Clang //===----------------------------------------------------------------------===// static void LLVMErrorHandler(void *UserData, const std::string &Message) { DiagnosticsEngine &Diags = *static_cast(UserData); Diags.Report(diag::err_fe_error_backend) << Message; // We cannot recover from llvm errors. assert(0); } static FrontendAction *CreateFrontendBaseAction(CompilerInstance &CI) { using namespace clang::frontend; switch (CI.getFrontendOpts().ProgramAction) { default: llvm_unreachable("Invalid program action!"); case ASTDump: return new ASTDumpAction(); case ASTPrint: return new ASTPrintAction(); case ASTView: return new ASTViewAction(); case DumpRawTokens: return new DumpRawTokensAction(); case DumpTokens: return new DumpTokensAction(); case EmitAssembly: return new EmitAssemblyAction(); case EmitBC: return new EmitBCAction(); case EmitHTML: return new HTMLPrintAction(); case EmitLLVM: return new EmitLLVMAction(); case EmitLLVMOnly: return new EmitLLVMOnlyAction(); case EmitCodeGenOnly: return new EmitCodeGenOnlyAction(); case EmitObj: return new EmitObjAction(); case FixIt: return new FixItAction(); case GeneratePCH: return new GeneratePCHAction(); case GeneratePTH: return new GeneratePTHAction(); case InitOnly: return new InitOnlyAction(); case ParseSyntaxOnly: return new SyntaxOnlyAction(); case PluginAction: { for (FrontendPluginRegistry::iterator it = FrontendPluginRegistry::begin(), ie = FrontendPluginRegistry::end(); it != ie; ++it) { if (it->getName() == CI.getFrontendOpts().ActionName) { llvm::OwningPtr P(it->instantiate()); if (!P->ParseArgs(CI, CI.getFrontendOpts().PluginArgs)) return 0; return P.take(); } } CI.getDiagnostics().Report(diag::err_fe_invalid_plugin_name) << CI.getFrontendOpts().ActionName; return 0; } case PrintDeclContext: return new DeclContextPrintAction(); case PrintPreamble: return new PrintPreambleAction(); case PrintPreprocessedInput: return new PrintPreprocessedAction(); case RewriteMacros: return new RewriteMacrosAction(); case RewriteObjC: return new RewriteObjCAction(); case RewriteTest: return new RewriteTestAction(); //case RunAnalysis: return new AnalysisAction(); case RunPreprocessorOnly: return new PreprocessOnlyAction(); } } static FrontendAction *CreateFrontendAction(CompilerInstance &CI) { // Create the underlying action. FrontendAction *Act = CreateFrontendBaseAction(CI); if (!Act) return 0; // If there are any AST files to merge, create a frontend action // adaptor to perform the merge. if (!CI.getFrontendOpts().ASTMergeFiles.empty()) Act = new ASTMergeAction(Act, CI.getFrontendOpts().ASTMergeFiles); return Act; } //===----------------------------------------------------------------------===// // Implementation of ClangExpressionParser //===----------------------------------------------------------------------===// ClangExpressionParser::ClangExpressionParser (ExecutionContextScope *exe_scope, ClangExpression &expr) : m_expr (expr), m_compiler (), m_code_generator () { // Initialize targets first, so that --version shows registered targets. static struct InitializeLLVM { InitializeLLVM() { llvm::InitializeAllTargets(); llvm::InitializeAllAsmPrinters(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllDisassemblers(); } } InitializeLLVM; // 1. Create a new compiler instance. m_compiler.reset(new CompilerInstance()); // 2. Install the target. lldb::TargetSP target_sp; if (exe_scope) target_sp = exe_scope->CalculateTarget(); // TODO: figure out what to really do when we don't have a valid target. // Sometimes this will be ok to just use the host target triple (when we // evaluate say "2+3", but other expressions like breakpoint conditions // and other things that _are_ target specific really shouldn't just be // using the host triple. This needs to be fixed in a better way. if (target_sp && target_sp->GetArchitecture().IsValid()) { std::string triple = target_sp->GetArchitecture().GetTriple().str(); int dash_count = 0; for (size_t i = 0; i < triple.size(); ++i) { if (triple[i] == '-') dash_count++; if (dash_count == 3) { triple.resize(i); break; } } m_compiler->getTargetOpts().Triple = triple; } else { m_compiler->getTargetOpts().Triple = llvm::sys::getDefaultTargetTriple(); } if (target_sp->GetArchitecture().GetMachine() == llvm::Triple::x86 || target_sp->GetArchitecture().GetMachine() == llvm::Triple::x86_64) { m_compiler->getTargetOpts().Features.push_back("+sse"); m_compiler->getTargetOpts().Features.push_back("+sse2"); } if (m_compiler->getTargetOpts().Triple.find("ios") != std::string::npos) m_compiler->getTargetOpts().ABI = "apcs-gnu"; m_compiler->createDiagnostics(); // Create the target instance. m_compiler->setTarget(TargetInfo::CreateTargetInfo(m_compiler->getDiagnostics(), &m_compiler->getTargetOpts())); assert (m_compiler->hasTarget()); // 3. Set options. lldb::LanguageType language = expr.Language(); switch (language) { case lldb::eLanguageTypeC: break; case lldb::eLanguageTypeObjC: m_compiler->getLangOpts().ObjC1 = true; m_compiler->getLangOpts().ObjC2 = true; break; case lldb::eLanguageTypeC_plus_plus: m_compiler->getLangOpts().CPlusPlus = true; m_compiler->getLangOpts().CPlusPlus11 = true; break; case lldb::eLanguageTypeObjC_plus_plus: default: m_compiler->getLangOpts().ObjC1 = true; m_compiler->getLangOpts().ObjC2 = true; m_compiler->getLangOpts().CPlusPlus = true; m_compiler->getLangOpts().CPlusPlus11 = true; break; } m_compiler->getLangOpts().Bool = true; m_compiler->getLangOpts().WChar = true; m_compiler->getLangOpts().Blocks = true; m_compiler->getLangOpts().DebuggerSupport = true; // Features specifically for debugger clients if (expr.DesiredResultType() == ClangExpression::eResultTypeId) m_compiler->getLangOpts().DebuggerCastResultToId = true; // Spell checking is a nice feature, but it ends up completing a // lot of types that we didn't strictly speaking need to complete. // As a result, we spend a long time parsing and importing debug // information. m_compiler->getLangOpts().SpellChecking = false; lldb::ProcessSP process_sp; if (exe_scope) process_sp = exe_scope->CalculateProcess(); if (process_sp && m_compiler->getLangOpts().ObjC1) { if (process_sp->GetObjCLanguageRuntime()) { if (process_sp->GetObjCLanguageRuntime()->GetRuntimeVersion() == eAppleObjC_V2) m_compiler->getLangOpts().ObjCRuntime.set(ObjCRuntime::MacOSX, VersionTuple(10, 7)); else m_compiler->getLangOpts().ObjCRuntime.set(ObjCRuntime::FragileMacOSX, VersionTuple(10, 7)); if (process_sp->GetObjCLanguageRuntime()->HasNewLiteralsAndIndexing()) m_compiler->getLangOpts().DebuggerObjCLiteral = true; } } m_compiler->getLangOpts().ThreadsafeStatics = false; m_compiler->getLangOpts().AccessControl = false; // Debuggers get universal access m_compiler->getLangOpts().DollarIdents = true; // $ indicates a persistent variable name // Set CodeGen options m_compiler->getCodeGenOpts().EmitDeclMetadata = true; m_compiler->getCodeGenOpts().InstrumentFunctions = false; m_compiler->getCodeGenOpts().DisableFPElim = true; m_compiler->getCodeGenOpts().OmitLeafFramePointer = false; // Disable some warnings. m_compiler->getDiagnostics().setDiagnosticGroupMapping("unused-value", clang::diag::MAP_IGNORE, SourceLocation()); m_compiler->getDiagnostics().setDiagnosticGroupMapping("odr", clang::diag::MAP_IGNORE, SourceLocation()); // Inform the target of the language options // // FIXME: We shouldn't need to do this, the target should be immutable once // created. This complexity should be lifted elsewhere. m_compiler->getTarget().setForcedLangOptions(m_compiler->getLangOpts()); // 4. Set up the diagnostic buffer for reporting errors m_compiler->getDiagnostics().setClient(new clang::TextDiagnosticBuffer); // 5. Set up the source management objects inside the compiler clang::FileSystemOptions file_system_options; m_file_manager.reset(new clang::FileManager(file_system_options)); if (!m_compiler->hasSourceManager()) m_compiler->createSourceManager(*m_file_manager.get()); m_compiler->createFileManager(); m_compiler->createPreprocessor(); // 6. Most of this we get from the CompilerInstance, but we // also want to give the context an ExternalASTSource. m_selector_table.reset(new SelectorTable()); m_builtin_context.reset(new Builtin::Context()); std::unique_ptr ast_context(new ASTContext(m_compiler->getLangOpts(), m_compiler->getSourceManager(), &m_compiler->getTarget(), m_compiler->getPreprocessor().getIdentifierTable(), *m_selector_table.get(), *m_builtin_context.get(), 0)); ClangExpressionDeclMap *decl_map = m_expr.DeclMap(); if (decl_map) { llvm::OwningPtr ast_source(decl_map->CreateProxy()); decl_map->InstallASTContext(ast_context.get()); ast_context->setExternalSource(ast_source); } m_compiler->setASTContext(ast_context.release()); std::string module_name("$__lldb_module"); m_llvm_context.reset(new LLVMContext()); m_code_generator.reset(CreateLLVMCodeGen(m_compiler->getDiagnostics(), module_name, m_compiler->getCodeGenOpts(), m_compiler->getTargetOpts(), *m_llvm_context)); } ClangExpressionParser::~ClangExpressionParser() { } unsigned ClangExpressionParser::Parse (Stream &stream) { TextDiagnosticBuffer *diag_buf = static_cast(m_compiler->getDiagnostics().getClient()); diag_buf->FlushDiagnostics (m_compiler->getDiagnostics()); MemoryBuffer *memory_buffer = MemoryBuffer::getMemBufferCopy(m_expr.Text(), __FUNCTION__); m_compiler->getSourceManager().createMainFileIDForMemBuffer (memory_buffer); diag_buf->BeginSourceFile(m_compiler->getLangOpts(), &m_compiler->getPreprocessor()); ASTConsumer *ast_transformer = m_expr.ASTTransformer(m_code_generator.get()); if (ast_transformer) ParseAST(m_compiler->getPreprocessor(), ast_transformer, m_compiler->getASTContext()); else ParseAST(m_compiler->getPreprocessor(), m_code_generator.get(), m_compiler->getASTContext()); diag_buf->EndSourceFile(); TextDiagnosticBuffer::const_iterator diag_iterator; int num_errors = 0; for (diag_iterator = diag_buf->warn_begin(); diag_iterator != diag_buf->warn_end(); ++diag_iterator) stream.Printf("warning: %s\n", (*diag_iterator).second.c_str()); num_errors = 0; for (diag_iterator = diag_buf->err_begin(); diag_iterator != diag_buf->err_end(); ++diag_iterator) { num_errors++; stream.Printf("error: %s\n", (*diag_iterator).second.c_str()); } for (diag_iterator = diag_buf->note_begin(); diag_iterator != diag_buf->note_end(); ++diag_iterator) stream.Printf("note: %s\n", (*diag_iterator).second.c_str()); if (!num_errors) { if (m_expr.DeclMap() && !m_expr.DeclMap()->ResolveUnknownTypes()) { stream.Printf("error: Couldn't infer the type of a variable\n"); num_errors++; } } return num_errors; } static bool FindFunctionInModule (ConstString &mangled_name, llvm::Module *module, const char *orig_name) { for (llvm::Module::iterator fi = module->getFunctionList().begin(), fe = module->getFunctionList().end(); fi != fe; ++fi) { if (fi->getName().str().find(orig_name) != std::string::npos) { mangled_name.SetCString(fi->getName().str().c_str()); return true; } } return false; } Error ClangExpressionParser::PrepareForExecution (lldb::addr_t &func_addr, lldb::addr_t &func_end, std::unique_ptr &execution_unit_ap, ExecutionContext &exe_ctx, bool &can_interpret, ExecutionPolicy execution_policy) { func_addr = LLDB_INVALID_ADDRESS; func_end = LLDB_INVALID_ADDRESS; Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); std::unique_ptr execution_engine_ap; Error err; std::unique_ptr module_ap (m_code_generator->ReleaseModule()); if (!module_ap.get()) { err.SetErrorToGenericError(); err.SetErrorString("IR doesn't contain a module"); return err; } // Find the actual name of the function (it's often mangled somehow) ConstString function_name; if (!FindFunctionInModule(function_name, module_ap.get(), m_expr.FunctionName())) { err.SetErrorToGenericError(); err.SetErrorStringWithFormat("Couldn't find %s() in the module", m_expr.FunctionName()); return err; } else { if (log) log->Printf("Found function %s for %s", function_name.AsCString(), m_expr.FunctionName()); } m_execution_unit.reset(new IRExecutionUnit(m_llvm_context, // handed off here module_ap, // handed off here function_name, exe_ctx.GetTargetSP(), m_compiler->getTargetOpts().Features)); ClangExpressionDeclMap *decl_map = m_expr.DeclMap(); // result can be NULL if (decl_map) { Stream *error_stream = NULL; Target *target = exe_ctx.GetTargetPtr(); if (target) error_stream = &target->GetDebugger().GetErrorStream(); IRForTarget ir_for_target(decl_map, m_expr.NeedsVariableResolution(), *m_execution_unit, error_stream, function_name.AsCString()); bool ir_can_run = ir_for_target.runOnModule(*m_execution_unit->GetModule()); Error interpret_error; can_interpret = IRInterpreter::CanInterpret(*m_execution_unit->GetModule(), *m_execution_unit->GetFunction(), interpret_error); Process *process = exe_ctx.GetProcessPtr(); if (!ir_can_run) { err.SetErrorString("The expression could not be prepared to run in the target"); return err; } if (!can_interpret && execution_policy == eExecutionPolicyNever) { err.SetErrorStringWithFormat("Can't run the expression locally: %s", interpret_error.AsCString()); return err; } if (!process && execution_policy == eExecutionPolicyAlways) { err.SetErrorString("Expression needed to run in the target, but the target can't be run"); return err; } if (execution_policy == eExecutionPolicyAlways || !can_interpret) { if (m_expr.NeedsValidation() && process) { if (!process->GetDynamicCheckers()) { DynamicCheckerFunctions *dynamic_checkers = new DynamicCheckerFunctions(); StreamString install_errors; if (!dynamic_checkers->Install(install_errors, exe_ctx)) { if (install_errors.GetString().empty()) err.SetErrorString ("couldn't install checkers, unknown error"); else err.SetErrorString (install_errors.GetString().c_str()); return err; } process->SetDynamicCheckers(dynamic_checkers); if (log) log->Printf("== [ClangUserExpression::Evaluate] Finished installing dynamic checkers =="); } IRDynamicChecks ir_dynamic_checks(*process->GetDynamicCheckers(), function_name.AsCString()); if (!ir_dynamic_checks.runOnModule(*m_execution_unit->GetModule())) { err.SetErrorToGenericError(); err.SetErrorString("Couldn't add dynamic checks to the expression"); return err; } } m_execution_unit->GetRunnableInfo(err, func_addr, func_end); } } else { m_execution_unit->GetRunnableInfo(err, func_addr, func_end); } execution_unit_ap.reset (m_execution_unit.release()); return err; } Index: head/contrib/llvm/tools/lldb/source/Expression/IRExecutionUnit.cpp =================================================================== --- head/contrib/llvm/tools/lldb/source/Expression/IRExecutionUnit.cpp (revision 262120) +++ head/contrib/llvm/tools/lldb/source/Expression/IRExecutionUnit.cpp (revision 262121) @@ -1,726 +1,706 @@ //===-- IRExecutionUnit.cpp -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes // Other libraries and framework includes #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/Support/SourceMgr.h" // Project includes #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Disassembler.h" #include "lldb/Core/Log.h" #include "lldb/Expression/IRExecutionUnit.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Target.h" using namespace lldb_private; IRExecutionUnit::IRExecutionUnit (std::unique_ptr &context_ap, std::unique_ptr &module_ap, ConstString &name, const lldb::TargetSP &target_sp, std::vector &cpu_features) : IRMemoryMap(target_sp), m_context_ap(context_ap.release()), m_module_ap(module_ap.release()), m_module(m_module_ap.get()), m_cpu_features(cpu_features), m_name(name), m_did_jit(false), m_function_load_addr(LLDB_INVALID_ADDRESS), m_function_end_load_addr(LLDB_INVALID_ADDRESS) { } lldb::addr_t IRExecutionUnit::WriteNow (const uint8_t *bytes, size_t size, Error &error) { lldb::addr_t allocation_process_addr = Malloc (size, 8, lldb::ePermissionsWritable | lldb::ePermissionsReadable, eAllocationPolicyMirror, error); if (!error.Success()) return LLDB_INVALID_ADDRESS; WriteMemory(allocation_process_addr, bytes, size, error); if (!error.Success()) { Error err; Free (allocation_process_addr, err); return LLDB_INVALID_ADDRESS; } if (Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) { DataBufferHeap my_buffer(size, 0); Error err; ReadMemory(my_buffer.GetBytes(), allocation_process_addr, size, err); if (err.Success()) { DataExtractor my_extractor(my_buffer.GetBytes(), my_buffer.GetByteSize(), lldb::eByteOrderBig, 8); StreamString ss; my_extractor.Dump(&ss, 0, lldb::eFormatBytesWithASCII, 1, my_buffer.GetByteSize(), 32, allocation_process_addr, 0, 0); log->PutCString(ss.GetData()); } } return allocation_process_addr; } void IRExecutionUnit::FreeNow (lldb::addr_t allocation) { if (allocation == LLDB_INVALID_ADDRESS) return; Error err; Free(allocation, err); } Error IRExecutionUnit::DisassembleFunction (Stream &stream, lldb::ProcessSP &process_wp) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); ExecutionContext exe_ctx(process_wp); Error ret; ret.Clear(); lldb::addr_t func_local_addr = LLDB_INVALID_ADDRESS; lldb::addr_t func_remote_addr = LLDB_INVALID_ADDRESS; for (JittedFunction &function : m_jitted_functions) { if (strstr(function.m_name.c_str(), m_name.AsCString())) { func_local_addr = function.m_local_addr; func_remote_addr = function.m_remote_addr; } } if (func_local_addr == LLDB_INVALID_ADDRESS) { ret.SetErrorToGenericError(); ret.SetErrorStringWithFormat("Couldn't find function %s for disassembly", m_name.AsCString()); return ret; } if (log) log->Printf("Found function, has local address 0x%" PRIx64 " and remote address 0x%" PRIx64, (uint64_t)func_local_addr, (uint64_t)func_remote_addr); std::pair func_range; func_range = GetRemoteRangeForLocal(func_local_addr); if (func_range.first == 0 && func_range.second == 0) { ret.SetErrorToGenericError(); ret.SetErrorStringWithFormat("Couldn't find code range for function %s", m_name.AsCString()); return ret; } if (log) log->Printf("Function's code range is [0x%" PRIx64 "+0x%" PRIx64 "]", func_range.first, func_range.second); Target *target = exe_ctx.GetTargetPtr(); if (!target) { ret.SetErrorToGenericError(); ret.SetErrorString("Couldn't find the target"); return ret; } lldb::DataBufferSP buffer_sp(new DataBufferHeap(func_range.second, 0)); Process *process = exe_ctx.GetProcessPtr(); Error err; process->ReadMemory(func_remote_addr, buffer_sp->GetBytes(), buffer_sp->GetByteSize(), err); if (!err.Success()) { ret.SetErrorToGenericError(); ret.SetErrorStringWithFormat("Couldn't read from process: %s", err.AsCString("unknown error")); return ret; } ArchSpec arch(target->GetArchitecture()); const char *plugin_name = NULL; const char *flavor_string = NULL; lldb::DisassemblerSP disassembler_sp = Disassembler::FindPlugin(arch, flavor_string, plugin_name); if (!disassembler_sp) { ret.SetErrorToGenericError(); ret.SetErrorStringWithFormat("Unable to find disassembler plug-in for %s architecture.", arch.GetArchitectureName()); return ret; } if (!process) { ret.SetErrorToGenericError(); ret.SetErrorString("Couldn't find the process"); return ret; } DataExtractor extractor(buffer_sp, process->GetByteOrder(), target->GetArchitecture().GetAddressByteSize()); if (log) { log->Printf("Function data has contents:"); extractor.PutToLog (log, 0, extractor.GetByteSize(), func_remote_addr, 16, DataExtractor::TypeUInt8); } disassembler_sp->DecodeInstructions (Address (func_remote_addr), extractor, 0, UINT32_MAX, false, false); InstructionList &instruction_list = disassembler_sp->GetInstructionList(); const uint32_t max_opcode_byte_size = instruction_list.GetMaxOpcocdeByteSize(); for (size_t instruction_index = 0, num_instructions = instruction_list.GetSize(); instruction_index < num_instructions; ++instruction_index) { Instruction *instruction = instruction_list.GetInstructionAtIndex(instruction_index).get(); instruction->Dump (&stream, max_opcode_byte_size, true, true, &exe_ctx); stream.PutChar('\n'); } // FIXME: The DisassemblerLLVMC has a reference cycle and won't go away if it has any active instructions. // I'll fix that but for now, just clear the list and it will go away nicely. disassembler_sp->GetInstructionList().Clear(); return ret; } static void ReportInlineAsmError(const llvm::SMDiagnostic &diagnostic, void *Context, unsigned LocCookie) { Error *err = static_cast(Context); if (err && err->Success()) { err->SetErrorToGenericError(); err->SetErrorStringWithFormat("Inline assembly error: %s", diagnostic.getMessage().str().c_str()); } } void IRExecutionUnit::GetRunnableInfo(Error &error, lldb::addr_t &func_addr, lldb::addr_t &func_end) { lldb::ProcessSP process_sp(GetProcessWP().lock()); func_addr = LLDB_INVALID_ADDRESS; func_end = LLDB_INVALID_ADDRESS; if (!process_sp) { error.SetErrorToGenericError(); error.SetErrorString("Couldn't write the JIT compiled code into the process because the process is invalid"); return; } if (m_did_jit) { func_addr = m_function_load_addr; func_end = m_function_end_load_addr; return; }; m_did_jit = true; Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); std::string error_string; if (log) { std::string s; llvm::raw_string_ostream oss(s); m_module->print(oss, NULL); oss.flush(); log->Printf ("Module being sent to JIT: \n%s", s.c_str()); } llvm::Triple triple(m_module->getTargetTriple()); llvm::Function *function = m_module->getFunction (m_name.AsCString()); llvm::Reloc::Model relocModel; llvm::CodeModel::Model codeModel; if (triple.isOSBinFormatELF()) { relocModel = llvm::Reloc::Static; // This will be small for 32-bit and large for 64-bit. codeModel = llvm::CodeModel::JITDefault; } else { relocModel = llvm::Reloc::PIC_; codeModel = llvm::CodeModel::Small; } m_module_ap->getContext().setInlineAsmDiagnosticHandler(ReportInlineAsmError, &error); llvm::EngineBuilder builder(m_module_ap.get()); builder.setEngineKind(llvm::EngineKind::JIT) .setErrorStr(&error_string) .setRelocationModel(relocModel) .setJITMemoryManager(new MemoryManager(*this)) .setOptLevel(llvm::CodeGenOpt::Less) .setAllocateGVsWithCode(true) .setCodeModel(codeModel) .setUseMCJIT(true); llvm::StringRef mArch; llvm::StringRef mCPU; llvm::SmallVector mAttrs; for (std::string &feature : m_cpu_features) mAttrs.push_back(feature); llvm::TargetMachine *target_machine = builder.selectTarget(triple, mArch, mCPU, mAttrs); m_execution_engine_ap.reset(builder.create(target_machine)); if (!m_execution_engine_ap.get()) { error.SetErrorToGenericError(); error.SetErrorStringWithFormat("Couldn't JIT the function: %s", error_string.c_str()); return; } else { m_module_ap.release(); // ownership was transferred } m_execution_engine_ap->DisableLazyCompilation(); // We don't actually need the function pointer here, this just forces it to get resolved. void *fun_ptr = m_execution_engine_ap->getPointerToFunction(function); if (!error.Success()) { // We got an error through our callback! return; } if (!function) { error.SetErrorToGenericError(); error.SetErrorStringWithFormat("Couldn't find '%s' in the JITted module", m_name.AsCString()); return; } if (!fun_ptr) { error.SetErrorToGenericError(); error.SetErrorStringWithFormat("'%s' was in the JITted module but wasn't lowered", m_name.AsCString()); return; } m_jitted_functions.push_back (JittedFunction(m_name.AsCString(), (lldb::addr_t)fun_ptr)); CommitAllocations(process_sp); ReportAllocations(*m_execution_engine_ap); WriteData(process_sp); for (JittedFunction &jitted_function : m_jitted_functions) { jitted_function.m_remote_addr = GetRemoteAddressForLocal (jitted_function.m_local_addr); if (!jitted_function.m_name.compare(m_name.AsCString())) { AddrRange func_range = GetRemoteRangeForLocal(jitted_function.m_local_addr); m_function_end_load_addr = func_range.first + func_range.second; m_function_load_addr = jitted_function.m_remote_addr; } } if (log) { log->Printf("Code can be run in the target."); StreamString disassembly_stream; Error err = DisassembleFunction(disassembly_stream, process_sp); if (!err.Success()) { log->Printf("Couldn't disassemble function : %s", err.AsCString("unknown error")); } else { log->Printf("Function disassembly:\n%s", disassembly_stream.GetData()); } } func_addr = m_function_load_addr; func_end = m_function_end_load_addr; return; } IRExecutionUnit::~IRExecutionUnit () { m_module_ap.reset(); m_execution_engine_ap.reset(); m_context_ap.reset(); } IRExecutionUnit::MemoryManager::MemoryManager (IRExecutionUnit &parent) : m_default_mm_ap (llvm::JITMemoryManager::CreateDefaultMemManager()), m_parent (parent) { } void IRExecutionUnit::MemoryManager::setMemoryWritable () { m_default_mm_ap->setMemoryWritable(); } void IRExecutionUnit::MemoryManager::setMemoryExecutable () { m_default_mm_ap->setMemoryExecutable(); } uint8_t * IRExecutionUnit::MemoryManager::startFunctionBody(const llvm::Function *F, uintptr_t &ActualSize) { return m_default_mm_ap->startFunctionBody(F, ActualSize); } uint8_t * IRExecutionUnit::MemoryManager::allocateStub(const llvm::GlobalValue* F, unsigned StubSize, unsigned Alignment) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); uint8_t *return_value = m_default_mm_ap->allocateStub(F, StubSize, Alignment); m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, lldb::ePermissionsReadable | lldb::ePermissionsWritable, StubSize, Alignment)); if (log) { log->Printf("IRExecutionUnit::allocateStub (F=%p, StubSize=%u, Alignment=%u) = %p", F, StubSize, Alignment, return_value); } return return_value; } void IRExecutionUnit::MemoryManager::endFunctionBody(const llvm::Function *F, uint8_t *FunctionStart, uint8_t *FunctionEnd) { m_default_mm_ap->endFunctionBody(F, FunctionStart, FunctionEnd); } uint8_t * IRExecutionUnit::MemoryManager::allocateSpace(intptr_t Size, unsigned Alignment) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); uint8_t *return_value = m_default_mm_ap->allocateSpace(Size, Alignment); m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, lldb::ePermissionsReadable | lldb::ePermissionsWritable, Size, Alignment)); if (log) { log->Printf("IRExecutionUnit::allocateSpace(Size=%" PRIu64 ", Alignment=%u) = %p", (uint64_t)Size, Alignment, return_value); } return return_value; } uint8_t * IRExecutionUnit::MemoryManager::allocateCodeSection(uintptr_t Size, unsigned Alignment, - unsigned SectionID) + unsigned SectionID, + llvm::StringRef SectionName) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); - uint8_t *return_value = m_default_mm_ap->allocateCodeSection(Size, Alignment, SectionID); + uint8_t *return_value = m_default_mm_ap->allocateCodeSection(Size, Alignment, SectionID, SectionName); m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, lldb::ePermissionsReadable | lldb::ePermissionsExecutable, Size, Alignment, SectionID)); if (log) { log->Printf("IRExecutionUnit::allocateCodeSection(Size=0x%" PRIx64 ", Alignment=%u, SectionID=%u) = %p", (uint64_t)Size, Alignment, SectionID, return_value); } return return_value; } uint8_t * IRExecutionUnit::MemoryManager::allocateDataSection(uintptr_t Size, unsigned Alignment, unsigned SectionID, + llvm::StringRef SectionName, bool IsReadOnly) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); - uint8_t *return_value = m_default_mm_ap->allocateDataSection(Size, Alignment, SectionID, IsReadOnly); + uint8_t *return_value = m_default_mm_ap->allocateDataSection(Size, Alignment, SectionID, SectionName, IsReadOnly); m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, lldb::ePermissionsReadable | lldb::ePermissionsWritable, Size, Alignment, SectionID)); if (log) { log->Printf("IRExecutionUnit::allocateDataSection(Size=0x%" PRIx64 ", Alignment=%u, SectionID=%u) = %p", (uint64_t)Size, Alignment, SectionID, return_value); } return return_value; } uint8_t * IRExecutionUnit::MemoryManager::allocateGlobal(uintptr_t Size, unsigned Alignment) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); uint8_t *return_value = m_default_mm_ap->allocateGlobal(Size, Alignment); m_parent.m_records.push_back(AllocationRecord((uintptr_t)return_value, lldb::ePermissionsReadable | lldb::ePermissionsWritable, Size, Alignment)); if (log) { log->Printf("IRExecutionUnit::allocateGlobal(Size=0x%" PRIx64 ", Alignment=%u) = %p", (uint64_t)Size, Alignment, return_value); } return return_value; } void IRExecutionUnit::MemoryManager::deallocateFunctionBody(void *Body) { m_default_mm_ap->deallocateFunctionBody(Body); -} - -uint8_t* -IRExecutionUnit::MemoryManager::startExceptionTable(const llvm::Function* F, - uintptr_t &ActualSize) -{ - return m_default_mm_ap->startExceptionTable(F, ActualSize); -} - -void -IRExecutionUnit::MemoryManager::endExceptionTable(const llvm::Function *F, - uint8_t *TableStart, - uint8_t *TableEnd, - uint8_t* FrameRegister) -{ - m_default_mm_ap->endExceptionTable(F, TableStart, TableEnd, FrameRegister); -} - -void -IRExecutionUnit::MemoryManager::deallocateExceptionTable(void *ET) -{ - m_default_mm_ap->deallocateExceptionTable (ET); } lldb::addr_t IRExecutionUnit::GetRemoteAddressForLocal (lldb::addr_t local_address) { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); for (AllocationRecord &record : m_records) { if (local_address >= record.m_host_address && local_address < record.m_host_address + record.m_size) { if (record.m_process_address == LLDB_INVALID_ADDRESS) return LLDB_INVALID_ADDRESS; lldb::addr_t ret = record.m_process_address + (local_address - record.m_host_address); if (log) { log->Printf("IRExecutionUnit::GetRemoteAddressForLocal() found 0x%" PRIx64 " in [0x%" PRIx64 "..0x%" PRIx64 "], and returned 0x%" PRIx64 " from [0x%" PRIx64 "..0x%" PRIx64 "].", local_address, (uint64_t)record.m_host_address, (uint64_t)record.m_host_address + (uint64_t)record.m_size, ret, record.m_process_address, record.m_process_address + record.m_size); } return ret; } } return LLDB_INVALID_ADDRESS; } IRExecutionUnit::AddrRange IRExecutionUnit::GetRemoteRangeForLocal (lldb::addr_t local_address) { for (AllocationRecord &record : m_records) { if (local_address >= record.m_host_address && local_address < record.m_host_address + record.m_size) { if (record.m_process_address == LLDB_INVALID_ADDRESS) return AddrRange(0, 0); return AddrRange(record.m_process_address, record.m_size); } } return AddrRange (0, 0); } bool IRExecutionUnit::CommitAllocations (lldb::ProcessSP &process_sp) { bool ret = true; lldb_private::Error err; for (AllocationRecord &record : m_records) { if (record.m_process_address != LLDB_INVALID_ADDRESS) continue; record.m_process_address = Malloc(record.m_size, record.m_alignment, record.m_permissions, eAllocationPolicyProcessOnly, err); if (!err.Success()) { ret = false; break; } } if (!ret) { for (AllocationRecord &record : m_records) { if (record.m_process_address != LLDB_INVALID_ADDRESS) { Free(record.m_process_address, err); record.m_process_address = LLDB_INVALID_ADDRESS; } } } return ret; } void IRExecutionUnit::ReportAllocations (llvm::ExecutionEngine &engine) { for (AllocationRecord &record : m_records) { if (record.m_process_address == LLDB_INVALID_ADDRESS) continue; if (record.m_section_id == eSectionIDInvalid) continue; engine.mapSectionAddress((void*)record.m_host_address, record.m_process_address); } // Trigger re-application of relocations. engine.finalizeObject(); } bool IRExecutionUnit::WriteData (lldb::ProcessSP &process_sp) { for (AllocationRecord &record : m_records) { if (record.m_process_address == LLDB_INVALID_ADDRESS) return false; lldb_private::Error err; WriteMemory (record.m_process_address, (uint8_t*)record.m_host_address, record.m_size, err); } return true; } void IRExecutionUnit::AllocationRecord::dump (Log *log) { if (!log) return; log->Printf("[0x%llx+0x%llx]->0x%llx (alignment %d, section ID %d)", (unsigned long long)m_host_address, (unsigned long long)m_size, (unsigned long long)m_process_address, (unsigned)m_alignment, (unsigned)m_section_id); } Index: head/contrib/llvm/tools/lldb/source/Expression/IRForTarget.cpp =================================================================== --- head/contrib/llvm/tools/lldb/source/Expression/IRForTarget.cpp (revision 262120) +++ head/contrib/llvm/tools/lldb/source/Expression/IRForTarget.cpp (revision 262121) @@ -1,2865 +1,2879 @@ //===-- IRForTarget.cpp -----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Expression/IRForTarget.h" #include "llvm/Support/raw_ostream.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Intrinsics.h" #include "llvm/IR/Module.h" #include "llvm/PassManager.h" #include "llvm/Transforms/IPO.h" #include "llvm/IR/ValueSymbolTable.h" #include "clang/AST/ASTContext.h" #include "lldb/Core/dwarf.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Log.h" #include "lldb/Core/Scalar.h" #include "lldb/Core/StreamString.h" #include "lldb/Expression/ClangExpressionDeclMap.h" #include "lldb/Expression/IRExecutionUnit.h" #include "lldb/Expression/IRInterpreter.h" #include "lldb/Host/Endian.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ClangASTType.h" #include using namespace llvm; static char ID; IRForTarget::StaticDataAllocator::StaticDataAllocator(lldb_private::IRExecutionUnit &execution_unit) : m_execution_unit(execution_unit), m_stream_string(lldb_private::Stream::eBinary, execution_unit.GetAddressByteSize(), execution_unit.GetByteOrder()), m_allocation(LLDB_INVALID_ADDRESS) { } IRForTarget::FunctionValueCache::FunctionValueCache(Maker const &maker) : m_maker(maker), m_values() { } IRForTarget::FunctionValueCache::~FunctionValueCache() { } llvm::Value *IRForTarget::FunctionValueCache::GetValue(llvm::Function *function) { if (!m_values.count(function)) { llvm::Value *ret = m_maker(function); m_values[function] = ret; return ret; } return m_values[function]; } lldb::addr_t IRForTarget::StaticDataAllocator::Allocate() { lldb_private::Error err; if (m_allocation != LLDB_INVALID_ADDRESS) { m_execution_unit.FreeNow(m_allocation); m_allocation = LLDB_INVALID_ADDRESS; } m_allocation = m_execution_unit.WriteNow((const uint8_t*)m_stream_string.GetData(), m_stream_string.GetSize(), err); return m_allocation; } static llvm::Value *FindEntryInstruction (llvm::Function *function) { if (function->empty()) return NULL; return function->getEntryBlock().getFirstNonPHIOrDbg(); } IRForTarget::IRForTarget (lldb_private::ClangExpressionDeclMap *decl_map, bool resolve_vars, lldb_private::IRExecutionUnit &execution_unit, lldb_private::Stream *error_stream, const char *func_name) : ModulePass(ID), m_resolve_vars(resolve_vars), m_func_name(func_name), m_module(NULL), m_decl_map(decl_map), m_data_allocator(execution_unit), m_CFStringCreateWithBytes(NULL), m_sel_registerName(NULL), m_error_stream(error_stream), m_result_store(NULL), m_result_is_pointer(false), m_reloc_placeholder(NULL), m_entry_instruction_finder (FindEntryInstruction) { } /* Handy utility functions used at several places in the code */ static std::string PrintValue(const Value *value, bool truncate = false) { std::string s; if (value) { raw_string_ostream rso(s); value->print(rso); rso.flush(); if (truncate) s.resize(s.length() - 1); } return s; } static std::string PrintType(const llvm::Type *type, bool truncate = false) { std::string s; raw_string_ostream rso(s); type->print(rso); rso.flush(); if (truncate) s.resize(s.length() - 1); return s; } IRForTarget::~IRForTarget() { } bool IRForTarget::FixFunctionLinkage(llvm::Function &llvm_function) { llvm_function.setLinkage(GlobalValue::ExternalLinkage); std::string name = llvm_function.getName().str(); return true; } bool IRForTarget::GetFunctionAddress (llvm::Function *fun, uint64_t &fun_addr, lldb_private::ConstString &name, Constant **&value_ptr) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); fun_addr = LLDB_INVALID_ADDRESS; name.Clear(); value_ptr = NULL; if (fun->isIntrinsic()) { Intrinsic::ID intrinsic_id = (Intrinsic::ID)fun->getIntrinsicID(); switch (intrinsic_id) { default: if (log) log->Printf("Unresolved intrinsic \"%s\"", Intrinsic::getName(intrinsic_id).c_str()); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Call to unhandled compiler intrinsic '%s'\n", Intrinsic::getName(intrinsic_id).c_str()); return false; case Intrinsic::memcpy: { static lldb_private::ConstString g_memcpy_str ("memcpy"); name = g_memcpy_str; } break; case Intrinsic::memset: { static lldb_private::ConstString g_memset_str ("memset"); name = g_memset_str; } break; } if (log && name) log->Printf("Resolved intrinsic name \"%s\"", name.GetCString()); } else { name.SetCStringWithLength (fun->getName().data(), fun->getName().size()); } // Find the address of the function. clang::NamedDecl *fun_decl = DeclForGlobal (fun); if (fun_decl) { if (!m_decl_map->GetFunctionInfo (fun_decl, fun_addr)) { lldb_private::ConstString altnernate_name; bool found_it = m_decl_map->GetFunctionAddress (name, fun_addr); if (!found_it) { // Check for an alternate mangling for "std::basic_string" // that is part of the itanium C++ name mangling scheme const char *name_cstr = name.GetCString(); if (name_cstr && strncmp(name_cstr, "_ZNKSbIcE", strlen("_ZNKSbIcE")) == 0) { std::string alternate_mangling("_ZNKSs"); alternate_mangling.append (name_cstr + strlen("_ZNKSbIcE")); altnernate_name.SetCString(alternate_mangling.c_str()); found_it = m_decl_map->GetFunctionAddress (altnernate_name, fun_addr); } } if (!found_it) { lldb_private::Mangled mangled_name(name); lldb_private::Mangled alt_mangled_name(altnernate_name); if (log) { if (alt_mangled_name) log->Printf("Function \"%s\" (alternate name \"%s\") has no address", mangled_name.GetName().GetCString(), alt_mangled_name.GetName().GetCString()); else log->Printf("Function \"%s\" had no address", mangled_name.GetName().GetCString()); } if (m_error_stream) { if (alt_mangled_name) m_error_stream->Printf("error: call to a function '%s' (alternate name '%s') that is not present in the target\n", mangled_name.GetName().GetCString(), alt_mangled_name.GetName().GetCString()); else if (mangled_name.GetMangledName()) m_error_stream->Printf("error: call to a function '%s' ('%s') that is not present in the target\n", mangled_name.GetName().GetCString(), mangled_name.GetMangledName().GetCString()); else m_error_stream->Printf("error: call to a function '%s' that is not present in the target\n", mangled_name.GetName().GetCString()); } return false; } } } else { if (!m_decl_map->GetFunctionAddress (name, fun_addr)) { if (log) log->Printf ("Metadataless function \"%s\" had no address", name.GetCString()); if (m_error_stream) m_error_stream->Printf("Error [IRForTarget]: Call to a symbol-only function '%s' that is not present in the target\n", name.GetCString()); return false; } } if (log) log->Printf("Found \"%s\" at 0x%" PRIx64, name.GetCString(), fun_addr); return true; } llvm::Constant * IRForTarget::BuildFunctionPointer (llvm::Type *type, uint64_t ptr) { IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); PointerType *fun_ptr_ty = PointerType::getUnqual(type); Constant *fun_addr_int = ConstantInt::get(intptr_ty, ptr, false); return ConstantExpr::getIntToPtr(fun_addr_int, fun_ptr_ty); } void IRForTarget::RegisterFunctionMetadata(LLVMContext &context, llvm::Value *function_ptr, const char *name) { for (Value::use_iterator i = function_ptr->use_begin(), e = function_ptr->use_end(); i != e; ++i) { Value *user = *i; if (Instruction *user_inst = dyn_cast(user)) { MDString* md_name = MDString::get(context, StringRef(name)); MDNode *metadata = MDNode::get(context, md_name); user_inst->setMetadata("lldb.call.realName", metadata); } else { RegisterFunctionMetadata (context, user, name); } } } bool IRForTarget::ResolveFunctionPointers(llvm::Module &llvm_module) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); for (llvm::Module::iterator fi = llvm_module.begin(); fi != llvm_module.end(); ++fi) { Function *fun = fi; bool is_decl = fun->isDeclaration(); if (log) log->Printf("Examining %s function %s", (is_decl ? "declaration" : "non-declaration"), fun->getName().str().c_str()); if (!is_decl) continue; if (fun->hasNUses(0)) continue; // ignore uint64_t addr = LLDB_INVALID_ADDRESS; lldb_private::ConstString name; Constant **value_ptr = NULL; if (!GetFunctionAddress(fun, addr, name, value_ptr)) return false; // GetFunctionAddress reports its own errors Constant *value = BuildFunctionPointer(fun->getFunctionType(), addr); RegisterFunctionMetadata (llvm_module.getContext(), fun, name.AsCString()); if (value_ptr) *value_ptr = value; + // If we are replacing a function with the nobuiltin attribute, it may + // be called with the builtin attribute on call sites. Remove any such + // attributes since it's illegal to have a builtin call to something + // other than a nobuiltin function. + if (fun->hasFnAttribute(llvm::Attribute::NoBuiltin)) { + llvm::Attribute builtin = llvm::Attribute::get(fun->getContext(), llvm::Attribute::Builtin); + + for (auto u = fun->use_begin(), e = fun->use_end(); u != e; ++u) { + if (auto call = dyn_cast(*u)) { + call->removeAttribute(AttributeSet::FunctionIndex, builtin); + } + } + } + fun->replaceAllUsesWith(value); } return true; } clang::NamedDecl * IRForTarget::DeclForGlobal (const GlobalValue *global_val, Module *module) { NamedMDNode *named_metadata = module->getNamedMetadata("clang.global.decl.ptrs"); if (!named_metadata) return NULL; unsigned num_nodes = named_metadata->getNumOperands(); unsigned node_index; for (node_index = 0; node_index < num_nodes; ++node_index) { MDNode *metadata_node = named_metadata->getOperand(node_index); if (!metadata_node) return NULL; if (metadata_node->getNumOperands() != 2) continue; if (metadata_node->getOperand(0) != global_val) continue; ConstantInt *constant_int = dyn_cast(metadata_node->getOperand(1)); if (!constant_int) return NULL; uintptr_t ptr = constant_int->getZExtValue(); return reinterpret_cast(ptr); } return NULL; } clang::NamedDecl * IRForTarget::DeclForGlobal (GlobalValue *global_val) { return DeclForGlobal(global_val, m_module); } bool IRForTarget::CreateResultVariable (llvm::Function &llvm_function) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); if (!m_resolve_vars) return true; // Find the result variable. If it doesn't exist, we can give up right here. ValueSymbolTable& value_symbol_table = m_module->getValueSymbolTable(); std::string result_name_str; const char *result_name = NULL; for (ValueSymbolTable::iterator vi = value_symbol_table.begin(), ve = value_symbol_table.end(); vi != ve; ++vi) { result_name_str = vi->first().str(); const char *value_name = result_name_str.c_str(); if (strstr(value_name, "$__lldb_expr_result_ptr") && strncmp(value_name, "_ZGV", 4)) { result_name = value_name; m_result_is_pointer = true; break; } if (strstr(value_name, "$__lldb_expr_result") && strncmp(value_name, "_ZGV", 4)) { result_name = value_name; m_result_is_pointer = false; break; } } if (!result_name) { if (log) log->PutCString("Couldn't find result variable"); return true; } if (log) log->Printf("Result name: \"%s\"", result_name); Value *result_value = m_module->getNamedValue(result_name); if (!result_value) { if (log) log->PutCString("Result variable had no data"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Result variable's name (%s) exists, but not its definition\n", result_name); return false; } if (log) log->Printf("Found result in the IR: \"%s\"", PrintValue(result_value, false).c_str()); GlobalVariable *result_global = dyn_cast(result_value); if (!result_global) { if (log) log->PutCString("Result variable isn't a GlobalVariable"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Result variable (%s) is defined, but is not a global variable\n", result_name); return false; } clang::NamedDecl *result_decl = DeclForGlobal (result_global); if (!result_decl) { if (log) log->PutCString("Result variable doesn't have a corresponding Decl"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Result variable (%s) does not have a corresponding Clang entity\n", result_name); return false; } if (log) { std::string decl_desc_str; raw_string_ostream decl_desc_stream(decl_desc_str); result_decl->print(decl_desc_stream); decl_desc_stream.flush(); log->Printf("Found result decl: \"%s\"", decl_desc_str.c_str()); } clang::VarDecl *result_var = dyn_cast(result_decl); if (!result_var) { if (log) log->PutCString("Result variable Decl isn't a VarDecl"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Result variable (%s)'s corresponding Clang entity isn't a variable\n", result_name); return false; } // Get the next available result name from m_decl_map and create the persistent // variable for it // If the result is an Lvalue, it is emitted as a pointer; see // ASTResultSynthesizer::SynthesizeBodyResult. if (m_result_is_pointer) { clang::QualType pointer_qual_type = result_var->getType(); const clang::Type *pointer_type = pointer_qual_type.getTypePtr(); const clang::PointerType *pointer_pointertype = pointer_type->getAs(); const clang::ObjCObjectPointerType *pointer_objcobjpointertype = pointer_type->getAs(); if (pointer_pointertype) { clang::QualType element_qual_type = pointer_pointertype->getPointeeType(); m_result_type = lldb_private::TypeFromParser(element_qual_type.getAsOpaquePtr(), &result_decl->getASTContext()); } else if (pointer_objcobjpointertype) { clang::QualType element_qual_type = clang::QualType(pointer_objcobjpointertype->getObjectType(), 0); m_result_type = lldb_private::TypeFromParser(element_qual_type.getAsOpaquePtr(), &result_decl->getASTContext()); } else { if (log) log->PutCString("Expected result to have pointer type, but it did not"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Lvalue result (%s) is not a pointer variable\n", result_name); return false; } } else { m_result_type = lldb_private::TypeFromParser(result_var->getType().getAsOpaquePtr(), &result_decl->getASTContext()); } if (m_result_type.GetBitSize() == 0) { lldb_private::StreamString type_desc_stream; m_result_type.DumpTypeDescription(&type_desc_stream); if (log) log->Printf("Result type has size 0"); if (m_error_stream) m_error_stream->Printf("Error [IRForTarget]: Size of result type '%s' couldn't be determined\n", type_desc_stream.GetData()); return false; } if (log) { lldb_private::StreamString type_desc_stream; m_result_type.DumpTypeDescription(&type_desc_stream); log->Printf("Result decl type: \"%s\"", type_desc_stream.GetData()); } m_result_name = lldb_private::ConstString("$RESULT_NAME"); if (log) log->Printf("Creating a new result global: \"%s\" with size 0x%" PRIx64, m_result_name.GetCString(), m_result_type.GetByteSize()); // Construct a new result global and set up its metadata GlobalVariable *new_result_global = new GlobalVariable((*m_module), result_global->getType()->getElementType(), false, /* not constant */ GlobalValue::ExternalLinkage, NULL, /* no initializer */ m_result_name.GetCString ()); // It's too late in compilation to create a new VarDecl for this, but we don't // need to. We point the metadata at the old VarDecl. This creates an odd // anomaly: a variable with a Value whose name is something like $0 and a // Decl whose name is $__lldb_expr_result. This condition is handled in // ClangExpressionDeclMap::DoMaterialize, and the name of the variable is // fixed up. ConstantInt *new_constant_int = ConstantInt::get(llvm::Type::getInt64Ty(m_module->getContext()), reinterpret_cast(result_decl), false); llvm::Value* values[2]; values[0] = new_result_global; values[1] = new_constant_int; ArrayRef value_ref(values, 2); MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref); NamedMDNode *named_metadata = m_module->getNamedMetadata("clang.global.decl.ptrs"); named_metadata->addOperand(persistent_global_md); if (log) log->Printf("Replacing \"%s\" with \"%s\"", PrintValue(result_global).c_str(), PrintValue(new_result_global).c_str()); if (result_global->hasNUses(0)) { // We need to synthesize a store for this variable, because otherwise // there's nothing to put into its equivalent persistent variable. BasicBlock &entry_block(llvm_function.getEntryBlock()); Instruction *first_entry_instruction(entry_block.getFirstNonPHIOrDbg()); if (!first_entry_instruction) return false; if (!result_global->hasInitializer()) { if (log) log->Printf("Couldn't find initializer for unused variable"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Result variable (%s) has no writes and no initializer\n", result_name); return false; } Constant *initializer = result_global->getInitializer(); StoreInst *synthesized_store = new StoreInst(initializer, new_result_global, first_entry_instruction); if (log) log->Printf("Synthesized result store \"%s\"\n", PrintValue(synthesized_store).c_str()); } else { result_global->replaceAllUsesWith(new_result_global); } if (!m_decl_map->AddPersistentVariable(result_decl, m_result_name, m_result_type, true, m_result_is_pointer)) return false; result_global->eraseFromParent(); return true; } #if 0 static void DebugUsers(Log *log, Value *value, uint8_t depth) { if (!depth) return; depth--; if (log) log->Printf(" ", value->getNumUses()); for (Value::use_iterator ui = value->use_begin(), ue = value->use_end(); ui != ue; ++ui) { if (log) log->Printf(" %s", *ui, PrintValue(*ui).c_str()); DebugUsers(log, *ui, depth); } if (log) log->Printf(" "); } #endif bool IRForTarget::RewriteObjCConstString (llvm::GlobalVariable *ns_str, llvm::GlobalVariable *cstr) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); Type *ns_str_ty = ns_str->getType(); Type *i8_ptr_ty = Type::getInt8PtrTy(m_module->getContext()); IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); Type *i32_ty = Type::getInt32Ty(m_module->getContext()); Type *i8_ty = Type::getInt8Ty(m_module->getContext()); if (!m_CFStringCreateWithBytes) { lldb::addr_t CFStringCreateWithBytes_addr; static lldb_private::ConstString g_CFStringCreateWithBytes_str ("CFStringCreateWithBytes"); if (!m_decl_map->GetFunctionAddress (g_CFStringCreateWithBytes_str, CFStringCreateWithBytes_addr)) { if (log) log->PutCString("Couldn't find CFStringCreateWithBytes in the target"); if (m_error_stream) m_error_stream->Printf("Error [IRForTarget]: Rewriting an Objective-C constant string requires CFStringCreateWithBytes\n"); return false; } if (log) log->Printf("Found CFStringCreateWithBytes at 0x%" PRIx64, CFStringCreateWithBytes_addr); // Build the function type: // // CFStringRef CFStringCreateWithBytes ( // CFAllocatorRef alloc, // const UInt8 *bytes, // CFIndex numBytes, // CFStringEncoding encoding, // Boolean isExternalRepresentation // ); // // We make the following substitutions: // // CFStringRef -> i8* // CFAllocatorRef -> i8* // UInt8 * -> i8* // CFIndex -> long (i32 or i64, as appropriate; we ask the module for its pointer size for now) // CFStringEncoding -> i32 // Boolean -> i8 Type *arg_type_array[5]; arg_type_array[0] = i8_ptr_ty; arg_type_array[1] = i8_ptr_ty; arg_type_array[2] = intptr_ty; arg_type_array[3] = i32_ty; arg_type_array[4] = i8_ty; ArrayRef CFSCWB_arg_types(arg_type_array, 5); llvm::Type *CFSCWB_ty = FunctionType::get(ns_str_ty, CFSCWB_arg_types, false); // Build the constant containing the pointer to the function PointerType *CFSCWB_ptr_ty = PointerType::getUnqual(CFSCWB_ty); Constant *CFSCWB_addr_int = ConstantInt::get(intptr_ty, CFStringCreateWithBytes_addr, false); m_CFStringCreateWithBytes = ConstantExpr::getIntToPtr(CFSCWB_addr_int, CFSCWB_ptr_ty); } ConstantDataSequential *string_array = NULL; if (cstr) string_array = dyn_cast(cstr->getInitializer()); Constant *alloc_arg = Constant::getNullValue(i8_ptr_ty); Constant *bytes_arg = cstr ? ConstantExpr::getBitCast(cstr, i8_ptr_ty) : Constant::getNullValue(i8_ptr_ty); Constant *numBytes_arg = ConstantInt::get(intptr_ty, cstr ? string_array->getNumElements() - 1 : 0, false); Constant *encoding_arg = ConstantInt::get(i32_ty, 0x0600, false); /* 0x0600 is kCFStringEncodingASCII */ Constant *isExternal_arg = ConstantInt::get(i8_ty, 0x0, false); /* 0x0 is false */ Value *argument_array[5]; argument_array[0] = alloc_arg; argument_array[1] = bytes_arg; argument_array[2] = numBytes_arg; argument_array[3] = encoding_arg; argument_array[4] = isExternal_arg; ArrayRef CFSCWB_arguments(argument_array, 5); FunctionValueCache CFSCWB_Caller ([this, &CFSCWB_arguments] (llvm::Function *function)->llvm::Value * { return CallInst::Create(m_CFStringCreateWithBytes, CFSCWB_arguments, "CFStringCreateWithBytes", llvm::cast(m_entry_instruction_finder.GetValue(function))); }); if (!UnfoldConstant(ns_str, CFSCWB_Caller, m_entry_instruction_finder)) { if (log) log->PutCString("Couldn't replace the NSString with the result of the call"); if (m_error_stream) m_error_stream->Printf("Error [IRForTarget]: Couldn't replace an Objective-C constant string with a dynamic string\n"); return false; } ns_str->eraseFromParent(); return true; } bool IRForTarget::RewriteObjCConstStrings() { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); ValueSymbolTable& value_symbol_table = m_module->getValueSymbolTable(); for (ValueSymbolTable::iterator vi = value_symbol_table.begin(), ve = value_symbol_table.end(); vi != ve; ++vi) { std::string value_name = vi->first().str(); const char *value_name_cstr = value_name.c_str(); if (strstr(value_name_cstr, "_unnamed_cfstring_")) { Value *nsstring_value = vi->second; GlobalVariable *nsstring_global = dyn_cast(nsstring_value); if (!nsstring_global) { if (log) log->PutCString("NSString variable is not a GlobalVariable"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string is not a global variable\n"); return false; } if (!nsstring_global->hasInitializer()) { if (log) log->PutCString("NSString variable does not have an initializer"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string does not have an initializer\n"); return false; } ConstantStruct *nsstring_struct = dyn_cast(nsstring_global->getInitializer()); if (!nsstring_struct) { if (log) log->PutCString("NSString variable's initializer is not a ConstantStruct"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string is not a structure constant\n"); return false; } // We expect the following structure: // // struct { // int *isa; // int flags; // char *str; // long length; // }; if (nsstring_struct->getNumOperands() != 4) { if (log) log->Printf("NSString variable's initializer structure has an unexpected number of members. Should be 4, is %d", nsstring_struct->getNumOperands()); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: The struct for an Objective-C constant string is not as expected\n"); return false; } Constant *nsstring_member = nsstring_struct->getOperand(2); if (!nsstring_member) { if (log) log->PutCString("NSString initializer's str element was empty"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string does not have a string initializer\n"); return false; } ConstantExpr *nsstring_expr = dyn_cast(nsstring_member); if (!nsstring_expr) { if (log) log->PutCString("NSString initializer's str element is not a ConstantExpr"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer is not constant\n"); return false; } if (nsstring_expr->getOpcode() != Instruction::GetElementPtr) { if (log) log->Printf("NSString initializer's str element is not a GetElementPtr expression, it's a %s", nsstring_expr->getOpcodeName()); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer is not an array\n"); return false; } Constant *nsstring_cstr = nsstring_expr->getOperand(0); GlobalVariable *cstr_global = dyn_cast(nsstring_cstr); if (!cstr_global) { if (log) log->PutCString("NSString initializer's str element is not a GlobalVariable"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer doesn't point to a global\n"); return false; } if (!cstr_global->hasInitializer()) { if (log) log->PutCString("NSString initializer's str element does not have an initializer"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer doesn't point to initialized data\n"); return false; } /* if (!cstr_array) { if (log) log->PutCString("NSString initializer's str element is not a ConstantArray"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer doesn't point to an array\n"); return false; } if (!cstr_array->isCString()) { if (log) log->PutCString("NSString initializer's str element is not a C string array"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: An Objective-C constant string's string initializer doesn't point to a C string\n"); return false; } */ ConstantDataArray *cstr_array = dyn_cast(cstr_global->getInitializer()); if (log) { if (cstr_array) log->Printf("Found NSString constant %s, which contains \"%s\"", value_name_cstr, cstr_array->getAsString().str().c_str()); else log->Printf("Found NSString constant %s, which contains \"\"", value_name_cstr); } if (!cstr_array) cstr_global = NULL; if (!RewriteObjCConstString(nsstring_global, cstr_global)) { if (log) log->PutCString("Error rewriting the constant string"); // We don't print an error message here because RewriteObjCConstString has done so for us. return false; } } } for (ValueSymbolTable::iterator vi = value_symbol_table.begin(), ve = value_symbol_table.end(); vi != ve; ++vi) { std::string value_name = vi->first().str(); const char *value_name_cstr = value_name.c_str(); if (!strcmp(value_name_cstr, "__CFConstantStringClassReference")) { GlobalVariable *gv = dyn_cast(vi->second); if (!gv) { if (log) log->PutCString("__CFConstantStringClassReference is not a global variable"); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Found a CFConstantStringClassReference, but it is not a global object\n"); return false; } gv->eraseFromParent(); break; } } return true; } static bool IsObjCSelectorRef (Value *value) { GlobalVariable *global_variable = dyn_cast(value); if (!global_variable || !global_variable->hasName() || !global_variable->getName().startswith("\01L_OBJC_SELECTOR_REFERENCES_")) return false; return true; } // This function does not report errors; its callers are responsible. bool IRForTarget::RewriteObjCSelector (Instruction* selector_load) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); LoadInst *load = dyn_cast(selector_load); if (!load) return false; // Unpack the message name from the selector. In LLVM IR, an objc_msgSend gets represented as // // %tmp = load i8** @"\01L_OBJC_SELECTOR_REFERENCES_" ; // %call = call i8* (i8*, i8*, ...)* @objc_msgSend(i8* %obj, i8* %tmp, ...) ; // // where %obj is the object pointer and %tmp is the selector. // // @"\01L_OBJC_SELECTOR_REFERENCES_" is a pointer to a character array called @"\01L_OBJC_llvm_moduleETH_VAR_NAllvm_moduleE_". // @"\01L_OBJC_llvm_moduleETH_VAR_NAllvm_moduleE_" contains the string. // Find the pointer's initializer (a ConstantExpr with opcode GetElementPtr) and get the string from its target GlobalVariable *_objc_selector_references_ = dyn_cast(load->getPointerOperand()); if (!_objc_selector_references_ || !_objc_selector_references_->hasInitializer()) return false; Constant *osr_initializer = _objc_selector_references_->getInitializer(); ConstantExpr *osr_initializer_expr = dyn_cast(osr_initializer); if (!osr_initializer_expr || osr_initializer_expr->getOpcode() != Instruction::GetElementPtr) return false; Value *osr_initializer_base = osr_initializer_expr->getOperand(0); if (!osr_initializer_base) return false; // Find the string's initializer (a ConstantArray) and get the string from it GlobalVariable *_objc_meth_var_name_ = dyn_cast(osr_initializer_base); if (!_objc_meth_var_name_ || !_objc_meth_var_name_->hasInitializer()) return false; Constant *omvn_initializer = _objc_meth_var_name_->getInitializer(); ConstantDataArray *omvn_initializer_array = dyn_cast(omvn_initializer); if (!omvn_initializer_array->isString()) return false; std::string omvn_initializer_string = omvn_initializer_array->getAsString(); if (log) log->Printf("Found Objective-C selector reference \"%s\"", omvn_initializer_string.c_str()); // Construct a call to sel_registerName if (!m_sel_registerName) { lldb::addr_t sel_registerName_addr; static lldb_private::ConstString g_sel_registerName_str ("sel_registerName"); if (!m_decl_map->GetFunctionAddress (g_sel_registerName_str, sel_registerName_addr)) return false; if (log) log->Printf("Found sel_registerName at 0x%" PRIx64, sel_registerName_addr); // Build the function type: struct objc_selector *sel_registerName(uint8_t*) // The below code would be "more correct," but in actuality what's required is uint8_t* //Type *sel_type = StructType::get(m_module->getContext()); //Type *sel_ptr_type = PointerType::getUnqual(sel_type); Type *sel_ptr_type = Type::getInt8PtrTy(m_module->getContext()); Type *type_array[1]; type_array[0] = llvm::Type::getInt8PtrTy(m_module->getContext()); ArrayRef srN_arg_types(type_array, 1); llvm::Type *srN_type = FunctionType::get(sel_ptr_type, srN_arg_types, false); // Build the constant containing the pointer to the function IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); PointerType *srN_ptr_ty = PointerType::getUnqual(srN_type); Constant *srN_addr_int = ConstantInt::get(intptr_ty, sel_registerName_addr, false); m_sel_registerName = ConstantExpr::getIntToPtr(srN_addr_int, srN_ptr_ty); } Value *argument_array[1]; Constant *omvn_pointer = ConstantExpr::getBitCast(_objc_meth_var_name_, Type::getInt8PtrTy(m_module->getContext())); argument_array[0] = omvn_pointer; ArrayRef srN_arguments(argument_array, 1); CallInst *srN_call = CallInst::Create(m_sel_registerName, srN_arguments, "sel_registerName", selector_load); // Replace the load with the call in all users selector_load->replaceAllUsesWith(srN_call); selector_load->eraseFromParent(); return true; } bool IRForTarget::RewriteObjCSelectors (BasicBlock &basic_block) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); BasicBlock::iterator ii; typedef SmallVector InstrList; typedef InstrList::iterator InstrIterator; InstrList selector_loads; for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) { Instruction &inst = *ii; if (LoadInst *load = dyn_cast(&inst)) if (IsObjCSelectorRef(load->getPointerOperand())) selector_loads.push_back(&inst); } InstrIterator iter; for (iter = selector_loads.begin(); iter != selector_loads.end(); ++iter) { if (!RewriteObjCSelector(*iter)) { if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Couldn't change a static reference to an Objective-C selector to a dynamic reference\n"); if (log) log->PutCString("Couldn't rewrite a reference to an Objective-C selector"); return false; } } return true; } // This function does not report errors; its callers are responsible. bool IRForTarget::RewritePersistentAlloc (llvm::Instruction *persistent_alloc) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); AllocaInst *alloc = dyn_cast(persistent_alloc); MDNode *alloc_md = alloc->getMetadata("clang.decl.ptr"); if (!alloc_md || !alloc_md->getNumOperands()) return false; ConstantInt *constant_int = dyn_cast(alloc_md->getOperand(0)); if (!constant_int) return false; // We attempt to register this as a new persistent variable with the DeclMap. uintptr_t ptr = constant_int->getZExtValue(); clang::VarDecl *decl = reinterpret_cast(ptr); lldb_private::TypeFromParser result_decl_type (decl->getType().getAsOpaquePtr(), &decl->getASTContext()); StringRef decl_name (decl->getName()); lldb_private::ConstString persistent_variable_name (decl_name.data(), decl_name.size()); if (!m_decl_map->AddPersistentVariable(decl, persistent_variable_name, result_decl_type, false, false)) return false; GlobalVariable *persistent_global = new GlobalVariable((*m_module), alloc->getType(), false, /* not constant */ GlobalValue::ExternalLinkage, NULL, /* no initializer */ alloc->getName().str().c_str()); // What we're going to do here is make believe this was a regular old external // variable. That means we need to make the metadata valid. NamedMDNode *named_metadata = m_module->getOrInsertNamedMetadata("clang.global.decl.ptrs"); llvm::Value* values[2]; values[0] = persistent_global; values[1] = constant_int; ArrayRef value_ref(values, 2); MDNode *persistent_global_md = MDNode::get(m_module->getContext(), value_ref); named_metadata->addOperand(persistent_global_md); // Now, since the variable is a pointer variable, we will drop in a load of that // pointer variable. LoadInst *persistent_load = new LoadInst (persistent_global, "", alloc); if (log) log->Printf("Replacing \"%s\" with \"%s\"", PrintValue(alloc).c_str(), PrintValue(persistent_load).c_str()); alloc->replaceAllUsesWith(persistent_load); alloc->eraseFromParent(); return true; } bool IRForTarget::RewritePersistentAllocs(llvm::BasicBlock &basic_block) { if (!m_resolve_vars) return true; lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); BasicBlock::iterator ii; typedef SmallVector InstrList; typedef InstrList::iterator InstrIterator; InstrList pvar_allocs; for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) { Instruction &inst = *ii; if (AllocaInst *alloc = dyn_cast(&inst)) { llvm::StringRef alloc_name = alloc->getName(); if (alloc_name.startswith("$") && !alloc_name.startswith("$__lldb")) { if (alloc_name.find_first_of("0123456789") == 1) { if (log) log->Printf("Rejecting a numeric persistent variable."); if (m_error_stream) m_error_stream->Printf("Error [IRForTarget]: Names starting with $0, $1, ... are reserved for use as result names\n"); return false; } pvar_allocs.push_back(alloc); } } } InstrIterator iter; for (iter = pvar_allocs.begin(); iter != pvar_allocs.end(); ++iter) { if (!RewritePersistentAlloc(*iter)) { if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Couldn't rewrite the creation of a persistent variable\n"); if (log) log->PutCString("Couldn't rewrite the creation of a persistent variable"); return false; } } return true; } bool IRForTarget::MaterializeInitializer (uint8_t *data, Constant *initializer) { if (!initializer) return true; lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); if (log && log->GetVerbose()) log->Printf(" MaterializeInitializer(%p, %s)", data, PrintValue(initializer).c_str()); Type *initializer_type = initializer->getType(); if (ConstantInt *int_initializer = dyn_cast(initializer)) { memcpy (data, int_initializer->getValue().getRawData(), m_target_data->getTypeStoreSize(initializer_type)); return true; } else if (ConstantDataArray *array_initializer = dyn_cast(initializer)) { if (array_initializer->isString()) { std::string array_initializer_string = array_initializer->getAsString(); memcpy (data, array_initializer_string.c_str(), m_target_data->getTypeStoreSize(initializer_type)); } else { ArrayType *array_initializer_type = array_initializer->getType(); Type *array_element_type = array_initializer_type->getElementType(); size_t element_size = m_target_data->getTypeAllocSize(array_element_type); for (unsigned i = 0; i < array_initializer->getNumOperands(); ++i) { Value *operand_value = array_initializer->getOperand(i); Constant *operand_constant = dyn_cast(operand_value); if (!operand_constant) return false; if (!MaterializeInitializer(data + (i * element_size), operand_constant)) return false; } } return true; } else if (ConstantStruct *struct_initializer = dyn_cast(initializer)) { StructType *struct_initializer_type = struct_initializer->getType(); const StructLayout *struct_layout = m_target_data->getStructLayout(struct_initializer_type); for (unsigned i = 0; i < struct_initializer->getNumOperands(); ++i) { if (!MaterializeInitializer(data + struct_layout->getElementOffset(i), struct_initializer->getOperand(i))) return false; } return true; } else if (isa(initializer)) { memset(data, 0, m_target_data->getTypeStoreSize(initializer_type)); return true; } return false; } bool IRForTarget::MaterializeInternalVariable (GlobalVariable *global_variable) { if (GlobalVariable::isExternalLinkage(global_variable->getLinkage())) return false; if (global_variable == m_reloc_placeholder) return true; uint64_t offset = m_data_allocator.GetStream().GetSize(); llvm::Type *variable_type = global_variable->getType(); Constant *initializer = global_variable->getInitializer(); llvm::Type *initializer_type = initializer->getType(); size_t size = m_target_data->getTypeAllocSize(initializer_type); size_t align = m_target_data->getPrefTypeAlignment(initializer_type); const size_t mask = (align - 1); uint64_t aligned_offset = (offset + mask) & ~mask; m_data_allocator.GetStream().PutNHex8(aligned_offset - offset, 0); offset = aligned_offset; lldb_private::DataBufferHeap data(size, '\0'); if (initializer) if (!MaterializeInitializer(data.GetBytes(), initializer)) return false; m_data_allocator.GetStream().Write(data.GetBytes(), data.GetByteSize()); Constant *new_pointer = BuildRelocation(variable_type, offset); global_variable->replaceAllUsesWith(new_pointer); global_variable->eraseFromParent(); return true; } // This function does not report errors; its callers are responsible. bool IRForTarget::MaybeHandleVariable (Value *llvm_value_ptr) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); if (log) log->Printf("MaybeHandleVariable (%s)", PrintValue(llvm_value_ptr).c_str()); if (ConstantExpr *constant_expr = dyn_cast(llvm_value_ptr)) { switch (constant_expr->getOpcode()) { default: break; case Instruction::GetElementPtr: case Instruction::BitCast: Value *s = constant_expr->getOperand(0); if (!MaybeHandleVariable(s)) return false; } } else if (GlobalVariable *global_variable = dyn_cast(llvm_value_ptr)) { if (!GlobalValue::isExternalLinkage(global_variable->getLinkage())) return MaterializeInternalVariable(global_variable); clang::NamedDecl *named_decl = DeclForGlobal(global_variable); if (!named_decl) { if (IsObjCSelectorRef(llvm_value_ptr)) return true; if (!global_variable->hasExternalLinkage()) return true; if (log) log->Printf("Found global variable \"%s\" without metadata", global_variable->getName().str().c_str()); return false; } std::string name (named_decl->getName().str()); clang::ValueDecl *value_decl = dyn_cast(named_decl); if (value_decl == NULL) return false; lldb_private::ClangASTType clang_type(&value_decl->getASTContext(), value_decl->getType()); const Type *value_type = NULL; if (name[0] == '$') { // The $__lldb_expr_result name indicates the the return value has allocated as // a static variable. Per the comment at ASTResultSynthesizer::SynthesizeBodyResult, // accesses to this static variable need to be redirected to the result of dereferencing // a pointer that is passed in as one of the arguments. // // Consequently, when reporting the size of the type, we report a pointer type pointing // to the type of $__lldb_expr_result, not the type itself. // // We also do this for any user-declared persistent variables. clang_type = clang_type.GetPointerType(); value_type = PointerType::get(global_variable->getType(), 0); } else { value_type = global_variable->getType(); } const uint64_t value_size = clang_type.GetByteSize(); off_t value_alignment = (clang_type.GetTypeBitAlign() + 7ull) / 8ull; if (log) { log->Printf("Type of \"%s\" is [clang \"%s\", llvm \"%s\"] [size %" PRIu64 ", align %" PRId64 "]", name.c_str(), clang_type.GetQualType().getAsString().c_str(), PrintType(value_type).c_str(), value_size, value_alignment); } if (named_decl && !m_decl_map->AddValueToStruct(named_decl, lldb_private::ConstString (name.c_str()), llvm_value_ptr, value_size, value_alignment)) { if (!global_variable->hasExternalLinkage()) return true; else if (HandleSymbol (global_variable)) return true; else return false; } } else if (dyn_cast(llvm_value_ptr)) { if (log) log->Printf("Function pointers aren't handled right now"); return false; } return true; } // This function does not report errors; its callers are responsible. bool IRForTarget::HandleSymbol (Value *symbol) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); lldb_private::ConstString name(symbol->getName().str().c_str()); lldb::addr_t symbol_addr = m_decl_map->GetSymbolAddress (name, lldb::eSymbolTypeAny); if (symbol_addr == LLDB_INVALID_ADDRESS) { if (log) log->Printf ("Symbol \"%s\" had no address", name.GetCString()); return false; } if (log) log->Printf("Found \"%s\" at 0x%" PRIx64, name.GetCString(), symbol_addr); Type *symbol_type = symbol->getType(); IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); Constant *symbol_addr_int = ConstantInt::get(intptr_ty, symbol_addr, false); Value *symbol_addr_ptr = ConstantExpr::getIntToPtr(symbol_addr_int, symbol_type); if (log) log->Printf("Replacing %s with %s", PrintValue(symbol).c_str(), PrintValue(symbol_addr_ptr).c_str()); symbol->replaceAllUsesWith(symbol_addr_ptr); return true; } bool IRForTarget::MaybeHandleCallArguments (CallInst *Old) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); if (log) log->Printf("MaybeHandleCallArguments(%s)", PrintValue(Old).c_str()); for (unsigned op_index = 0, num_ops = Old->getNumArgOperands(); op_index < num_ops; ++op_index) if (!MaybeHandleVariable(Old->getArgOperand(op_index))) // conservatively believe that this is a store { if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Couldn't rewrite one of the arguments of a function call.\n"); return false; } return true; } bool IRForTarget::HandleObjCClass(Value *classlist_reference) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); GlobalVariable *global_variable = dyn_cast(classlist_reference); if (!global_variable) return false; Constant *initializer = global_variable->getInitializer(); if (!initializer) return false; if (!initializer->hasName()) return false; StringRef name(initializer->getName()); lldb_private::ConstString name_cstr(name.str().c_str()); lldb::addr_t class_ptr = m_decl_map->GetSymbolAddress(name_cstr, lldb::eSymbolTypeObjCClass); if (log) log->Printf("Found reference to Objective-C class %s (0x%llx)", name_cstr.AsCString(), (unsigned long long)class_ptr); if (class_ptr == LLDB_INVALID_ADDRESS) return false; if (global_variable->use_begin() == global_variable->use_end()) return false; SmallVector load_instructions; for (Value::use_iterator i = global_variable->use_begin(), e = global_variable->use_end(); i != e; ++i) { if (LoadInst *load_instruction = dyn_cast(*i)) load_instructions.push_back(load_instruction); } if (load_instructions.empty()) return false; IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); Constant *class_addr = ConstantInt::get(intptr_ty, (uint64_t)class_ptr); for (LoadInst *load_instruction : load_instructions) { Constant *class_bitcast = ConstantExpr::getIntToPtr(class_addr, load_instruction->getType()); load_instruction->replaceAllUsesWith(class_bitcast); load_instruction->eraseFromParent(); } return true; } bool IRForTarget::RemoveCXAAtExit (BasicBlock &basic_block) { BasicBlock::iterator ii; std::vector calls_to_remove; for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) { Instruction &inst = *ii; CallInst *call = dyn_cast(&inst); // MaybeHandleCallArguments handles error reporting; we are silent here if (!call) continue; bool remove = false; llvm::Function *func = call->getCalledFunction(); if (func && func->getName() == "__cxa_atexit") remove = true; llvm::Value *val = call->getCalledValue(); if (val && val->getName() == "__cxa_atexit") remove = true; if (remove) calls_to_remove.push_back(call); } for (std::vector::iterator ci = calls_to_remove.begin(), ce = calls_to_remove.end(); ci != ce; ++ci) { (*ci)->eraseFromParent(); } return true; } bool IRForTarget::ResolveCalls(BasicBlock &basic_block) { ///////////////////////////////////////////////////////////////////////// // Prepare the current basic block for execution in the remote process // BasicBlock::iterator ii; for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) { Instruction &inst = *ii; CallInst *call = dyn_cast(&inst); // MaybeHandleCallArguments handles error reporting; we are silent here if (call && !MaybeHandleCallArguments(call)) return false; } return true; } bool IRForTarget::ResolveExternals (Function &llvm_function) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); for (Module::global_iterator global = m_module->global_begin(), end = m_module->global_end(); global != end; ++global) { if (!global) { if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: global variable is NULL"); return false; } std::string global_name = (*global).getName().str(); if (log) log->Printf("Examining %s, DeclForGlobalValue returns %p", global_name.c_str(), DeclForGlobal(global)); if (global_name.find("OBJC_IVAR") == 0) { if (!HandleSymbol(global)) { if (m_error_stream) m_error_stream->Printf("Error [IRForTarget]: Couldn't find Objective-C indirect ivar symbol %s\n", global_name.c_str()); return false; } } else if (global_name.find("OBJC_CLASSLIST_REFERENCES_$") != global_name.npos) { if (!HandleObjCClass(global)) { if (m_error_stream) m_error_stream->Printf("Error [IRForTarget]: Couldn't resolve the class for an Objective-C static method call\n"); return false; } } else if (global_name.find("OBJC_CLASSLIST_SUP_REFS_$") != global_name.npos) { if (!HandleObjCClass(global)) { if (m_error_stream) m_error_stream->Printf("Error [IRForTarget]: Couldn't resolve the class for an Objective-C static method call\n"); return false; } } else if (DeclForGlobal(global)) { if (!MaybeHandleVariable (global)) { if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Couldn't rewrite external variable %s\n", global_name.c_str()); return false; } } } return true; } bool IRForTarget::ReplaceStrings () { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); typedef std::map OffsetsTy; OffsetsTy offsets; for (Module::global_iterator gi = m_module->global_begin(), ge = m_module->global_end(); gi != ge; ++gi) { GlobalVariable *gv = gi; if (!gv->hasInitializer()) continue; Constant *gc = gv->getInitializer(); std::string str; if (gc->isNullValue()) { Type *gc_type = gc->getType(); ArrayType *gc_array_type = dyn_cast(gc_type); if (!gc_array_type) continue; Type *gc_element_type = gc_array_type->getElementType(); IntegerType *gc_integer_type = dyn_cast(gc_element_type); if (gc_integer_type->getBitWidth() != 8) continue; str = ""; } else { ConstantDataArray *gc_array = dyn_cast(gc); if (!gc_array) continue; if (!gc_array->isCString()) continue; if (log) log->Printf("Found a GlobalVariable with string initializer %s", PrintValue(gc).c_str()); str = gc_array->getAsString(); } offsets[gv] = m_data_allocator.GetStream().GetSize(); m_data_allocator.GetStream().Write(str.c_str(), str.length() + 1); } Type *char_ptr_ty = Type::getInt8PtrTy(m_module->getContext()); for (OffsetsTy::iterator oi = offsets.begin(), oe = offsets.end(); oi != oe; ++oi) { GlobalVariable *gv = oi->first; size_t offset = oi->second; Constant *new_initializer = BuildRelocation(char_ptr_ty, offset); if (log) log->Printf("Replacing GV %s with %s", PrintValue(gv).c_str(), PrintValue(new_initializer).c_str()); for (GlobalVariable::use_iterator ui = gv->use_begin(), ue = gv->use_end(); ui != ue; ++ui) { if (log) log->Printf("Found use %s", PrintValue(*ui).c_str()); ConstantExpr *const_expr = dyn_cast(*ui); StoreInst *store_inst = dyn_cast(*ui); if (const_expr) { if (const_expr->getOpcode() != Instruction::GetElementPtr) { if (log) log->Printf("Use (%s) of string variable is not a GetElementPtr constant", PrintValue(const_expr).c_str()); return false; } Constant *bit_cast = ConstantExpr::getBitCast(new_initializer, const_expr->getOperand(0)->getType()); Constant *new_gep = const_expr->getWithOperandReplaced(0, bit_cast); const_expr->replaceAllUsesWith(new_gep); } else if (store_inst) { Constant *bit_cast = ConstantExpr::getBitCast(new_initializer, store_inst->getValueOperand()->getType()); store_inst->setOperand(0, bit_cast); } else { if (log) log->Printf("Use (%s) of string variable is neither a constant nor a store", PrintValue(const_expr).c_str()); return false; } } gv->eraseFromParent(); } return true; } bool IRForTarget::ReplaceStaticLiterals (llvm::BasicBlock &basic_block) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); typedef SmallVector ConstantList; typedef SmallVector UserList; typedef ConstantList::iterator ConstantIterator; typedef UserList::iterator UserIterator; ConstantList static_constants; UserList static_users; for (BasicBlock::iterator ii = basic_block.begin(), ie = basic_block.end(); ii != ie; ++ii) { llvm::Instruction &inst = *ii; for (Instruction::op_iterator oi = inst.op_begin(), oe = inst.op_end(); oi != oe; ++oi) { Value *operand_val = oi->get(); ConstantFP *operand_constant_fp = dyn_cast(operand_val); if (operand_constant_fp/* && operand_constant_fp->getType()->isX86_FP80Ty()*/) { static_constants.push_back(operand_val); static_users.push_back(ii); } } } ConstantIterator constant_iter; UserIterator user_iter; for (constant_iter = static_constants.begin(), user_iter = static_users.begin(); constant_iter != static_constants.end(); ++constant_iter, ++user_iter) { Value *operand_val = *constant_iter; llvm::Instruction *inst = *user_iter; ConstantFP *operand_constant_fp = dyn_cast(operand_val); if (operand_constant_fp) { Type *operand_type = operand_constant_fp->getType(); APFloat operand_apfloat = operand_constant_fp->getValueAPF(); APInt operand_apint = operand_apfloat.bitcastToAPInt(); const uint8_t* operand_raw_data = (const uint8_t*)operand_apint.getRawData(); size_t operand_data_size = operand_apint.getBitWidth() / 8; if (log) { std::string s; raw_string_ostream ss(s); for (size_t index = 0; index < operand_data_size; ++index) { ss << (uint32_t)operand_raw_data[index]; ss << " "; } ss.flush(); log->Printf("Found ConstantFP with size %zu and raw data %s", operand_data_size, s.c_str()); } lldb_private::DataBufferHeap data(operand_data_size, 0); if (lldb::endian::InlHostByteOrder() != m_data_allocator.GetStream().GetByteOrder()) { uint8_t *data_bytes = data.GetBytes(); for (size_t index = 0; index < operand_data_size; ++index) { data_bytes[index] = operand_raw_data[operand_data_size - (1 + index)]; } } else { memcpy(data.GetBytes(), operand_raw_data, operand_data_size); } uint64_t offset = m_data_allocator.GetStream().GetSize(); size_t align = m_target_data->getPrefTypeAlignment(operand_type); const size_t mask = (align - 1); uint64_t aligned_offset = (offset + mask) & ~mask; m_data_allocator.GetStream().PutNHex8(aligned_offset - offset, 0); offset = aligned_offset; m_data_allocator.GetStream().Write(data.GetBytes(), operand_data_size); llvm::Type *fp_ptr_ty = operand_constant_fp->getType()->getPointerTo(); Constant *new_pointer = BuildRelocation(fp_ptr_ty, aligned_offset); llvm::LoadInst *fp_load = new llvm::LoadInst(new_pointer, "fp_load", inst); operand_constant_fp->replaceAllUsesWith(fp_load); } } return true; } static bool isGuardVariableRef(Value *V) { Constant *Old = NULL; if (!(Old = dyn_cast(V))) return false; ConstantExpr *CE = NULL; if ((CE = dyn_cast(V))) { if (CE->getOpcode() != Instruction::BitCast) return false; Old = CE->getOperand(0); } GlobalVariable *GV = dyn_cast(Old); if (!GV || !GV->hasName() || !GV->getName().startswith("_ZGV")) return false; return true; } void IRForTarget::TurnGuardLoadIntoZero(llvm::Instruction* guard_load) { Constant* zero(ConstantInt::get(Type::getInt8Ty(m_module->getContext()), 0, true)); Value::use_iterator ui; for (ui = guard_load->use_begin(); ui != guard_load->use_end(); ++ui) { if (isa(*ui)) { // do nothing for the moment } else { ui->replaceUsesOfWith(guard_load, zero); } } guard_load->eraseFromParent(); } static void ExciseGuardStore(Instruction* guard_store) { guard_store->eraseFromParent(); } bool IRForTarget::RemoveGuards(BasicBlock &basic_block) { /////////////////////////////////////////////////////// // Eliminate any reference to guard variables found. // BasicBlock::iterator ii; typedef SmallVector InstrList; typedef InstrList::iterator InstrIterator; InstrList guard_loads; InstrList guard_stores; for (ii = basic_block.begin(); ii != basic_block.end(); ++ii) { Instruction &inst = *ii; if (LoadInst *load = dyn_cast(&inst)) if (isGuardVariableRef(load->getPointerOperand())) guard_loads.push_back(&inst); if (StoreInst *store = dyn_cast(&inst)) if (isGuardVariableRef(store->getPointerOperand())) guard_stores.push_back(&inst); } InstrIterator iter; for (iter = guard_loads.begin(); iter != guard_loads.end(); ++iter) TurnGuardLoadIntoZero(*iter); for (iter = guard_stores.begin(); iter != guard_stores.end(); ++iter) ExciseGuardStore(*iter); return true; } // This function does not report errors; its callers are responsible. bool IRForTarget::UnfoldConstant(Constant *old_constant, FunctionValueCache &value_maker, FunctionValueCache &entry_instruction_finder) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); Value::use_iterator ui; SmallVector users; // We do this because the use list might change, invalidating our iterator. // Much better to keep a work list ourselves. for (ui = old_constant->use_begin(); ui != old_constant->use_end(); ++ui) users.push_back(*ui); for (size_t i = 0; i < users.size(); ++i) { User *user = users[i]; if (Constant *constant = dyn_cast(user)) { // synthesize a new non-constant equivalent of the constant if (ConstantExpr *constant_expr = dyn_cast(constant)) { switch (constant_expr->getOpcode()) { default: if (log) log->Printf("Unhandled constant expression type: \"%s\"", PrintValue(constant_expr).c_str()); return false; case Instruction::BitCast: { FunctionValueCache bit_cast_maker ([&value_maker, &entry_instruction_finder, old_constant, constant_expr] (llvm::Function *function)->llvm::Value* { // UnaryExpr // OperandList[0] is value if (constant_expr->getOperand(0) != old_constant) return constant_expr; return new BitCastInst(value_maker.GetValue(function), constant_expr->getType(), "", llvm::cast(entry_instruction_finder.GetValue(function))); }); if (!UnfoldConstant(constant_expr, bit_cast_maker, entry_instruction_finder)) return false; } break; case Instruction::GetElementPtr: { // GetElementPtrConstantExpr // OperandList[0] is base // OperandList[1]... are indices FunctionValueCache get_element_pointer_maker ([&value_maker, &entry_instruction_finder, old_constant, constant_expr] (llvm::Function *function)->llvm::Value* { Value *ptr = constant_expr->getOperand(0); if (ptr == old_constant) ptr = value_maker.GetValue(function); std::vector index_vector; unsigned operand_index; unsigned num_operands = constant_expr->getNumOperands(); for (operand_index = 1; operand_index < num_operands; ++operand_index) { Value *operand = constant_expr->getOperand(operand_index); if (operand == old_constant) operand = value_maker.GetValue(function); index_vector.push_back(operand); } ArrayRef indices(index_vector); return GetElementPtrInst::Create(ptr, indices, "", llvm::cast(entry_instruction_finder.GetValue(function))); }); if (!UnfoldConstant(constant_expr, get_element_pointer_maker, entry_instruction_finder)) return false; } break; } } else { if (log) log->Printf("Unhandled constant type: \"%s\"", PrintValue(constant).c_str()); return false; } } else { if (Instruction *inst = llvm::dyn_cast(user)) { inst->replaceUsesOfWith(old_constant, value_maker.GetValue(inst->getParent()->getParent())); } else { if (log) log->Printf("Unhandled non-constant type: \"%s\"", PrintValue(user).c_str()); return false; } } } if (!isa(old_constant)) { old_constant->destroyConstant(); } return true; } bool IRForTarget::ReplaceVariables (Function &llvm_function) { if (!m_resolve_vars) return true; lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); m_decl_map->DoStructLayout(); if (log) log->Printf("Element arrangement:"); uint32_t num_elements; uint32_t element_index; size_t size; off_t alignment; if (!m_decl_map->GetStructInfo (num_elements, size, alignment)) return false; Function::arg_iterator iter(llvm_function.getArgumentList().begin()); if (iter == llvm_function.getArgumentList().end()) { if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes no arguments (should take at least a struct pointer)"); return false; } Argument *argument = iter; if (argument->getName().equals("this")) { ++iter; if (iter == llvm_function.getArgumentList().end()) { if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes only 'this' argument (should take a struct pointer too)"); return false; } argument = iter; } else if (argument->getName().equals("self")) { ++iter; if (iter == llvm_function.getArgumentList().end()) { if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes only 'self' argument (should take '_cmd' and a struct pointer too)"); return false; } if (!iter->getName().equals("_cmd")) { if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes '%s' after 'self' argument (should take '_cmd')", iter->getName().str().c_str()); return false; } ++iter; if (iter == llvm_function.getArgumentList().end()) { if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes only 'self' and '_cmd' arguments (should take a struct pointer too)"); return false; } argument = iter; } if (!argument->getName().equals("$__lldb_arg")) { if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Wrapper takes an argument named '%s' instead of the struct pointer", argument->getName().str().c_str()); return false; } if (log) log->Printf("Arg: \"%s\"", PrintValue(argument).c_str()); BasicBlock &entry_block(llvm_function.getEntryBlock()); Instruction *FirstEntryInstruction(entry_block.getFirstNonPHIOrDbg()); if (!FirstEntryInstruction) { if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Couldn't find the first instruction in the wrapper for use in rewriting"); return false; } LLVMContext &context(m_module->getContext()); IntegerType *offset_type(Type::getInt32Ty(context)); if (!offset_type) { if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Couldn't produce an offset type"); return false; } for (element_index = 0; element_index < num_elements; ++element_index) { const clang::NamedDecl *decl = NULL; Value *value = NULL; off_t offset; lldb_private::ConstString name; if (!m_decl_map->GetStructElement (decl, value, offset, name, element_index)) { if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Structure information is incomplete"); return false; } if (log) log->Printf(" \"%s\" (\"%s\") placed at %" PRId64, name.GetCString(), decl->getNameAsString().c_str(), offset); if (value) { if (log) log->Printf(" Replacing [%s]", PrintValue(value).c_str()); FunctionValueCache body_result_maker ([this, name, offset_type, offset, argument, value] (llvm::Function *function)->llvm::Value * { // Per the comment at ASTResultSynthesizer::SynthesizeBodyResult, in cases where the result // variable is an rvalue, we have to synthesize a dereference of the appropriate structure // entry in order to produce the static variable that the AST thinks it is accessing. llvm::Instruction *entry_instruction = llvm::cast(m_entry_instruction_finder.GetValue(function)); ConstantInt *offset_int(ConstantInt::get(offset_type, offset, true)); GetElementPtrInst *get_element_ptr = GetElementPtrInst::Create(argument, offset_int, "", entry_instruction); if (name == m_result_name && !m_result_is_pointer) { BitCastInst *bit_cast = new BitCastInst(get_element_ptr, value->getType()->getPointerTo(), "", entry_instruction); LoadInst *load = new LoadInst(bit_cast, "", entry_instruction); return load; } else { BitCastInst *bit_cast = new BitCastInst(get_element_ptr, value->getType(), "", entry_instruction); return bit_cast; } }); if (Constant *constant = dyn_cast(value)) { UnfoldConstant(constant, body_result_maker, m_entry_instruction_finder); } else if (Instruction *instruction = dyn_cast(value)) { value->replaceAllUsesWith(body_result_maker.GetValue(instruction->getParent()->getParent())); } else { if (log) log->Printf("Unhandled non-constant type: \"%s\"", PrintValue(value).c_str()); return false; } if (GlobalVariable *var = dyn_cast(value)) var->eraseFromParent(); } } if (log) log->Printf("Total structure [align %" PRId64 ", size %zu]", alignment, size); return true; } llvm::Constant * IRForTarget::BuildRelocation(llvm::Type *type, uint64_t offset) { IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); llvm::Constant *offset_int = ConstantInt::get(intptr_ty, offset); llvm::Constant *offset_array[1]; offset_array[0] = offset_int; llvm::ArrayRef offsets(offset_array, 1); llvm::Constant *reloc_getelementptr = ConstantExpr::getGetElementPtr(m_reloc_placeholder, offsets); llvm::Constant *reloc_getbitcast = ConstantExpr::getBitCast(reloc_getelementptr, type); return reloc_getbitcast; } bool IRForTarget::CompleteDataAllocation () { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); if (!m_data_allocator.GetStream().GetSize()) return true; lldb::addr_t allocation = m_data_allocator.Allocate(); if (log) { if (allocation) log->Printf("Allocated static data at 0x%llx", (unsigned long long)allocation); else log->Printf("Failed to allocate static data"); } if (!allocation || allocation == LLDB_INVALID_ADDRESS) return false; IntegerType *intptr_ty = Type::getIntNTy(m_module->getContext(), (m_module->getPointerSize() == Module::Pointer64) ? 64 : 32); Constant *relocated_addr = ConstantInt::get(intptr_ty, (uint64_t)allocation); Constant *relocated_bitcast = ConstantExpr::getIntToPtr(relocated_addr, llvm::Type::getInt8PtrTy(m_module->getContext())); m_reloc_placeholder->replaceAllUsesWith(relocated_bitcast); m_reloc_placeholder->eraseFromParent(); return true; } bool IRForTarget::StripAllGVs (Module &llvm_module) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); std::vector global_vars; std::seterased_vars; bool erased = true; while (erased) { erased = false; for (Module::global_iterator gi = llvm_module.global_begin(), ge = llvm_module.global_end(); gi != ge; ++gi) { GlobalVariable *global_var = dyn_cast(gi); global_var->removeDeadConstantUsers(); if (global_var->use_empty()) { if (log) log->Printf("Did remove %s", PrintValue(global_var).c_str()); global_var->eraseFromParent(); erased = true; break; } } } for (Module::global_iterator gi = llvm_module.global_begin(), ge = llvm_module.global_end(); gi != ge; ++gi) { GlobalVariable *global_var = dyn_cast(gi); GlobalValue::use_iterator ui = global_var->use_begin(); if (log) log->Printf("Couldn't remove %s because of %s", PrintValue(global_var).c_str(), PrintValue(*ui).c_str()); } return true; } bool IRForTarget::runOnModule (Module &llvm_module) { lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)); m_module = &llvm_module; m_target_data.reset(new DataLayout(m_module)); if (log) { std::string s; raw_string_ostream oss(s); m_module->print(oss, NULL); oss.flush(); log->Printf("Module as passed in to IRForTarget: \n\"%s\"", s.c_str()); } Function* main_function = m_module->getFunction(StringRef(m_func_name.c_str())); if (!main_function) { if (log) log->Printf("Couldn't find \"%s()\" in the module", m_func_name.c_str()); if (m_error_stream) m_error_stream->Printf("Internal error [IRForTarget]: Couldn't find wrapper '%s' in the module", m_func_name.c_str()); return false; } if (!FixFunctionLinkage (*main_function)) { if (log) log->Printf("Couldn't fix the linkage for the function"); return false; } llvm::Type *intptr_ty = Type::getInt8Ty(m_module->getContext()); m_reloc_placeholder = new llvm::GlobalVariable((*m_module), intptr_ty, false /* IsConstant */, GlobalVariable::InternalLinkage, Constant::getNullValue(intptr_ty), "reloc_placeholder", NULL /* InsertBefore */, GlobalVariable::NotThreadLocal /* ThreadLocal */, 0 /* AddressSpace */); //////////////////////////////////////////////////////////// // Replace $__lldb_expr_result with a persistent variable // if (!CreateResultVariable(*main_function)) { if (log) log->Printf("CreateResultVariable() failed"); // CreateResultVariable() reports its own errors, so we don't do so here return false; } if (log && log->GetVerbose()) { std::string s; raw_string_ostream oss(s); m_module->print(oss, NULL); oss.flush(); log->Printf("Module after creating the result variable: \n\"%s\"", s.c_str()); } for (Module::iterator fi = m_module->begin(), fe = m_module->end(); fi != fe; ++fi) { llvm::Function *function = fi; if (function->begin() == function->end()) continue; Function::iterator bbi; for (bbi = function->begin(); bbi != function->end(); ++bbi) { if (!RemoveGuards(*bbi)) { if (log) log->Printf("RemoveGuards() failed"); // RemoveGuards() reports its own errors, so we don't do so here return false; } if (!RewritePersistentAllocs(*bbi)) { if (log) log->Printf("RewritePersistentAllocs() failed"); // RewritePersistentAllocs() reports its own errors, so we don't do so here return false; } if (!RemoveCXAAtExit(*bbi)) { if (log) log->Printf("RemoveCXAAtExit() failed"); // RemoveCXAAtExit() reports its own errors, so we don't do so here return false; } } } /////////////////////////////////////////////////////////////////////////////// // Fix all Objective-C constant strings to use NSStringWithCString:encoding: // if (!RewriteObjCConstStrings()) { if (log) log->Printf("RewriteObjCConstStrings() failed"); // RewriteObjCConstStrings() reports its own errors, so we don't do so here return false; } /////////////////////////////// // Resolve function pointers // if (!ResolveFunctionPointers(llvm_module)) { if (log) log->Printf("ResolveFunctionPointers() failed"); // ResolveFunctionPointers() reports its own errors, so we don't do so here return false; } for (Module::iterator fi = m_module->begin(), fe = m_module->end(); fi != fe; ++fi) { llvm::Function *function = fi; for (llvm::Function::iterator bbi = function->begin(), bbe = function->end(); bbi != bbe; ++bbi) { if (!RewriteObjCSelectors(*bbi)) { if (log) log->Printf("RewriteObjCSelectors() failed"); // RewriteObjCSelectors() reports its own errors, so we don't do so here return false; } } } for (Module::iterator fi = m_module->begin(), fe = m_module->end(); fi != fe; ++fi) { llvm::Function *function = fi; for (llvm::Function::iterator bbi = function->begin(), bbe = function->end(); bbi != bbe; ++bbi) { if (!ResolveCalls(*bbi)) { if (log) log->Printf("ResolveCalls() failed"); // ResolveCalls() reports its own errors, so we don't do so here return false; } if (!ReplaceStaticLiterals(*bbi)) { if (log) log->Printf("ReplaceStaticLiterals() failed"); return false; } } } //////////////////////////////////////////////////////////////////////// // Run function-level passes that only make sense on the main function // if (!ResolveExternals(*main_function)) { if (log) log->Printf("ResolveExternals() failed"); // ResolveExternals() reports its own errors, so we don't do so here return false; } if (!ReplaceVariables(*main_function)) { if (log) log->Printf("ReplaceVariables() failed"); // ReplaceVariables() reports its own errors, so we don't do so here return false; } if (!ReplaceStrings()) { if (log) log->Printf("ReplaceStrings() failed"); return false; } if (!CompleteDataAllocation()) { if (log) log->Printf("CompleteDataAllocation() failed"); return false; } if (!StripAllGVs(llvm_module)) { if (log) log->Printf("StripAllGVs() failed"); } if (log && log->GetVerbose()) { std::string s; raw_string_ostream oss(s); m_module->print(oss, NULL); oss.flush(); log->Printf("Module after preparing for execution: \n\"%s\"", s.c_str()); } return true; } void IRForTarget::assignPassManager (PMStack &pass_mgr_stack, PassManagerType pass_mgr_type) { } PassManagerType IRForTarget::getPotentialPassManagerType() const { return PMT_ModulePassManager; } Index: head/contrib/llvm/tools/lldb/source/Host/common/FileSpec.cpp =================================================================== --- head/contrib/llvm/tools/lldb/source/Host/common/FileSpec.cpp (revision 262120) +++ head/contrib/llvm/tools/lldb/source/Host/common/FileSpec.cpp (revision 262121) @@ -1,1310 +1,1309 @@ //===-- FileSpec.cpp --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef _WIN32 #include #else #include "lldb/Host/windows/windows.h" #endif #include #ifndef _MSC_VER #include #endif #include #include #include #include #include "lldb/Host/Config.h" // Have to include this before we test the define... #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER #include #endif #include "llvm/ADT/StringRef.h" #include "llvm/Support/Path.h" #include "llvm/Support/Program.h" #include "lldb/Core/StreamString.h" #include "lldb/Host/File.h" #include "lldb/Host/FileSpec.h" #include "lldb/Host/Host.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/DataBufferMemoryMap.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/Stream.h" #include "lldb/Host/Host.h" #include "lldb/Utility/CleanUp.h" using namespace lldb; using namespace lldb_private; static bool GetFileStats (const FileSpec *file_spec, struct stat *stats_ptr) { char resolved_path[PATH_MAX]; if (file_spec->GetPath (resolved_path, sizeof(resolved_path))) return ::stat (resolved_path, stats_ptr) == 0; return false; } #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER static const char* GetCachedGlobTildeSlash() { static std::string g_tilde; if (g_tilde.empty()) { struct passwd *user_entry; user_entry = getpwuid(geteuid()); if (user_entry != NULL) g_tilde = user_entry->pw_dir; if (g_tilde.empty()) return NULL; } return g_tilde.c_str(); } #endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER // Resolves the username part of a path of the form ~user/other/directories, and // writes the result into dst_path. // Returns 0 if there WAS a ~ in the path but the username couldn't be resolved. // Otherwise returns the number of characters copied into dst_path. If the return // is >= dst_len, then the resolved path is too long... size_t FileSpec::ResolveUsername (const char *src_path, char *dst_path, size_t dst_len) { if (src_path == NULL || src_path[0] == '\0') return 0; #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER char user_home[PATH_MAX]; const char *user_name; // If there's no ~, then just copy src_path straight to dst_path (they may be the same string...) if (src_path[0] != '~') { size_t len = strlen (src_path); if (len >= dst_len) { ::bcopy (src_path, dst_path, dst_len - 1); dst_path[dst_len] = '\0'; } else ::bcopy (src_path, dst_path, len + 1); return len; } const char *first_slash = ::strchr (src_path, '/'); char remainder[PATH_MAX]; if (first_slash == NULL) { // The whole name is the username (minus the ~): user_name = src_path + 1; remainder[0] = '\0'; } else { size_t user_name_len = first_slash - src_path - 1; ::memcpy (user_home, src_path + 1, user_name_len); user_home[user_name_len] = '\0'; user_name = user_home; ::strcpy (remainder, first_slash); } if (user_name == NULL) return 0; // User name of "" means the current user... struct passwd *user_entry; const char *home_dir = NULL; if (user_name[0] == '\0') { home_dir = GetCachedGlobTildeSlash(); } else { user_entry = ::getpwnam (user_name); if (user_entry != NULL) home_dir = user_entry->pw_dir; } if (home_dir == NULL) return 0; else return ::snprintf (dst_path, dst_len, "%s%s", home_dir, remainder); #else // Resolving home directories is not supported, just copy the path... return ::snprintf (dst_path, dst_len, "%s", src_path); #endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER } size_t FileSpec::ResolvePartialUsername (const char *partial_name, StringList &matches) { #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER size_t extant_entries = matches.GetSize(); setpwent(); struct passwd *user_entry; const char *name_start = partial_name + 1; std::set name_list; while ((user_entry = getpwent()) != NULL) { if (strstr(user_entry->pw_name, name_start) == user_entry->pw_name) { std::string tmp_buf("~"); tmp_buf.append(user_entry->pw_name); tmp_buf.push_back('/'); name_list.insert(tmp_buf); } } std::set::iterator pos, end = name_list.end(); for (pos = name_list.begin(); pos != end; pos++) { matches.AppendString((*pos).c_str()); } return matches.GetSize() - extant_entries; #else // Resolving home directories is not supported, just copy the path... return 0; #endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER } size_t FileSpec::Resolve (const char *src_path, char *dst_path, size_t dst_len) { if (src_path == NULL || src_path[0] == '\0') return 0; // Glob if needed for ~/, otherwise copy in case src_path is same as dst_path... char unglobbed_path[PATH_MAX]; #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER if (src_path[0] == '~') { size_t return_count = ResolveUsername(src_path, unglobbed_path, sizeof(unglobbed_path)); // If we couldn't find the user referred to, or the resultant path was too long, // then just copy over the src_path. if (return_count == 0 || return_count >= sizeof(unglobbed_path)) ::snprintf (unglobbed_path, sizeof(unglobbed_path), "%s", src_path); } else #endif // #ifdef LLDB_CONFIG_TILDE_RESOLVES_TO_USER { ::snprintf(unglobbed_path, sizeof(unglobbed_path), "%s", src_path); } // Now resolve the path if needed char resolved_path[PATH_MAX]; if (::realpath (unglobbed_path, resolved_path)) { // Success, copy the resolved path return ::snprintf(dst_path, dst_len, "%s", resolved_path); } else { // Failed, just copy the unglobbed path return ::snprintf(dst_path, dst_len, "%s", unglobbed_path); } } FileSpec::FileSpec() : m_directory(), m_filename() { } //------------------------------------------------------------------ // Default constructor that can take an optional full path to a // file on disk. //------------------------------------------------------------------ FileSpec::FileSpec(const char *pathname, bool resolve_path) : m_directory(), m_filename(), m_is_resolved(false) { if (pathname && pathname[0]) SetFile(pathname, resolve_path); } //------------------------------------------------------------------ // Copy constructor //------------------------------------------------------------------ FileSpec::FileSpec(const FileSpec& rhs) : m_directory (rhs.m_directory), m_filename (rhs.m_filename), m_is_resolved (rhs.m_is_resolved) { } //------------------------------------------------------------------ // Copy constructor //------------------------------------------------------------------ FileSpec::FileSpec(const FileSpec* rhs) : m_directory(), m_filename() { if (rhs) *this = *rhs; } //------------------------------------------------------------------ // Virtual destrcuctor in case anyone inherits from this class. //------------------------------------------------------------------ FileSpec::~FileSpec() { } //------------------------------------------------------------------ // Assignment operator. //------------------------------------------------------------------ const FileSpec& FileSpec::operator= (const FileSpec& rhs) { if (this != &rhs) { m_directory = rhs.m_directory; m_filename = rhs.m_filename; m_is_resolved = rhs.m_is_resolved; } return *this; } //------------------------------------------------------------------ // Update the contents of this object with a new path. The path will // be split up into a directory and filename and stored as uniqued // string values for quick comparison and efficient memory usage. //------------------------------------------------------------------ void FileSpec::SetFile (const char *pathname, bool resolve) { m_filename.Clear(); m_directory.Clear(); m_is_resolved = false; if (pathname == NULL || pathname[0] == '\0') return; char resolved_path[PATH_MAX]; bool path_fit = true; if (resolve) { path_fit = (FileSpec::Resolve (pathname, resolved_path, sizeof(resolved_path)) < sizeof(resolved_path) - 1); m_is_resolved = path_fit; } else { // Copy the path because "basename" and "dirname" want to muck with the // path buffer if (::strlen (pathname) > sizeof(resolved_path) - 1) path_fit = false; else ::strcpy (resolved_path, pathname); } if (path_fit) { char *filename = ::basename (resolved_path); if (filename) { m_filename.SetCString (filename); // Truncate the basename off the end of the resolved path // Only attempt to get the dirname if it looks like we have a path if (strchr(resolved_path, '/') #ifdef _WIN32 || strchr(resolved_path, '\\') #endif ) { char *directory = ::dirname (resolved_path); // Make sure we didn't get our directory resolved to "." without having // specified if (directory) m_directory.SetCString(directory); else { char *last_resolved_path_slash = strrchr(resolved_path, '/'); #ifdef _WIN32 char* last_resolved_path_slash_windows = strrchr(resolved_path, '\\'); if (last_resolved_path_slash_windows > last_resolved_path_slash) last_resolved_path_slash = last_resolved_path_slash_windows; #endif if (last_resolved_path_slash) { *last_resolved_path_slash = '\0'; m_directory.SetCString(resolved_path); } } } } else m_directory.SetCString(resolved_path); } } //---------------------------------------------------------------------- // Convert to pointer operator. This allows code to check any FileSpec // objects to see if they contain anything valid using code such as: // // if (file_spec) // {} //---------------------------------------------------------------------- FileSpec::operator bool() const { return m_filename || m_directory; } //---------------------------------------------------------------------- // Logical NOT operator. This allows code to check any FileSpec // objects to see if they are invalid using code such as: // // if (!file_spec) // {} //---------------------------------------------------------------------- bool FileSpec::operator!() const { return !m_directory && !m_filename; } //------------------------------------------------------------------ // Equal to operator //------------------------------------------------------------------ bool FileSpec::operator== (const FileSpec& rhs) const { if (m_filename == rhs.m_filename) { if (m_directory == rhs.m_directory) return true; // TODO: determine if we want to keep this code in here. // The code below was added to handle a case where we were // trying to set a file and line breakpoint and one path // was resolved, and the other not and the directory was // in a mount point that resolved to a more complete path: // "/tmp/a.c" == "/private/tmp/a.c". I might end up pulling // this out... if (IsResolved() && rhs.IsResolved()) { // Both paths are resolved, no need to look further... return false; } FileSpec resolved_lhs(*this); // If "this" isn't resolved, resolve it if (!IsResolved()) { if (resolved_lhs.ResolvePath()) { // This path wasn't resolved but now it is. Check if the resolved // directory is the same as our unresolved directory, and if so, // we can mark this object as resolved to avoid more future resolves m_is_resolved = (m_directory == resolved_lhs.m_directory); } else return false; } FileSpec resolved_rhs(rhs); if (!rhs.IsResolved()) { if (resolved_rhs.ResolvePath()) { // rhs's path wasn't resolved but now it is. Check if the resolved // directory is the same as rhs's unresolved directory, and if so, // we can mark this object as resolved to avoid more future resolves rhs.m_is_resolved = (rhs.m_directory == resolved_rhs.m_directory); } else return false; } // If we reach this point in the code we were able to resolve both paths // and since we only resolve the paths if the basenames are equal, then // we can just check if both directories are equal... return resolved_lhs.GetDirectory() == resolved_rhs.GetDirectory(); } return false; } //------------------------------------------------------------------ // Not equal to operator //------------------------------------------------------------------ bool FileSpec::operator!= (const FileSpec& rhs) const { return !(*this == rhs); } //------------------------------------------------------------------ // Less than operator //------------------------------------------------------------------ bool FileSpec::operator< (const FileSpec& rhs) const { return FileSpec::Compare(*this, rhs, true) < 0; } //------------------------------------------------------------------ // Dump a FileSpec object to a stream //------------------------------------------------------------------ Stream& lldb_private::operator << (Stream &s, const FileSpec& f) { f.Dump(&s); return s; } //------------------------------------------------------------------ // Clear this object by releasing both the directory and filename // string values and making them both the empty string. //------------------------------------------------------------------ void FileSpec::Clear() { m_directory.Clear(); m_filename.Clear(); } //------------------------------------------------------------------ // Compare two FileSpec objects. If "full" is true, then both // the directory and the filename must match. If "full" is false, // then the directory names for "a" and "b" are only compared if // they are both non-empty. This allows a FileSpec object to only // contain a filename and it can match FileSpec objects that have // matching filenames with different paths. // // Return -1 if the "a" is less than "b", 0 if "a" is equal to "b" // and "1" if "a" is greater than "b". //------------------------------------------------------------------ int FileSpec::Compare(const FileSpec& a, const FileSpec& b, bool full) { int result = 0; // If full is true, then we must compare both the directory and filename. // If full is false, then if either directory is empty, then we match on // the basename only, and if both directories have valid values, we still // do a full compare. This allows for matching when we just have a filename // in one of the FileSpec objects. if (full || (a.m_directory && b.m_directory)) { result = ConstString::Compare(a.m_directory, b.m_directory); if (result) return result; } return ConstString::Compare (a.m_filename, b.m_filename); } bool FileSpec::Equal (const FileSpec& a, const FileSpec& b, bool full) { if (!full && (a.GetDirectory().IsEmpty() || b.GetDirectory().IsEmpty())) return a.m_filename == b.m_filename; else return a == b; } //------------------------------------------------------------------ // Dump the object to the supplied stream. If the object contains // a valid directory name, it will be displayed followed by a // directory delimiter, and the filename. //------------------------------------------------------------------ void FileSpec::Dump(Stream *s) const { static ConstString g_slash_only ("/"); if (s) { m_directory.Dump(s); if (m_directory && m_directory != g_slash_only) s->PutChar('/'); m_filename.Dump(s); } } //------------------------------------------------------------------ // Returns true if the file exists. //------------------------------------------------------------------ bool FileSpec::Exists () const { struct stat file_stats; return GetFileStats (this, &file_stats); } bool FileSpec::ResolveExecutableLocation () { if (!m_directory) { const char *file_cstr = m_filename.GetCString(); if (file_cstr) { const std::string file_str (file_cstr); - llvm::sys::Path path = llvm::sys::Program::FindProgramByName (file_str); - const std::string &path_str = path.str(); - llvm::StringRef dir_ref = llvm::sys::path::parent_path(path_str); + std::string path = llvm::sys::FindProgramByName (file_str); + llvm::StringRef dir_ref = llvm::sys::path::parent_path(path); //llvm::StringRef dir_ref = path.getDirname(); if (! dir_ref.empty()) { // FindProgramByName returns "." if it can't find the file. if (strcmp (".", dir_ref.data()) == 0) return false; m_directory.SetCString (dir_ref.data()); if (Exists()) return true; else { // If FindProgramByName found the file, it returns the directory + filename in its return results. // We need to separate them. FileSpec tmp_file (dir_ref.data(), false); if (tmp_file.Exists()) { m_directory = tmp_file.m_directory; return true; } } } } } return false; } bool FileSpec::ResolvePath () { if (m_is_resolved) return true; // We have already resolved this path char path_buf[PATH_MAX]; if (!GetPath (path_buf, PATH_MAX)) return false; // SetFile(...) will set m_is_resolved correctly if it can resolve the path SetFile (path_buf, true); return m_is_resolved; } uint64_t FileSpec::GetByteSize() const { struct stat file_stats; if (GetFileStats (this, &file_stats)) return file_stats.st_size; return 0; } FileSpec::FileType FileSpec::GetFileType () const { struct stat file_stats; if (GetFileStats (this, &file_stats)) { mode_t file_type = file_stats.st_mode & S_IFMT; switch (file_type) { case S_IFDIR: return eFileTypeDirectory; case S_IFREG: return eFileTypeRegular; #ifndef _WIN32 case S_IFIFO: return eFileTypePipe; case S_IFSOCK: return eFileTypeSocket; case S_IFLNK: return eFileTypeSymbolicLink; #endif default: break; } return eFileTypeUnknown; } return eFileTypeInvalid; } uint32_t FileSpec::GetPermissions () const { uint32_t file_permissions = 0; if (*this) Host::GetFilePermissions(GetPath().c_str(), file_permissions); return file_permissions; } TimeValue FileSpec::GetModificationTime () const { TimeValue mod_time; struct stat file_stats; if (GetFileStats (this, &file_stats)) mod_time.OffsetWithSeconds(file_stats.st_mtime); return mod_time; } //------------------------------------------------------------------ // Directory string get accessor. //------------------------------------------------------------------ ConstString & FileSpec::GetDirectory() { return m_directory; } //------------------------------------------------------------------ // Directory string const get accessor. //------------------------------------------------------------------ const ConstString & FileSpec::GetDirectory() const { return m_directory; } //------------------------------------------------------------------ // Filename string get accessor. //------------------------------------------------------------------ ConstString & FileSpec::GetFilename() { return m_filename; } //------------------------------------------------------------------ // Filename string const get accessor. //------------------------------------------------------------------ const ConstString & FileSpec::GetFilename() const { return m_filename; } //------------------------------------------------------------------ // Extract the directory and path into a fixed buffer. This is // needed as the directory and path are stored in separate string // values. //------------------------------------------------------------------ size_t FileSpec::GetPath(char *path, size_t path_max_len) const { if (path_max_len) { const char *dirname = m_directory.GetCString(); const char *filename = m_filename.GetCString(); if (dirname) { if (filename) return ::snprintf (path, path_max_len, "%s/%s", dirname, filename); else return ::snprintf (path, path_max_len, "%s", dirname); } else if (filename) { return ::snprintf (path, path_max_len, "%s", filename); } } if (path) path[0] = '\0'; return 0; } std::string FileSpec::GetPath (void) const { static ConstString g_slash_only ("/"); std::string path; const char *dirname = m_directory.GetCString(); const char *filename = m_filename.GetCString(); if (dirname) { path.append (dirname); if (filename && m_directory != g_slash_only) path.append ("/"); } if (filename) path.append (filename); return path; } ConstString FileSpec::GetFileNameExtension () const { if (m_filename) { const char *filename = m_filename.GetCString(); const char* dot_pos = strrchr(filename, '.'); if (dot_pos && dot_pos[1] != '\0') return ConstString(dot_pos+1); } return ConstString(); } ConstString FileSpec::GetFileNameStrippingExtension () const { const char *filename = m_filename.GetCString(); if (filename == NULL) return ConstString(); const char* dot_pos = strrchr(filename, '.'); if (dot_pos == NULL) return m_filename; return ConstString(filename, dot_pos-filename); } //------------------------------------------------------------------ // Returns a shared pointer to a data buffer that contains all or // part of the contents of a file. The data is memory mapped and // will lazily page in data from the file as memory is accessed. // The data that is mappped will start "file_offset" bytes into the // file, and "file_size" bytes will be mapped. If "file_size" is // greater than the number of bytes available in the file starting // at "file_offset", the number of bytes will be appropriately // truncated. The final number of bytes that get mapped can be // verified using the DataBuffer::GetByteSize() function. //------------------------------------------------------------------ DataBufferSP FileSpec::MemoryMapFileContents(off_t file_offset, size_t file_size) const { DataBufferSP data_sp; std::unique_ptr mmap_data(new DataBufferMemoryMap()); if (mmap_data.get()) { const size_t mapped_length = mmap_data->MemoryMapFromFileSpec (this, file_offset, file_size); if (((file_size == SIZE_MAX) && (mapped_length > 0)) || (mapped_length >= file_size)) data_sp.reset(mmap_data.release()); } return data_sp; } //------------------------------------------------------------------ // Return the size in bytes that this object takes in memory. This // returns the size in bytes of this object, not any shared string // values it may refer to. //------------------------------------------------------------------ size_t FileSpec::MemorySize() const { return m_filename.MemorySize() + m_directory.MemorySize(); } size_t FileSpec::ReadFileContents (off_t file_offset, void *dst, size_t dst_len, Error *error_ptr) const { Error error; size_t bytes_read = 0; char resolved_path[PATH_MAX]; if (GetPath(resolved_path, sizeof(resolved_path))) { File file; error = file.Open(resolved_path, File::eOpenOptionRead); if (error.Success()) { off_t file_offset_after_seek = file_offset; bytes_read = dst_len; error = file.Read(dst, bytes_read, file_offset_after_seek); } } else { error.SetErrorString("invalid file specification"); } if (error_ptr) *error_ptr = error; return bytes_read; } //------------------------------------------------------------------ // Returns a shared pointer to a data buffer that contains all or // part of the contents of a file. The data copies into a heap based // buffer that lives in the DataBuffer shared pointer object returned. // The data that is cached will start "file_offset" bytes into the // file, and "file_size" bytes will be mapped. If "file_size" is // greater than the number of bytes available in the file starting // at "file_offset", the number of bytes will be appropriately // truncated. The final number of bytes that get mapped can be // verified using the DataBuffer::GetByteSize() function. //------------------------------------------------------------------ DataBufferSP FileSpec::ReadFileContents (off_t file_offset, size_t file_size, Error *error_ptr) const { Error error; DataBufferSP data_sp; char resolved_path[PATH_MAX]; if (GetPath(resolved_path, sizeof(resolved_path))) { File file; error = file.Open(resolved_path, File::eOpenOptionRead); if (error.Success()) { const bool null_terminate = false; error = file.Read (file_size, file_offset, null_terminate, data_sp); } } else { error.SetErrorString("invalid file specification"); } if (error_ptr) *error_ptr = error; return data_sp; } DataBufferSP FileSpec::ReadFileContentsAsCString(Error *error_ptr) { Error error; DataBufferSP data_sp; char resolved_path[PATH_MAX]; if (GetPath(resolved_path, sizeof(resolved_path))) { File file; error = file.Open(resolved_path, File::eOpenOptionRead); if (error.Success()) { off_t offset = 0; size_t length = SIZE_MAX; const bool null_terminate = true; error = file.Read (length, offset, null_terminate, data_sp); } } else { error.SetErrorString("invalid file specification"); } if (error_ptr) *error_ptr = error; return data_sp; } size_t FileSpec::ReadFileLines (STLStringArray &lines) { lines.clear(); char path[PATH_MAX]; if (GetPath(path, sizeof(path))) { std::ifstream file_stream (path); if (file_stream) { std::string line; while (getline (file_stream, line)) lines.push_back (line); } } return lines.size(); } FileSpec::EnumerateDirectoryResult FileSpec::EnumerateDirectory ( const char *dir_path, bool find_directories, bool find_files, bool find_other, EnumerateDirectoryCallbackType callback, void *callback_baton ) { if (dir_path && dir_path[0]) { #if _WIN32 char szDir[MAX_PATH]; strcpy_s(szDir, MAX_PATH, dir_path); strcat_s(szDir, MAX_PATH, "\\*"); WIN32_FIND_DATA ffd; HANDLE hFind = FindFirstFile(szDir, &ffd); if (hFind == INVALID_HANDLE_VALUE) { return eEnumerateDirectoryResultNext; } do { bool call_callback = false; FileSpec::FileType file_type = eFileTypeUnknown; if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { size_t len = strlen(ffd.cFileName); if (len == 1 && ffd.cFileName[0] == '.') continue; if (len == 2 && ffd.cFileName[0] == '.' && ffd.cFileName[1] == '.') continue; file_type = eFileTypeDirectory; call_callback = find_directories; } else if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) { file_type = eFileTypeOther; call_callback = find_other; } else { file_type = eFileTypeRegular; call_callback = find_files; } if (call_callback) { char child_path[MAX_PATH]; const int child_path_len = ::snprintf (child_path, sizeof(child_path), "%s\\%s", dir_path, ffd.cFileName); if (child_path_len < (int)(sizeof(child_path) - 1)) { // Don't resolve the file type or path FileSpec child_path_spec (child_path, false); EnumerateDirectoryResult result = callback (callback_baton, file_type, child_path_spec); switch (result) { case eEnumerateDirectoryResultNext: // Enumerate next entry in the current directory. We just // exit this switch and will continue enumerating the // current directory as we currently are... break; case eEnumerateDirectoryResultEnter: // Recurse into the current entry if it is a directory or symlink, or next if not if (FileSpec::EnumerateDirectory(child_path, find_directories, find_files, find_other, callback, callback_baton) == eEnumerateDirectoryResultQuit) { // The subdirectory returned Quit, which means to // stop all directory enumerations at all levels. return eEnumerateDirectoryResultQuit; } break; case eEnumerateDirectoryResultExit: // Exit from the current directory at the current level. // Exit from this directory level and tell parent to // keep enumerating. return eEnumerateDirectoryResultNext; case eEnumerateDirectoryResultQuit: // Stop directory enumerations at any level return eEnumerateDirectoryResultQuit; } } } } while (FindNextFile(hFind, &ffd) != 0); FindClose(hFind); #else lldb_utility::CleanUp dir_path_dir(opendir(dir_path), NULL, closedir); if (dir_path_dir.is_valid()) { long path_max = fpathconf (dirfd (dir_path_dir.get()), _PC_NAME_MAX); #if defined (__APPLE_) && defined (__DARWIN_MAXPATHLEN) if (path_max < __DARWIN_MAXPATHLEN) path_max = __DARWIN_MAXPATHLEN; #endif struct dirent *buf, *dp; buf = (struct dirent *) malloc (offsetof (struct dirent, d_name) + path_max + 1); while (buf && readdir_r(dir_path_dir.get(), buf, &dp) == 0 && dp) { // Only search directories if (dp->d_type == DT_DIR || dp->d_type == DT_UNKNOWN) { size_t len = strlen(dp->d_name); if (len == 1 && dp->d_name[0] == '.') continue; if (len == 2 && dp->d_name[0] == '.' && dp->d_name[1] == '.') continue; } bool call_callback = false; FileSpec::FileType file_type = eFileTypeUnknown; switch (dp->d_type) { default: case DT_UNKNOWN: file_type = eFileTypeUnknown; call_callback = true; break; case DT_FIFO: file_type = eFileTypePipe; call_callback = find_other; break; case DT_CHR: file_type = eFileTypeOther; call_callback = find_other; break; case DT_DIR: file_type = eFileTypeDirectory; call_callback = find_directories; break; case DT_BLK: file_type = eFileTypeOther; call_callback = find_other; break; case DT_REG: file_type = eFileTypeRegular; call_callback = find_files; break; case DT_LNK: file_type = eFileTypeSymbolicLink; call_callback = find_other; break; case DT_SOCK: file_type = eFileTypeSocket; call_callback = find_other; break; #if !defined(__OpenBSD__) case DT_WHT: file_type = eFileTypeOther; call_callback = find_other; break; #endif } if (call_callback) { char child_path[PATH_MAX]; const int child_path_len = ::snprintf (child_path, sizeof(child_path), "%s/%s", dir_path, dp->d_name); if (child_path_len < (int)(sizeof(child_path) - 1)) { // Don't resolve the file type or path FileSpec child_path_spec (child_path, false); EnumerateDirectoryResult result = callback (callback_baton, file_type, child_path_spec); switch (result) { case eEnumerateDirectoryResultNext: // Enumerate next entry in the current directory. We just // exit this switch and will continue enumerating the // current directory as we currently are... break; case eEnumerateDirectoryResultEnter: // Recurse into the current entry if it is a directory or symlink, or next if not if (FileSpec::EnumerateDirectory (child_path, find_directories, find_files, find_other, callback, callback_baton) == eEnumerateDirectoryResultQuit) { // The subdirectory returned Quit, which means to // stop all directory enumerations at all levels. if (buf) free (buf); return eEnumerateDirectoryResultQuit; } break; case eEnumerateDirectoryResultExit: // Exit from the current directory at the current level. // Exit from this directory level and tell parent to // keep enumerating. if (buf) free (buf); return eEnumerateDirectoryResultNext; case eEnumerateDirectoryResultQuit: // Stop directory enumerations at any level if (buf) free (buf); return eEnumerateDirectoryResultQuit; } } } } if (buf) { free (buf); } } #endif } // By default when exiting a directory, we tell the parent enumeration // to continue enumerating. return eEnumerateDirectoryResultNext; } FileSpec FileSpec::CopyByAppendingPathComponent (const char *new_path) const { const bool resolve = false; if (m_filename.IsEmpty() && m_directory.IsEmpty()) return FileSpec(new_path,resolve); StreamString stream; if (m_filename.IsEmpty()) stream.Printf("%s/%s",m_directory.GetCString(),new_path); else if (m_directory.IsEmpty()) stream.Printf("%s/%s",m_filename.GetCString(),new_path); else stream.Printf("%s/%s/%s",m_directory.GetCString(), m_filename.GetCString(),new_path); return FileSpec(stream.GetData(),resolve); } FileSpec FileSpec::CopyByRemovingLastPathComponent () const { const bool resolve = false; if (m_filename.IsEmpty() && m_directory.IsEmpty()) return FileSpec("",resolve); if (m_directory.IsEmpty()) return FileSpec("",resolve); if (m_filename.IsEmpty()) { const char* dir_cstr = m_directory.GetCString(); const char* last_slash_ptr = ::strrchr(dir_cstr, '/'); // check for obvious cases before doing the full thing if (!last_slash_ptr) return FileSpec("",resolve); if (last_slash_ptr == dir_cstr) return FileSpec("/",resolve); size_t last_slash_pos = last_slash_ptr - dir_cstr+1; ConstString new_path(dir_cstr,last_slash_pos); return FileSpec(new_path.GetCString(),resolve); } else return FileSpec(m_directory.GetCString(),resolve); } ConstString FileSpec::GetLastPathComponent () const { if (m_filename) return m_filename; if (m_directory) { const char* dir_cstr = m_directory.GetCString(); const char* last_slash_ptr = ::strrchr(dir_cstr, '/'); if (last_slash_ptr == NULL) return m_directory; if (last_slash_ptr == dir_cstr) { if (last_slash_ptr[1] == 0) return ConstString(last_slash_ptr); else return ConstString(last_slash_ptr+1); } if (last_slash_ptr[1] != 0) return ConstString(last_slash_ptr+1); const char* penultimate_slash_ptr = last_slash_ptr; while (*penultimate_slash_ptr) { --penultimate_slash_ptr; if (penultimate_slash_ptr == dir_cstr) break; if (*penultimate_slash_ptr == '/') break; } ConstString result(penultimate_slash_ptr+1,last_slash_ptr-penultimate_slash_ptr); return result; } return ConstString(); } void FileSpec::AppendPathComponent (const char *new_path) { const bool resolve = false; if (m_filename.IsEmpty() && m_directory.IsEmpty()) { SetFile(new_path,resolve); return; } StreamString stream; if (m_filename.IsEmpty()) stream.Printf("%s/%s",m_directory.GetCString(),new_path); else if (m_directory.IsEmpty()) stream.Printf("%s/%s",m_filename.GetCString(),new_path); else stream.Printf("%s/%s/%s",m_directory.GetCString(), m_filename.GetCString(),new_path); SetFile(stream.GetData(), resolve); } void FileSpec::RemoveLastPathComponent () { const bool resolve = false; if (m_filename.IsEmpty() && m_directory.IsEmpty()) { SetFile("",resolve); return; } if (m_directory.IsEmpty()) { SetFile("",resolve); return; } if (m_filename.IsEmpty()) { const char* dir_cstr = m_directory.GetCString(); const char* last_slash_ptr = ::strrchr(dir_cstr, '/'); // check for obvious cases before doing the full thing if (!last_slash_ptr) { SetFile("",resolve); return; } if (last_slash_ptr == dir_cstr) { SetFile("/",resolve); return; } size_t last_slash_pos = last_slash_ptr - dir_cstr+1; ConstString new_path(dir_cstr,last_slash_pos); SetFile(new_path.GetCString(),resolve); } else SetFile(m_directory.GetCString(),resolve); } //------------------------------------------------------------------ /// Returns true if the filespec represents an implementation source /// file (files with a ".c", ".cpp", ".m", ".mm" (many more) /// extension). /// /// @return /// \b true if the filespec represents an implementation source /// file, \b false otherwise. //------------------------------------------------------------------ bool FileSpec::IsSourceImplementationFile () const { ConstString extension (GetFileNameExtension()); if (extension) { static RegularExpression g_source_file_regex ("^(c|m|mm|cpp|c\\+\\+|cxx|cc|cp|s|asm|f|f77|f90|f95|f03|for|ftn|fpp|ada|adb|ads)$", REG_EXTENDED | REG_ICASE); return g_source_file_regex.Execute (extension.GetCString()); } return false; } bool FileSpec::IsRelativeToCurrentWorkingDirectory () const { const char *directory = m_directory.GetCString(); if (directory && directory[0]) { // If the path doesn't start with '/' or '~', return true switch (directory[0]) { case '/': case '~': return false; default: return true; } } else if (m_filename) { // No directory, just a basename, return true return true; } return false; } Index: head/contrib/llvm/tools/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp =================================================================== --- head/contrib/llvm/tools/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp (revision 262120) +++ head/contrib/llvm/tools/lldb/source/Plugins/Disassembler/llvm/DisassemblerLLVMC.cpp (revision 262121) @@ -1,851 +1,860 @@ //===-- DisassemblerLLVMC.cpp -----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "DisassemblerLLVMC.h" #include "llvm-c/Disassembler.h" +#include "llvm/ADT/OwningPtr.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDisassembler.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCRelocationInfo.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MemoryObject.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/ADT/SmallString.h" #include "lldb/Core/Address.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Module.h" #include "lldb/Core/Stream.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Target.h" #include "lldb/Target/StackFrame.h" #include "lldb/Core/RegularExpression.h" using namespace lldb; using namespace lldb_private; class InstructionLLVMC : public lldb_private::Instruction { public: InstructionLLVMC (DisassemblerLLVMC &disasm, const lldb_private::Address &address, AddressClass addr_class) : Instruction (address, addr_class), m_disasm_sp (disasm.shared_from_this()), m_does_branch (eLazyBoolCalculate), m_is_valid (false), m_using_file_addr (false) { } virtual ~InstructionLLVMC () { } virtual bool DoesBranch () { if (m_does_branch == eLazyBoolCalculate) { GetDisassemblerLLVMC().Lock(this, NULL); DataExtractor data; if (m_opcode.GetData(data)) { bool is_alternate_isa; lldb::addr_t pc = m_address.GetFileAddress(); DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr = GetDisasmToUse (is_alternate_isa); const uint8_t *opcode_data = data.GetDataStart(); const size_t opcode_data_len = data.GetByteSize(); llvm::MCInst inst; const size_t inst_size = mc_disasm_ptr->GetMCInst (opcode_data, opcode_data_len, pc, inst); // Be conservative, if we didn't understand the instruction, say it might branch... if (inst_size == 0) m_does_branch = eLazyBoolYes; else { const bool can_branch = mc_disasm_ptr->CanBranch(inst); if (can_branch) m_does_branch = eLazyBoolYes; else m_does_branch = eLazyBoolNo; } } GetDisassemblerLLVMC().Unlock(); } return m_does_branch == eLazyBoolYes; } DisassemblerLLVMC::LLVMCDisassembler * GetDisasmToUse (bool &is_alternate_isa) { is_alternate_isa = false; DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC(); if (llvm_disasm.m_alternate_disasm_ap.get() != NULL) { const AddressClass address_class = GetAddressClass (); if (address_class == eAddressClassCodeAlternateISA) { is_alternate_isa = true; return llvm_disasm.m_alternate_disasm_ap.get(); } } return llvm_disasm.m_disasm_ap.get(); } virtual size_t Decode (const lldb_private::Disassembler &disassembler, const lldb_private::DataExtractor &data, lldb::offset_t data_offset) { // All we have to do is read the opcode which can be easy for some // architectures bool got_op = false; DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC(); const ArchSpec &arch = llvm_disasm.GetArchitecture(); const uint32_t min_op_byte_size = arch.GetMinimumOpcodeByteSize(); const uint32_t max_op_byte_size = arch.GetMaximumOpcodeByteSize(); if (min_op_byte_size == max_op_byte_size) { // Fixed size instructions, just read that amount of data. if (!data.ValidOffsetForDataOfSize(data_offset, min_op_byte_size)) return false; switch (min_op_byte_size) { case 1: m_opcode.SetOpcode8 (data.GetU8 (&data_offset)); got_op = true; break; case 2: m_opcode.SetOpcode16 (data.GetU16 (&data_offset)); got_op = true; break; case 4: m_opcode.SetOpcode32 (data.GetU32 (&data_offset)); got_op = true; break; case 8: m_opcode.SetOpcode64 (data.GetU64 (&data_offset)); got_op = true; break; default: m_opcode.SetOpcodeBytes(data.PeekData(data_offset, min_op_byte_size), min_op_byte_size); got_op = true; break; } } if (!got_op) { bool is_alternate_isa = false; DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr = GetDisasmToUse (is_alternate_isa); const llvm::Triple::ArchType machine = arch.GetMachine(); if (machine == llvm::Triple::arm || machine == llvm::Triple::thumb) { if (machine == llvm::Triple::thumb || is_alternate_isa) { uint32_t thumb_opcode = data.GetU16(&data_offset); if ((thumb_opcode & 0xe000) != 0xe000 || ((thumb_opcode & 0x1800u) == 0)) { m_opcode.SetOpcode16 (thumb_opcode); m_is_valid = true; } else { thumb_opcode <<= 16; thumb_opcode |= data.GetU16(&data_offset); m_opcode.SetOpcode16_2 (thumb_opcode); m_is_valid = true; } } else { m_opcode.SetOpcode32 (data.GetU32(&data_offset)); m_is_valid = true; } } else { // The opcode isn't evenly sized, so we need to actually use the llvm // disassembler to parse it and get the size. uint8_t *opcode_data = const_cast(data.PeekData (data_offset, 1)); const size_t opcode_data_len = data.BytesLeft(data_offset); const addr_t pc = m_address.GetFileAddress(); llvm::MCInst inst; llvm_disasm.Lock(this, NULL); const size_t inst_size = mc_disasm_ptr->GetMCInst(opcode_data, opcode_data_len, pc, inst); llvm_disasm.Unlock(); if (inst_size == 0) m_opcode.Clear(); else { m_opcode.SetOpcodeBytes(opcode_data, inst_size); m_is_valid = true; } } } return m_opcode.GetByteSize(); } void AppendComment (std::string &description) { if (m_comment.empty()) m_comment.swap (description); else { m_comment.append(", "); m_comment.append(description); } } virtual void CalculateMnemonicOperandsAndComment (const lldb_private::ExecutionContext *exe_ctx) { DataExtractor data; const AddressClass address_class = GetAddressClass (); if (m_opcode.GetData(data)) { char out_string[512]; DisassemblerLLVMC &llvm_disasm = GetDisassemblerLLVMC(); DisassemblerLLVMC::LLVMCDisassembler *mc_disasm_ptr; if (address_class == eAddressClassCodeAlternateISA) mc_disasm_ptr = llvm_disasm.m_alternate_disasm_ap.get(); else mc_disasm_ptr = llvm_disasm.m_disasm_ap.get(); lldb::addr_t pc = m_address.GetFileAddress(); m_using_file_addr = true; const bool data_from_file = GetDisassemblerLLVMC().m_data_from_file; bool use_hex_immediates = true; Disassembler::HexImmediateStyle hex_style = Disassembler::eHexStyleC; if (exe_ctx) { Target *target = exe_ctx->GetTargetPtr(); if (target) { use_hex_immediates = target->GetUseHexImmediates(); hex_style = target->GetHexImmediateStyle(); if (!data_from_file) { const lldb::addr_t load_addr = m_address.GetLoadAddress(target); if (load_addr != LLDB_INVALID_ADDRESS) { pc = load_addr; m_using_file_addr = false; } } } } llvm_disasm.Lock(this, exe_ctx); const uint8_t *opcode_data = data.GetDataStart(); const size_t opcode_data_len = data.GetByteSize(); llvm::MCInst inst; size_t inst_size = mc_disasm_ptr->GetMCInst (opcode_data, opcode_data_len, pc, inst); if (inst_size > 0) { mc_disasm_ptr->SetStyle(use_hex_immediates, hex_style); mc_disasm_ptr->PrintMCInst(inst, out_string, sizeof(out_string)); } llvm_disasm.Unlock(); if (inst_size == 0) { m_comment.assign ("unknown opcode"); inst_size = m_opcode.GetByteSize(); StreamString mnemonic_strm; lldb::offset_t offset = 0; switch (inst_size) { case 1: { const uint8_t uval8 = data.GetU8 (&offset); m_opcode.SetOpcode8 (uval8); m_opcode_name.assign (".byte"); mnemonic_strm.Printf("0x%2.2x", uval8); } break; case 2: { const uint16_t uval16 = data.GetU16(&offset); m_opcode.SetOpcode16(uval16); m_opcode_name.assign (".short"); mnemonic_strm.Printf("0x%4.4x", uval16); } break; case 4: { const uint32_t uval32 = data.GetU32(&offset); m_opcode.SetOpcode32(uval32); m_opcode_name.assign (".long"); mnemonic_strm.Printf("0x%8.8x", uval32); } break; case 8: { const uint64_t uval64 = data.GetU64(&offset); m_opcode.SetOpcode64(uval64); m_opcode_name.assign (".quad"); mnemonic_strm.Printf("0x%16.16" PRIx64, uval64); } break; default: if (inst_size == 0) return; else { const uint8_t *bytes = data.PeekData(offset, inst_size); if (bytes == NULL) return; m_opcode_name.assign (".byte"); m_opcode.SetOpcodeBytes(bytes, inst_size); mnemonic_strm.Printf("0x%2.2x", bytes[0]); for (uint32_t i=1; iCanBranch(inst); if (can_branch) m_does_branch = eLazyBoolYes; else m_does_branch = eLazyBoolNo; } } static RegularExpression s_regex("[ \t]*([^ ^\t]+)[ \t]*([^ ^\t].*)?", REG_EXTENDED); RegularExpression::Match matches(3); if (s_regex.Execute(out_string, &matches)) { matches.GetMatchAtIndex(out_string, 1, m_opcode_name); matches.GetMatchAtIndex(out_string, 2, m_mnemonics); } } } bool IsValid () const { return m_is_valid; } bool UsingFileAddress() const { return m_using_file_addr; } size_t GetByteSize () const { return m_opcode.GetByteSize(); } DisassemblerLLVMC & GetDisassemblerLLVMC () { return *(DisassemblerLLVMC *)m_disasm_sp.get(); } protected: DisassemblerSP m_disasm_sp; // for ownership LazyBool m_does_branch; bool m_is_valid; bool m_using_file_addr; }; DisassemblerLLVMC::LLVMCDisassembler::LLVMCDisassembler (const char *triple, unsigned flavor, DisassemblerLLVMC &owner): m_is_valid(true) { std::string Error; const llvm::Target *curr_target = llvm::TargetRegistry::lookupTarget(triple, Error); if (!curr_target) { m_is_valid = false; return; } m_instr_info_ap.reset(curr_target->createMCInstrInfo()); m_reg_info_ap.reset (curr_target->createMCRegInfo(triple)); std::string features_str; m_subtarget_info_ap.reset(curr_target->createMCSubtargetInfo(triple, "", features_str)); - m_asm_info_ap.reset(curr_target->createMCAsmInfo(triple)); - + m_asm_info_ap.reset(curr_target->createMCAsmInfo(*curr_target->createMCRegInfo(triple), triple)); + if (m_instr_info_ap.get() == NULL || m_reg_info_ap.get() == NULL || m_subtarget_info_ap.get() == NULL || m_asm_info_ap.get() == NULL) { m_is_valid = false; return; } - m_context_ap.reset(new llvm::MCContext(*m_asm_info_ap.get(), *(m_reg_info_ap.get()), 0)); + m_context_ap.reset(new llvm::MCContext(m_asm_info_ap.get(), m_reg_info_ap.get(), 0)); m_disasm_ap.reset(curr_target->createMCDisassembler(*m_subtarget_info_ap.get())); - if (m_disasm_ap.get()) + if (m_disasm_ap.get() && m_context_ap.get()) { + llvm::OwningPtr RelInfo(curr_target->createMCRelocationInfo(triple, *m_context_ap.get())); + if (!RelInfo) + { + m_is_valid = false; + return; + } m_disasm_ap->setupForSymbolicDisassembly(NULL, - DisassemblerLLVMC::SymbolLookupCallback, - (void *) &owner, - m_context_ap.get()); + DisassemblerLLVMC::SymbolLookupCallback, + (void *) &owner, + m_context_ap.get(), + RelInfo); unsigned asm_printer_variant; if (flavor == ~0U) asm_printer_variant = m_asm_info_ap->getAssemblerDialect(); else { asm_printer_variant = flavor; } m_instr_printer_ap.reset(curr_target->createMCInstPrinter(asm_printer_variant, *m_asm_info_ap.get(), *m_instr_info_ap.get(), *m_reg_info_ap.get(), *m_subtarget_info_ap.get())); if (m_instr_printer_ap.get() == NULL) { m_disasm_ap.reset(); m_is_valid = false; } } else m_is_valid = false; } DisassemblerLLVMC::LLVMCDisassembler::~LLVMCDisassembler() { } namespace { // This is the memory object we use in GetInstruction. class LLDBDisasmMemoryObject : public llvm::MemoryObject { const uint8_t *m_bytes; uint64_t m_size; uint64_t m_base_PC; public: LLDBDisasmMemoryObject(const uint8_t *bytes, uint64_t size, uint64_t basePC) : m_bytes(bytes), m_size(size), m_base_PC(basePC) {} uint64_t getBase() const { return m_base_PC; } uint64_t getExtent() const { return m_size; } int readByte(uint64_t addr, uint8_t *byte) const { if (addr - m_base_PC >= m_size) return -1; *byte = m_bytes[addr - m_base_PC]; return 0; } }; } // End Anonymous Namespace uint64_t DisassemblerLLVMC::LLVMCDisassembler::GetMCInst (const uint8_t *opcode_data, size_t opcode_data_len, lldb::addr_t pc, llvm::MCInst &mc_inst) { LLDBDisasmMemoryObject memory_object (opcode_data, opcode_data_len, pc); llvm::MCDisassembler::DecodeStatus status; uint64_t new_inst_size; status = m_disasm_ap->getInstruction(mc_inst, new_inst_size, memory_object, pc, llvm::nulls(), llvm::nulls()); if (status == llvm::MCDisassembler::Success) return new_inst_size; else return 0; } uint64_t DisassemblerLLVMC::LLVMCDisassembler::PrintMCInst (llvm::MCInst &mc_inst, char *dst, size_t dst_len) { llvm::StringRef unused_annotations; llvm::SmallString<64> inst_string; llvm::raw_svector_ostream inst_stream(inst_string); m_instr_printer_ap->printInst (&mc_inst, inst_stream, unused_annotations); inst_stream.flush(); const size_t output_size = std::min(dst_len - 1, inst_string.size()); std::memcpy(dst, inst_string.data(), output_size); dst[output_size] = '\0'; return output_size; } void DisassemblerLLVMC::LLVMCDisassembler::SetStyle (bool use_hex_immed, HexImmediateStyle hex_style) { m_instr_printer_ap->setPrintImmHex(use_hex_immed); switch(hex_style) { case eHexStyleC: m_instr_printer_ap->setPrintImmHex(llvm::HexStyle::C); break; case eHexStyleAsm: m_instr_printer_ap->setPrintImmHex(llvm::HexStyle::Asm); break; } } bool DisassemblerLLVMC::LLVMCDisassembler::CanBranch (llvm::MCInst &mc_inst) { return m_instr_info_ap->get(mc_inst.getOpcode()).mayAffectControlFlow(mc_inst, *m_reg_info_ap.get()); } bool DisassemblerLLVMC::FlavorValidForArchSpec (const lldb_private::ArchSpec &arch, const char *flavor) { llvm::Triple triple = arch.GetTriple(); if (flavor == NULL || strcmp (flavor, "default") == 0) return true; if (triple.getArch() == llvm::Triple::x86 || triple.getArch() == llvm::Triple::x86_64) { if (strcmp (flavor, "intel") == 0 || strcmp (flavor, "att") == 0) return true; else return false; } else return false; } Disassembler * DisassemblerLLVMC::CreateInstance (const ArchSpec &arch, const char *flavor) { if (arch.GetTriple().getArch() != llvm::Triple::UnknownArch) { std::unique_ptr disasm_ap (new DisassemblerLLVMC(arch, flavor)); if (disasm_ap.get() && disasm_ap->IsValid()) return disasm_ap.release(); } return NULL; } DisassemblerLLVMC::DisassemblerLLVMC (const ArchSpec &arch, const char *flavor_string) : Disassembler(arch, flavor_string), m_exe_ctx (NULL), m_inst (NULL), m_data_from_file (false) { if (!FlavorValidForArchSpec (arch, m_flavor.c_str())) { m_flavor.assign("default"); } const char *triple = arch.GetTriple().getTriple().c_str(); unsigned flavor = ~0U; // So far the only supported flavor is "intel" on x86. The base class will set this // correctly coming in. if (arch.GetTriple().getArch() == llvm::Triple::x86 || arch.GetTriple().getArch() == llvm::Triple::x86_64) { if (m_flavor == "intel") { flavor = 1; } else if (m_flavor == "att") { flavor = 0; } } ArchSpec thumb_arch(arch); if (arch.GetTriple().getArch() == llvm::Triple::arm) { std::string thumb_arch_name (thumb_arch.GetTriple().getArchName().str()); // Replace "arm" with "thumb" so we get all thumb variants correct if (thumb_arch_name.size() > 3) { thumb_arch_name.erase(0,3); thumb_arch_name.insert(0, "thumb"); } else { thumb_arch_name = "thumbv7"; } thumb_arch.GetTriple().setArchName(llvm::StringRef(thumb_arch_name.c_str())); } // Cortex-M3 devices (e.g. armv7m) can only execute thumb (T2) instructions, // so hardcode the primary disassembler to thumb mode. Same for Cortex-M4 (armv7em). // // Handle the Cortex-M0 (armv6m) the same; the ISA is a subset of the T and T32 // instructions defined in ARMv7-A. if (arch.GetTriple().getArch() == llvm::Triple::arm && (arch.GetCore() == ArchSpec::Core::eCore_arm_armv7m || arch.GetCore() == ArchSpec::Core::eCore_arm_armv7em || arch.GetCore() == ArchSpec::Core::eCore_arm_armv6m)) { triple = thumb_arch.GetTriple().getTriple().c_str(); } m_disasm_ap.reset (new LLVMCDisassembler(triple, flavor, *this)); if (!m_disasm_ap->IsValid()) { // We use m_disasm_ap.get() to tell whether we are valid or not, so if this isn't good for some reason, // we reset it, and then we won't be valid and FindPlugin will fail and we won't get used. m_disasm_ap.reset(); } // For arm CPUs that can execute arm or thumb instructions, also create a thumb instruction disassembler. if (arch.GetTriple().getArch() == llvm::Triple::arm) { std::string thumb_triple(thumb_arch.GetTriple().getTriple()); m_alternate_disasm_ap.reset(new LLVMCDisassembler(thumb_triple.c_str(), flavor, *this)); if (!m_alternate_disasm_ap->IsValid()) { m_disasm_ap.reset(); m_alternate_disasm_ap.reset(); } } } DisassemblerLLVMC::~DisassemblerLLVMC() { } size_t DisassemblerLLVMC::DecodeInstructions (const Address &base_addr, const DataExtractor& data, lldb::offset_t data_offset, size_t num_instructions, bool append, bool data_from_file) { if (!append) m_instruction_list.Clear(); if (!IsValid()) return 0; m_data_from_file = data_from_file; uint32_t data_cursor = data_offset; const size_t data_byte_size = data.GetByteSize(); uint32_t instructions_parsed = 0; Address inst_addr(base_addr); while (data_cursor < data_byte_size && instructions_parsed < num_instructions) { AddressClass address_class = eAddressClassCode; if (m_alternate_disasm_ap.get() != NULL) address_class = inst_addr.GetAddressClass (); InstructionSP inst_sp(new InstructionLLVMC(*this, inst_addr, address_class)); if (!inst_sp) break; uint32_t inst_size = inst_sp->Decode(*this, data, data_cursor); if (inst_size == 0) break; m_instruction_list.Append(inst_sp); data_cursor += inst_size; inst_addr.Slide(inst_size); instructions_parsed++; } return data_cursor - data_offset; } void DisassemblerLLVMC::Initialize() { PluginManager::RegisterPlugin (GetPluginNameStatic(), "Disassembler that uses LLVM MC to disassemble i386, x86_64 and ARM.", CreateInstance); llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmParsers(); llvm::InitializeAllDisassemblers(); } void DisassemblerLLVMC::Terminate() { PluginManager::UnregisterPlugin (CreateInstance); } ConstString DisassemblerLLVMC::GetPluginNameStatic() { static ConstString g_name("llvm-mc"); return g_name; } int DisassemblerLLVMC::OpInfoCallback (void *disassembler, uint64_t pc, uint64_t offset, uint64_t size, int tag_type, void *tag_bug) { return static_cast(disassembler)->OpInfo (pc, offset, size, tag_type, tag_bug); } const char *DisassemblerLLVMC::SymbolLookupCallback (void *disassembler, uint64_t value, uint64_t *type, uint64_t pc, const char **name) { return static_cast(disassembler)->SymbolLookup(value, type, pc, name); } int DisassemblerLLVMC::OpInfo (uint64_t PC, uint64_t Offset, uint64_t Size, int tag_type, void *tag_bug) { switch (tag_type) { default: break; case 1: memset (tag_bug, 0, sizeof(::LLVMOpInfo1)); break; } return 0; } const char *DisassemblerLLVMC::SymbolLookup (uint64_t value, uint64_t *type_ptr, uint64_t pc, const char **name) { if (*type_ptr) { if (m_exe_ctx && m_inst) { //std::string remove_this_prior_to_checkin; Target *target = m_exe_ctx ? m_exe_ctx->GetTargetPtr() : NULL; Address value_so_addr; if (m_inst->UsingFileAddress()) { ModuleSP module_sp(m_inst->GetAddress().GetModule()); if (module_sp) module_sp->ResolveFileAddress(value, value_so_addr); } else if (target && !target->GetSectionLoadList().IsEmpty()) { target->GetSectionLoadList().ResolveLoadAddress(value, value_so_addr); } if (value_so_addr.IsValid() && value_so_addr.GetSection()) { StreamString ss; value_so_addr.Dump (&ss, target, Address::DumpStyleResolvedDescriptionNoModule, Address::DumpStyleSectionNameOffset); if (!ss.GetString().empty()) { m_inst->AppendComment(ss.GetString()); } } } } *type_ptr = LLVMDisassembler_ReferenceType_InOut_None; *name = NULL; return NULL; } //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ ConstString DisassemblerLLVMC::GetPluginName() { return GetPluginNameStatic(); } uint32_t DisassemblerLLVMC::GetPluginVersion() { return 1; } Index: head/contrib/llvm/tools/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp =================================================================== --- head/contrib/llvm/tools/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp (revision 262120) +++ head/contrib/llvm/tools/lldb/source/Plugins/Instruction/ARM/EmulateInstructionARM.cpp (revision 262121) @@ -1,13625 +1,13625 @@ //===-- EmulateInstructionARM.cpp -------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include "EmulateInstructionARM.h" #include "EmulationStateARM.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Address.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/Stream.h" #include "lldb/Interpreter/OptionValueArray.h" #include "lldb/Interpreter/OptionValueDictionary.h" #include "lldb/Symbol/UnwindPlan.h" #include "Plugins/Process/Utility/ARMDefines.h" #include "Plugins/Process/Utility/ARMUtils.h" #include "Utility/ARM_DWARF_Registers.h" #include "llvm/Support/MathExtras.h" // for SignExtend32 template function - // and CountTrailingZeros_32 function + // and countTrailingZeros function using namespace lldb; using namespace lldb_private; // Convenient macro definitions. #define APSR_C Bit32(m_opcode_cpsr, CPSR_C_POS) #define APSR_V Bit32(m_opcode_cpsr, CPSR_V_POS) #define AlignPC(pc_val) (pc_val & 0xFFFFFFFC) //---------------------------------------------------------------------- // // ITSession implementation // //---------------------------------------------------------------------- // A8.6.50 // Valid return values are {1, 2, 3, 4}, with 0 signifying an error condition. static uint32_t CountITSize (uint32_t ITMask) { // First count the trailing zeros of the IT mask. - uint32_t TZ = llvm::CountTrailingZeros_32(ITMask); + uint32_t TZ = llvm::countTrailingZeros(ITMask); if (TZ > 3) { #ifdef LLDB_CONFIGURATION_DEBUG printf("Encoding error: IT Mask '0000'\n"); #endif return 0; } return (4 - TZ); } // Init ITState. Note that at least one bit is always 1 in mask. bool ITSession::InitIT(uint32_t bits7_0) { ITCounter = CountITSize(Bits32(bits7_0, 3, 0)); if (ITCounter == 0) return false; // A8.6.50 IT unsigned short FirstCond = Bits32(bits7_0, 7, 4); if (FirstCond == 0xF) { #ifdef LLDB_CONFIGURATION_DEBUG printf("Encoding error: IT FirstCond '1111'\n"); #endif return false; } if (FirstCond == 0xE && ITCounter != 1) { #ifdef LLDB_CONFIGURATION_DEBUG printf("Encoding error: IT FirstCond '1110' && Mask != '1000'\n"); #endif return false; } ITState = bits7_0; return true; } // Update ITState if necessary. void ITSession::ITAdvance() { //assert(ITCounter); --ITCounter; if (ITCounter == 0) ITState = 0; else { unsigned short NewITState4_0 = Bits32(ITState, 4, 0) << 1; SetBits32(ITState, 4, 0, NewITState4_0); } } // Return true if we're inside an IT Block. bool ITSession::InITBlock() { return ITCounter != 0; } // Return true if we're the last instruction inside an IT Block. bool ITSession::LastInITBlock() { return ITCounter == 1; } // Get condition bits for the current thumb instruction. uint32_t ITSession::GetCond() { if (InITBlock()) return Bits32(ITState, 7, 4); else return COND_AL; } // ARM constants used during decoding #define REG_RD 0 #define LDM_REGLIST 1 #define SP_REG 13 #define LR_REG 14 #define PC_REG 15 #define PC_REGLIST_BIT 0x8000 #define ARMv4 (1u << 0) #define ARMv4T (1u << 1) #define ARMv5T (1u << 2) #define ARMv5TE (1u << 3) #define ARMv5TEJ (1u << 4) #define ARMv6 (1u << 5) #define ARMv6K (1u << 6) #define ARMv6T2 (1u << 7) #define ARMv7 (1u << 8) #define ARMv7S (1u << 9) #define ARMv8 (1u << 10) #define ARMvAll (0xffffffffu) #define ARMV4T_ABOVE (ARMv4T|ARMv5T|ARMv5TE|ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv7S|ARMv8) #define ARMV5_ABOVE (ARMv5T|ARMv5TE|ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv7S|ARMv8) #define ARMV5TE_ABOVE (ARMv5TE|ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv7S|ARMv8) #define ARMV5J_ABOVE (ARMv5TEJ|ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv7S|ARMv8) #define ARMV6_ABOVE (ARMv6|ARMv6K|ARMv6T2|ARMv7|ARMv7S|ARMv8) #define ARMV6T2_ABOVE (ARMv6T2|ARMv7|ARMv7S|ARMv8) #define ARMV7_ABOVE (ARMv7|ARMv7S|ARMv8) #define No_VFP 0 #define VFPv1 (1u << 1) #define VFPv2 (1u << 2) #define VFPv3 (1u << 3) #define AdvancedSIMD (1u << 4) #define VFPv1_ABOVE (VFPv1 | VFPv2 | VFPv3 | AdvancedSIMD) #define VFPv2_ABOVE (VFPv2 | VFPv3 | AdvancedSIMD) #define VFPv2v3 (VFPv2 | VFPv3) //---------------------------------------------------------------------- // // EmulateInstructionARM implementation // //---------------------------------------------------------------------- void EmulateInstructionARM::Initialize () { PluginManager::RegisterPlugin (GetPluginNameStatic (), GetPluginDescriptionStatic (), CreateInstance); } void EmulateInstructionARM::Terminate () { PluginManager::UnregisterPlugin (CreateInstance); } ConstString EmulateInstructionARM::GetPluginNameStatic () { static ConstString g_name("arm"); return g_name; } const char * EmulateInstructionARM::GetPluginDescriptionStatic () { return "Emulate instructions for the ARM architecture."; } EmulateInstruction * EmulateInstructionARM::CreateInstance (const ArchSpec &arch, InstructionType inst_type) { if (EmulateInstructionARM::SupportsEmulatingInstructionsOfTypeStatic(inst_type)) { if (arch.GetTriple().getArch() == llvm::Triple::arm) { std::unique_ptr emulate_insn_ap (new EmulateInstructionARM (arch)); if (emulate_insn_ap.get()) return emulate_insn_ap.release(); } else if (arch.GetTriple().getArch() == llvm::Triple::thumb) { std::unique_ptr emulate_insn_ap (new EmulateInstructionARM (arch)); if (emulate_insn_ap.get()) return emulate_insn_ap.release(); } } return NULL; } bool EmulateInstructionARM::SetTargetTriple (const ArchSpec &arch) { if (arch.GetTriple().getArch () == llvm::Triple::arm) return true; else if (arch.GetTriple().getArch () == llvm::Triple::thumb) return true; return false; } // Write "bits (32) UNKNOWN" to memory address "address". Helper function for many ARM instructions. bool EmulateInstructionARM::WriteBits32UnknownToMemory (addr_t address) { EmulateInstruction::Context context; context.type = EmulateInstruction::eContextWriteMemoryRandomBits; context.SetNoArgs (); uint32_t random_data = rand (); const uint32_t addr_byte_size = GetAddressByteSize(); if (!MemAWrite (context, address, random_data, addr_byte_size)) return false; return true; } // Write "bits (32) UNKNOWN" to register n. Helper function for many ARM instructions. bool EmulateInstructionARM::WriteBits32Unknown (int n) { EmulateInstruction::Context context; context.type = EmulateInstruction::eContextWriteRegisterRandomBits; context.SetNoArgs (); bool success; uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data)) return false; return true; } bool EmulateInstructionARM::GetRegisterInfo (uint32_t reg_kind, uint32_t reg_num, RegisterInfo ®_info) { if (reg_kind == eRegisterKindGeneric) { switch (reg_num) { case LLDB_REGNUM_GENERIC_PC: reg_kind = eRegisterKindDWARF; reg_num = dwarf_pc; break; case LLDB_REGNUM_GENERIC_SP: reg_kind = eRegisterKindDWARF; reg_num = dwarf_sp; break; case LLDB_REGNUM_GENERIC_FP: reg_kind = eRegisterKindDWARF; reg_num = dwarf_r7; break; case LLDB_REGNUM_GENERIC_RA: reg_kind = eRegisterKindDWARF; reg_num = dwarf_lr; break; case LLDB_REGNUM_GENERIC_FLAGS: reg_kind = eRegisterKindDWARF; reg_num = dwarf_cpsr; break; default: return false; } } if (reg_kind == eRegisterKindDWARF) return GetARMDWARFRegisterInfo(reg_num, reg_info); return false; } uint32_t EmulateInstructionARM::GetFramePointerRegisterNumber () const { if (m_opcode_mode == eModeThumb) { switch (m_arch.GetTriple().getOS()) { case llvm::Triple::Darwin: case llvm::Triple::MacOSX: case llvm::Triple::IOS: return 7; default: break; } } return 11; } uint32_t EmulateInstructionARM::GetFramePointerDWARFRegisterNumber () const { if (m_opcode_mode == eModeThumb) { switch (m_arch.GetTriple().getOS()) { case llvm::Triple::Darwin: case llvm::Triple::MacOSX: case llvm::Triple::IOS: return dwarf_r7; default: break; } } return dwarf_r11; } // Push Multiple Registers stores multiple registers to the stack, storing to // consecutive memory locations ending just below the address in SP, and updates // SP to point to the start of the stored data. bool EmulateInstructionARM::EmulatePUSH (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); NullCheckIfThumbEE(13); address = SP - 4*BitCount(registers); for (i = 0 to 14) { if (registers == '1') { if i == 13 && i != LowestSetBit(registers) // Only possible for encoding A1 MemA[address,4] = bits(32) UNKNOWN; else MemA[address,4] = R[i]; address = address + 4; } } if (registers<15> == '1') // Only possible for encoding A1 or A2 MemA[address,4] = PCStoreValue(); SP = SP - 4*BitCount(registers); } #endif bool conditional = false; bool success = false; if (ConditionPassed(opcode, &conditional)) { const uint32_t addr_byte_size = GetAddressByteSize(); const addr_t sp = ReadCoreReg (SP_REG, &success); if (!success) return false; uint32_t registers = 0; uint32_t Rt; // the source register switch (encoding) { case eEncodingT1: registers = Bits32(opcode, 7, 0); // The M bit represents LR. if (Bit32(opcode, 8)) registers |= (1u << 14); // if BitCount(registers) < 1 then UNPREDICTABLE; if (BitCount(registers) < 1) return false; break; case eEncodingT2: // Ignore bits 15 & 13. registers = Bits32(opcode, 15, 0) & ~0xa000; // if BitCount(registers) < 2 then UNPREDICTABLE; if (BitCount(registers) < 2) return false; break; case eEncodingT3: Rt = Bits32(opcode, 15, 12); // if BadReg(t) then UNPREDICTABLE; if (BadReg(Rt)) return false; registers = (1u << Rt); break; case eEncodingA1: registers = Bits32(opcode, 15, 0); // Instead of return false, let's handle the following case as well, // which amounts to pushing one reg onto the full descending stacks. // if BitCount(register_list) < 2 then SEE STMDB / STMFD; break; case eEncodingA2: Rt = Bits32(opcode, 15, 12); // if t == 13 then UNPREDICTABLE; if (Rt == dwarf_sp) return false; registers = (1u << Rt); break; default: return false; } addr_t sp_offset = addr_byte_size * BitCount (registers); addr_t addr = sp - sp_offset; uint32_t i; EmulateInstruction::Context context; if (conditional) context.type = EmulateInstruction::eContextRegisterStore; else context.type = EmulateInstruction::eContextPushRegisterOnStack; RegisterInfo reg_info; RegisterInfo sp_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); for (i=0; i<15; ++i) { if (BitIsSet (registers, i)) { GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, reg_info); context.SetRegisterToRegisterPlusOffset (reg_info, sp_reg, addr - sp); uint32_t reg_value = ReadCoreReg(i, &success); if (!success) return false; if (!MemAWrite (context, addr, reg_value, addr_byte_size)) return false; addr += addr_byte_size; } } if (BitIsSet (registers, 15)) { GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, reg_info); context.SetRegisterToRegisterPlusOffset (reg_info, sp_reg, addr - sp); const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; if (!MemAWrite (context, addr, pc, addr_byte_size)) return false; } context.type = EmulateInstruction::eContextAdjustStackPointer; context.SetImmediateSigned (-sp_offset); if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp - sp_offset)) return false; } return true; } // Pop Multiple Registers loads multiple registers from the stack, loading from // consecutive memory locations staring at the address in SP, and updates // SP to point just above the loaded data. bool EmulateInstructionARM::EmulatePOP (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); NullCheckIfThumbEE(13); address = SP; for i = 0 to 14 if registers == '1' then R[i] = if UnalignedAllowed then MemU[address,4] else MemA[address,4]; address = address + 4; if registers<15> == '1' then if UnalignedAllowed then LoadWritePC(MemU[address,4]); else LoadWritePC(MemA[address,4]); if registers<13> == '0' then SP = SP + 4*BitCount(registers); if registers<13> == '1' then SP = bits(32) UNKNOWN; } #endif bool success = false; bool conditional = false; if (ConditionPassed(opcode, &conditional)) { const uint32_t addr_byte_size = GetAddressByteSize(); const addr_t sp = ReadCoreReg (SP_REG, &success); if (!success) return false; uint32_t registers = 0; uint32_t Rt; // the destination register switch (encoding) { case eEncodingT1: registers = Bits32(opcode, 7, 0); // The P bit represents PC. if (Bit32(opcode, 8)) registers |= (1u << 15); // if BitCount(registers) < 1 then UNPREDICTABLE; if (BitCount(registers) < 1) return false; break; case eEncodingT2: // Ignore bit 13. registers = Bits32(opcode, 15, 0) & ~0x2000; // if BitCount(registers) < 2 || (P == '1' && M == '1') then UNPREDICTABLE; if (BitCount(registers) < 2 || (Bit32(opcode, 15) && Bit32(opcode, 14))) return false; // if registers<15> == '1' && InITBlock() && !LastInITBlock() then UNPREDICTABLE; if (BitIsSet(registers, 15) && InITBlock() && !LastInITBlock()) return false; break; case eEncodingT3: Rt = Bits32(opcode, 15, 12); // if t == 13 || (t == 15 && InITBlock() && !LastInITBlock()) then UNPREDICTABLE; if (Rt == 13) return false; if (Rt == 15 && InITBlock() && !LastInITBlock()) return false; registers = (1u << Rt); break; case eEncodingA1: registers = Bits32(opcode, 15, 0); // Instead of return false, let's handle the following case as well, // which amounts to popping one reg from the full descending stacks. // if BitCount(register_list) < 2 then SEE LDM / LDMIA / LDMFD; // if registers<13> == '1' && ArchVersion() >= 7 then UNPREDICTABLE; if (BitIsSet(opcode, 13) && ArchVersion() >= ARMv7) return false; break; case eEncodingA2: Rt = Bits32(opcode, 15, 12); // if t == 13 then UNPREDICTABLE; if (Rt == dwarf_sp) return false; registers = (1u << Rt); break; default: return false; } addr_t sp_offset = addr_byte_size * BitCount (registers); addr_t addr = sp; uint32_t i, data; EmulateInstruction::Context context; if (conditional) context.type = EmulateInstruction::eContextRegisterLoad; else context.type = EmulateInstruction::eContextPopRegisterOffStack; RegisterInfo sp_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); for (i=0; i<15; ++i) { if (BitIsSet (registers, i)) { context.SetRegisterPlusOffset (sp_reg, addr - sp); data = MemARead(context, addr, 4, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned(context, eRegisterKindDWARF, dwarf_r0 + i, data)) return false; addr += addr_byte_size; } } if (BitIsSet (registers, 15)) { context.SetRegisterPlusOffset (sp_reg, addr - sp); data = MemARead(context, addr, 4, 0, &success); if (!success) return false; // In ARMv5T and above, this is an interworking branch. if (!LoadWritePC(context, data)) return false; //addr += addr_byte_size; } context.type = EmulateInstruction::eContextAdjustStackPointer; context.SetImmediateSigned (sp_offset); if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, sp + sp_offset)) return false; } return true; } // Set r7 or ip to point to saved value residing within the stack. // ADD (SP plus immediate) bool EmulateInstructionARM::EmulateADDRdSPImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(SP, imm32, '0'); if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; } #endif bool success = false; if (ConditionPassed(opcode)) { const addr_t sp = ReadCoreReg (SP_REG, &success); if (!success) return false; uint32_t Rd; // the destination register uint32_t imm32; switch (encoding) { case eEncodingT1: Rd = 7; imm32 = Bits32(opcode, 7, 0) << 2; // imm32 = ZeroExtend(imm8:'00', 32) break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) break; default: return false; } addr_t sp_offset = imm32; addr_t addr = sp + sp_offset; // a pointer to the stack area EmulateInstruction::Context context; context.type = eContextSetFramePointer; RegisterInfo sp_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); context.SetRegisterPlusOffset (sp_reg, sp_offset); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + Rd, addr)) return false; } return true; } // Set r7 or ip to the current stack pointer. // MOV (register) bool EmulateInstructionARM::EmulateMOVRdSP (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); result = R[m]; if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); // APSR.C unchanged // APSR.V unchanged } #endif bool success = false; if (ConditionPassed(opcode)) { const addr_t sp = ReadCoreReg (SP_REG, &success); if (!success) return false; uint32_t Rd; // the destination register switch (encoding) { case eEncodingT1: Rd = 7; break; case eEncodingA1: Rd = 12; break; default: return false; } EmulateInstruction::Context context; if (Rd == GetFramePointerRegisterNumber()) context.type = EmulateInstruction::eContextSetFramePointer; else context.type = EmulateInstruction::eContextRegisterPlusOffset; RegisterInfo sp_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); context.SetRegisterPlusOffset (sp_reg, 0); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + Rd, sp)) return false; } return true; } // Move from high register (r8-r15) to low register (r0-r7). // MOV (register) bool EmulateInstructionARM::EmulateMOVLowHigh (const uint32_t opcode, const ARMEncoding encoding) { return EmulateMOVRdRm (opcode, encoding); } // Move from register to register. // MOV (register) bool EmulateInstructionARM::EmulateMOVRdRm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); result = R[m]; if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); // APSR.C unchanged // APSR.V unchanged } #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rm; // the source register uint32_t Rd; // the destination register bool setflags; switch (encoding) { case eEncodingT1: Rd = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0); Rm = Bits32(opcode, 6, 3); setflags = false; if (Rd == 15 && InITBlock() && !LastInITBlock()) return false; break; case eEncodingT2: Rd = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = true; if (InITBlock()) return false; break; case eEncodingT3: Rd = Bits32(opcode, 11, 8); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); // if setflags && (BadReg(d) || BadReg(m)) then UNPREDICTABLE; if (setflags && (BadReg(Rd) || BadReg(Rm))) return false; // if !setflags && (d == 15 || m == 15 || (d == 13 && m == 13)) then UNPREDICTABLE; if (!setflags && (Rd == 15 || Rm == 15 || (Rd == 13 && Rm == 13))) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } uint32_t result = ReadCoreReg(Rm, &success); if (!success) return false; // The context specifies that Rm is to be moved into Rd. EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterLoad; RegisterInfo dwarf_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg); context.SetRegister (dwarf_reg); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags)) return false; } return true; } // Move (immediate) writes an immediate value to the destination register. It // can optionally update the condition flags based on the value. // MOV (immediate) bool EmulateInstructionARM::EmulateMOVRdImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); result = imm32; if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged } #endif if (ConditionPassed(opcode)) { uint32_t Rd; // the destination register uint32_t imm32; // the immediate value to be written to Rd uint32_t carry = 0; // the carry bit after ThumbExpandImm_C or ARMExpandImm_C. // for setflags == false, this value is a don't care // initialized to 0 to silence the static analyzer bool setflags; switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 10, 8); setflags = !InITBlock(); imm32 = Bits32(opcode, 7, 0); // imm32 = ZeroExtend(imm8, 32) carry = APSR_C; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); if (BadReg(Rd)) return false; break; case eEncodingT3: { // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm4:i:imm3:imm8, 32); Rd = Bits32 (opcode, 11, 8); setflags = false; uint32_t imm4 = Bits32 (opcode, 19, 16); uint32_t imm3 = Bits32 (opcode, 14, 12); uint32_t i = Bit32 (opcode, 26); uint32_t imm8 = Bits32 (opcode, 7, 0); imm32 = (imm4 << 12) | (i << 11) | (imm3 << 8) | imm8; // if BadReg(d) then UNPREDICTABLE; if (BadReg (Rd)) return false; } break; case eEncodingA1: // d = UInt(Rd); setflags = (S == Ô1Õ); (imm32, carry) = ARMExpandImm_C(imm12, APSR.C); Rd = Bits32 (opcode, 15, 12); setflags = BitIsSet (opcode, 20); imm32 = ARMExpandImm_C (opcode, APSR_C, carry); // if Rd == Ô1111Õ && S == Ô1Õ then SEE SUBS PC, LR and related instructions; if ((Rd == 15) && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; case eEncodingA2: { // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm4:imm12, 32); Rd = Bits32 (opcode, 15, 12); setflags = false; uint32_t imm4 = Bits32 (opcode, 19, 16); uint32_t imm12 = Bits32 (opcode, 11, 0); imm32 = (imm4 << 12) | imm12; // if d == 15 then UNPREDICTABLE; if (Rd == 15) return false; } break; default: return false; } uint32_t result = imm32; // The context specifies that an immediate is to be moved into Rd. EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // MUL multiplies two register values. The least significant 32 bits of the result are written to the destination // register. These 32 bits do not depend on whether the source register values are considered to be signed values or // unsigned values. // // Optionally, it can update the condition flags based on the result. In the Thumb instruction set, this option is // limited to only a few forms of the instruction. bool EmulateInstructionARM::EmulateMUL (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); operand1 = SInt(R[n]); // operand1 = UInt(R[n]) produces the same final results operand2 = SInt(R[m]); // operand2 = UInt(R[m]) produces the same final results result = operand1 * operand2; R[d] = result<31:0>; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); if ArchVersion() == 4 then APSR.C = bit UNKNOWN; // else APSR.C unchanged // APSR.V always unchanged #endif if (ConditionPassed(opcode)) { uint32_t d; uint32_t n; uint32_t m; bool setflags; // EncodingSpecificOperations(); switch (encoding) { case eEncodingT1: // d = UInt(Rdm); n = UInt(Rn); m = UInt(Rdm); setflags = !InITBlock(); d = Bits32 (opcode, 2, 0); n = Bits32 (opcode, 5, 3); m = Bits32 (opcode, 2, 0); setflags = !InITBlock(); // if ArchVersion() < 6 && d == n then UNPREDICTABLE; if ((ArchVersion() < ARMv6) && (d == n)) return false; break; case eEncodingT2: // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = FALSE; d = Bits32 (opcode, 11, 8); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); setflags = false; // if BadReg(d) || BadReg(n) || BadReg(m) then UNPREDICTABLE; if (BadReg (d) || BadReg (n) || BadReg (m)) return false; break; case eEncodingA1: // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == '1'); d = Bits32 (opcode, 19, 16); n = Bits32 (opcode, 3, 0); m = Bits32 (opcode, 11, 8); setflags = BitIsSet (opcode, 20); // if d == 15 || n == 15 || m == 15 then UNPREDICTABLE; if ((d == 15) || (n == 15) || (m == 15)) return false; // if ArchVersion() < 6 && d == n then UNPREDICTABLE; if ((ArchVersion() < ARMv6) && (d == n)) return false; break; default: return false; } bool success = false; // operand1 = SInt(R[n]); // operand1 = UInt(R[n]) produces the same final results uint64_t operand1 = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; // operand2 = SInt(R[m]); // operand2 = UInt(R[m]) produces the same final results uint64_t operand2 = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; // result = operand1 * operand2; uint64_t result = operand1 * operand2; // R[d] = result<31:0>; RegisterInfo op1_reg; RegisterInfo op2_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, op1_reg); GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, op2_reg); EmulateInstruction::Context context; context.type = eContextArithmetic; context.SetRegisterRegisterOperands (op1_reg, op2_reg); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, (0x0000ffff & result))) return false; // if setflags then if (setflags) { // APSR.N = result<31>; // APSR.Z = IsZeroBit(result); m_new_inst_cpsr = m_opcode_cpsr; SetBit32 (m_new_inst_cpsr, CPSR_N_POS, Bit32 (result, 31)); SetBit32 (m_new_inst_cpsr, CPSR_Z_POS, result == 0 ? 1 : 0); if (m_new_inst_cpsr != m_opcode_cpsr) { if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_FLAGS, m_new_inst_cpsr)) return false; } // if ArchVersion() == 4 then // APSR.C = bit UNKNOWN; } } return true; } // Bitwise NOT (immediate) writes the bitwise inverse of an immediate value to the destination register. // It can optionally update the condition flags based on the value. bool EmulateInstructionARM::EmulateMVNImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); result = NOT(imm32); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged } #endif if (ConditionPassed(opcode)) { uint32_t Rd; // the destination register uint32_t imm32; // the output after ThumbExpandImm_C or ARMExpandImm_C uint32_t carry; // the carry bit after ThumbExpandImm_C or ARMExpandImm_C bool setflags; switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } uint32_t result = ~imm32; // The context specifies that an immediate is to be moved into Rd. EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // Bitwise NOT (register) writes the bitwise inverse of a register value to the destination register. // It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateMVNReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); result = NOT(shifted); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged } #endif if (ConditionPassed(opcode)) { uint32_t Rm; // the source register uint32_t Rd; // the destination register ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm bool setflags; uint32_t carry; // the carry bit after the shift operation switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; if (InITBlock()) return false; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); // if (BadReg(d) || BadReg(m)) then UNPREDICTABLE; if (BadReg(Rd) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); break; default: return false; } bool success = false; uint32_t value = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift_C(value, shift_t, shift_n, APSR_C, carry, &success); if (!success) return false; uint32_t result = ~shifted; // The context specifies that an immediate is to be moved into Rd. EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // PC relative immediate load into register, possibly followed by ADD (SP plus register). // LDR (literal) bool EmulateInstructionARM::EmulateLDRRtPCRelative (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); NullCheckIfThumbEE(15); base = Align(PC,4); address = if add then (base + imm32) else (base - imm32); data = MemU[address,4]; if t == 15 then if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; elsif UnalignedSupport() || address<1:0> = '00' then R[t] = data; else // Can only apply before ARMv7 if CurrentInstrSet() == InstrSet_ARM then R[t] = ROR(data, 8*UInt(address<1:0>)); else R[t] = bits(32) UNKNOWN; } #endif if (ConditionPassed(opcode)) { bool success = false; const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; // PC relative immediate load context EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterPlusOffset; RegisterInfo pc_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg); context.SetRegisterPlusOffset (pc_reg, 0); uint32_t Rt; // the destination register uint32_t imm32; // immediate offset from the PC bool add; // +imm32 or -imm32? addr_t base; // the base address addr_t address; // the PC relative address uint32_t data; // the literal data value from the PC relative load switch (encoding) { case eEncodingT1: Rt = Bits32(opcode, 10, 8); imm32 = Bits32(opcode, 7, 0) << 2; // imm32 = ZeroExtend(imm8:'00', 32); add = true; break; case eEncodingT2: Rt = Bits32(opcode, 15, 12); imm32 = Bits32(opcode, 11, 0) << 2; // imm32 = ZeroExtend(imm12, 32); add = BitIsSet(opcode, 23); if (Rt == 15 && InITBlock() && !LastInITBlock()) return false; break; default: return false; } base = Align(pc, 4); if (add) address = base + imm32; else address = base - imm32; context.SetRegisterPlusOffset(pc_reg, address - base); data = MemURead(context, address, 4, 0, &success); if (!success) return false; if (Rt == 15) { if (Bits32(address, 1, 0) == 0) { // In ARMv5T and above, this is an interworking branch. if (!LoadWritePC(context, data)) return false; } else return false; } else if (UnalignedSupport() || Bits32(address, 1, 0) == 0) { if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + Rt, data)) return false; } else // We don't handle ARM for now. return false; } return true; } // An add operation to adjust the SP. // ADD (SP plus immediate) bool EmulateInstructionARM::EmulateADDSPImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(SP, imm32, '0'); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; } #endif bool success = false; if (ConditionPassed(opcode)) { const addr_t sp = ReadCoreReg (SP_REG, &success); if (!success) return false; uint32_t imm32; // the immediate operand uint32_t d; //bool setflags = false; // Add this back if/when support eEncodingT3 eEncodingA1 switch (encoding) { case eEncodingT1: // d = UInt(Rd); setflags = FALSE; imm32 = ZeroExtend(imm8:'00', 32); d = Bits32 (opcode, 10, 8); imm32 = (Bits32 (opcode, 7, 0) << 2); break; case eEncodingT2: // d = 13; setflags = FALSE; imm32 = ZeroExtend(imm7:'00', 32); d = 13; imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32) break; default: return false; } addr_t sp_offset = imm32; addr_t addr = sp + sp_offset; // the adjusted stack pointer value EmulateInstruction::Context context; context.type = EmulateInstruction::eContextAdjustStackPointer; RegisterInfo sp_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); context.SetRegisterPlusOffset (sp_reg, sp_offset); if (d == 15) { if (!ALUWritePC (context, addr)) return false; } else { if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, addr)) return false; // Add this back if/when support eEncodingT3 eEncodingA1 //if (setflags) //{ // APSR.N = result<31>; // APSR.Z = IsZeroBit(result); // APSR.C = carry; // APSR.V = overflow; //} } } return true; } // An add operation to adjust the SP. // ADD (SP plus register) bool EmulateInstructionARM::EmulateADDSPRm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(SP, shifted, '0'); if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; } #endif bool success = false; if (ConditionPassed(opcode)) { const addr_t sp = ReadCoreReg (SP_REG, &success); if (!success) return false; uint32_t Rm; // the second operand switch (encoding) { case eEncodingT2: Rm = Bits32(opcode, 6, 3); break; default: return false; } int32_t reg_value = ReadCoreReg(Rm, &success); if (!success) return false; addr_t addr = (int32_t)sp + reg_value; // the adjusted stack pointer value EmulateInstruction::Context context; context.type = eContextArithmetic; RegisterInfo sp_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); RegisterInfo other_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, other_reg); context.SetRegisterRegisterOperands (sp_reg, other_reg); if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, addr)) return false; } return true; } // Branch with Link and Exchange Instruction Sets (immediate) calls a subroutine // at a PC-relative address, and changes instruction set from ARM to Thumb, or // from Thumb to ARM. // BLX (immediate) bool EmulateInstructionARM::EmulateBLXImmediate (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); if CurrentInstrSet() == InstrSet_ARM then LR = PC - 4; else LR = PC<31:1> : '1'; if targetInstrSet == InstrSet_ARM then targetAddress = Align(PC,4) + imm32; else targetAddress = PC + imm32; SelectInstrSet(targetInstrSet); BranchWritePC(targetAddress); } #endif bool success = true; if (ConditionPassed(opcode)) { EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRelativeBranchImmediate; const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; addr_t lr; // next instruction address addr_t target; // target address int32_t imm32; // PC-relative offset switch (encoding) { case eEncodingT1: { lr = pc | 1u; // return address uint32_t S = Bit32(opcode, 26); uint32_t imm10 = Bits32(opcode, 25, 16); uint32_t J1 = Bit32(opcode, 13); uint32_t J2 = Bit32(opcode, 11); uint32_t imm11 = Bits32(opcode, 10, 0); uint32_t I1 = !(J1 ^ S); uint32_t I2 = !(J2 ^ S); uint32_t imm25 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1); imm32 = llvm::SignExtend32<25>(imm25); target = pc + imm32; context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); if (InITBlock() && !LastInITBlock()) return false; break; } case eEncodingT2: { lr = pc | 1u; // return address uint32_t S = Bit32(opcode, 26); uint32_t imm10H = Bits32(opcode, 25, 16); uint32_t J1 = Bit32(opcode, 13); uint32_t J2 = Bit32(opcode, 11); uint32_t imm10L = Bits32(opcode, 10, 1); uint32_t I1 = !(J1 ^ S); uint32_t I2 = !(J2 ^ S); uint32_t imm25 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10H << 12) | (imm10L << 2); imm32 = llvm::SignExtend32<25>(imm25); target = Align(pc, 4) + imm32; context.SetISAAndImmediateSigned (eModeARM, 4 + imm32); if (InITBlock() && !LastInITBlock()) return false; break; } case eEncodingA1: lr = pc - 4; // return address imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2); target = Align(pc, 4) + imm32; context.SetISAAndImmediateSigned (eModeARM, 8 + imm32); break; case eEncodingA2: lr = pc - 4; // return address imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2 | Bits32(opcode, 24, 24) << 1); target = pc + imm32; context.SetISAAndImmediateSigned (eModeThumb, 8 + imm32); break; default: return false; } if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, lr)) return false; if (!BranchWritePC(context, target)) return false; } return true; } // Branch with Link and Exchange (register) calls a subroutine at an address and // instruction set specified by a register. // BLX (register) bool EmulateInstructionARM::EmulateBLXRm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); target = R[m]; if CurrentInstrSet() == InstrSet_ARM then next_instr_addr = PC - 4; LR = next_instr_addr; else next_instr_addr = PC - 2; LR = next_instr_addr<31:1> : '1'; BXWritePC(target); } #endif bool success = false; if (ConditionPassed(opcode)) { EmulateInstruction::Context context; context.type = EmulateInstruction::eContextAbsoluteBranchRegister; const uint32_t pc = ReadCoreReg(PC_REG, &success); addr_t lr; // next instruction address if (!success) return false; uint32_t Rm; // the register with the target address switch (encoding) { case eEncodingT1: lr = (pc - 2) | 1u; // return address Rm = Bits32(opcode, 6, 3); // if m == 15 then UNPREDICTABLE; if (Rm == 15) return false; if (InITBlock() && !LastInITBlock()) return false; break; case eEncodingA1: lr = pc - 4; // return address Rm = Bits32(opcode, 3, 0); // if m == 15 then UNPREDICTABLE; if (Rm == 15) return false; break; default: return false; } addr_t target = ReadCoreReg (Rm, &success); if (!success) return false; RegisterInfo dwarf_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg); context.SetRegister (dwarf_reg); if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_RA, lr)) return false; if (!BXWritePC(context, target)) return false; } return true; } // Branch and Exchange causes a branch to an address and instruction set specified by a register. bool EmulateInstructionARM::EmulateBXRm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); BXWritePC(R[m]); } #endif if (ConditionPassed(opcode)) { EmulateInstruction::Context context; context.type = EmulateInstruction::eContextAbsoluteBranchRegister; uint32_t Rm; // the register with the target address switch (encoding) { case eEncodingT1: Rm = Bits32(opcode, 6, 3); if (InITBlock() && !LastInITBlock()) return false; break; case eEncodingA1: Rm = Bits32(opcode, 3, 0); break; default: return false; } bool success = false; addr_t target = ReadCoreReg (Rm, &success); if (!success) return false; RegisterInfo dwarf_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg); context.SetRegister (dwarf_reg); if (!BXWritePC(context, target)) return false; } return true; } // Branch and Exchange Jazelle attempts to change to Jazelle state. If the attempt fails, it branches to an // address and instruction set specified by a register as though it were a BX instruction. // // TODO: Emulate Jazelle architecture? // We currently assume that switching to Jazelle state fails, thus treating BXJ as a BX operation. bool EmulateInstructionARM::EmulateBXJRm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); if JMCR.JE == '0' || CurrentInstrSet() == InstrSet_ThumbEE then BXWritePC(R[m]); else if JazelleAcceptsExecution() then SwitchToJazelleExecution(); else SUBARCHITECTURE_DEFINED handler call; } #endif if (ConditionPassed(opcode)) { EmulateInstruction::Context context; context.type = EmulateInstruction::eContextAbsoluteBranchRegister; uint32_t Rm; // the register with the target address switch (encoding) { case eEncodingT1: Rm = Bits32(opcode, 19, 16); if (BadReg(Rm)) return false; if (InITBlock() && !LastInITBlock()) return false; break; case eEncodingA1: Rm = Bits32(opcode, 3, 0); if (Rm == 15) return false; break; default: return false; } bool success = false; addr_t target = ReadCoreReg (Rm, &success); if (!success) return false; RegisterInfo dwarf_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, dwarf_reg); context.SetRegister (dwarf_reg); if (!BXWritePC(context, target)) return false; } return true; } // Set r7 to point to some ip offset. // SUB (immediate) bool EmulateInstructionARM::EmulateSUBR7IPImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1'); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; } #endif if (ConditionPassed(opcode)) { bool success = false; const addr_t ip = ReadCoreReg (12, &success); if (!success) return false; uint32_t imm32; switch (encoding) { case eEncodingA1: imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) break; default: return false; } addr_t ip_offset = imm32; addr_t addr = ip - ip_offset; // the adjusted ip value EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterPlusOffset; RegisterInfo dwarf_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r12, dwarf_reg); context.SetRegisterPlusOffset (dwarf_reg, -ip_offset); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r7, addr)) return false; } return true; } // Set ip to point to some stack offset. // SUB (SP minus immediate) bool EmulateInstructionARM::EmulateSUBIPSPImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1'); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; } #endif if (ConditionPassed(opcode)) { bool success = false; const addr_t sp = ReadCoreReg (SP_REG, &success); if (!success) return false; uint32_t imm32; switch (encoding) { case eEncodingA1: imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) break; default: return false; } addr_t sp_offset = imm32; addr_t addr = sp - sp_offset; // the adjusted stack pointer value EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterPlusOffset; RegisterInfo dwarf_reg; GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, dwarf_reg); context.SetRegisterPlusOffset (dwarf_reg, -sp_offset); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r12, addr)) return false; } return true; } // This instruction subtracts an immediate value from the SP value, and writes // the result to the destination register. // // If Rd == 13 => A sub operation to adjust the SP -- allocate space for local storage. bool EmulateInstructionARM::EmulateSUBSPImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(SP, NOT(imm32), '1'); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; } #endif bool success = false; if (ConditionPassed(opcode)) { const addr_t sp = ReadCoreReg (SP_REG, &success); if (!success) return false; uint32_t Rd; bool setflags; uint32_t imm32; switch (encoding) { case eEncodingT1: Rd = 13; setflags = false; imm32 = ThumbImm7Scaled(opcode); // imm32 = ZeroExtend(imm7:'00', 32) break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) if (Rd == 15 && setflags) return EmulateCMPImm(opcode, eEncodingT2); if (Rd == 15 && !setflags) return false; break; case eEncodingT3: Rd = Bits32(opcode, 11, 8); setflags = false; imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32) if (Rd == 15) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } AddWithCarryResult res = AddWithCarry(sp, ~imm32, 1); EmulateInstruction::Context context; if (Rd == 13) { uint64_t imm64 = imm32; // Need to expand it to 64 bits before attempting to negate it, or the wrong // value gets passed down to context.SetImmediateSigned. context.type = EmulateInstruction::eContextAdjustStackPointer; context.SetImmediateSigned (-imm64); // the stack pointer offset } else { context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); } if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; } return true; } // A store operation to the stack that also updates the SP. bool EmulateInstructionARM::EmulateSTRRtSP (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; MemU[address,4] = if t == 15 then PCStoreValue() else R[t]; if wback then R[n] = offset_addr; } #endif bool conditional = false; bool success = false; if (ConditionPassed(opcode, &conditional)) { const uint32_t addr_byte_size = GetAddressByteSize(); const addr_t sp = ReadCoreReg (SP_REG, &success); if (!success) return false; uint32_t Rt; // the source register uint32_t imm12; uint32_t Rn; // This function assumes Rn is the SP, but we should verify that. bool index; bool add; bool wback; switch (encoding) { case eEncodingA1: Rt = Bits32(opcode, 15, 12); imm12 = Bits32(opcode, 11, 0); Rn = Bits32 (opcode, 19, 16); if (Rn != 13) // 13 is the SP reg on ARM. Verify that Rn == SP. return false; index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); if (wback && ((Rn == 15) || (Rn == Rt))) return false; break; default: return false; } addr_t offset_addr; if (add) offset_addr = sp + imm12; else offset_addr = sp - imm12; addr_t addr; if (index) addr = offset_addr; else addr = sp; EmulateInstruction::Context context; if (conditional) context.type = EmulateInstruction::eContextRegisterStore; else context.type = EmulateInstruction::eContextPushRegisterOnStack; RegisterInfo sp_reg; RegisterInfo dwarf_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rt, dwarf_reg); context.SetRegisterToRegisterPlusOffset ( dwarf_reg, sp_reg, addr - sp); if (Rt != 15) { uint32_t reg_value = ReadCoreReg(Rt, &success); if (!success) return false; if (!MemUWrite (context, addr, reg_value, addr_byte_size)) return false; } else { const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; if (!MemUWrite (context, addr, pc, addr_byte_size)) return false; } if (wback) { context.type = EmulateInstruction::eContextAdjustStackPointer; context.SetImmediateSigned (addr - sp); if (!WriteRegisterUnsigned (context, eRegisterKindGeneric, LLDB_REGNUM_GENERIC_SP, offset_addr)) return false; } } return true; } // Vector Push stores multiple extension registers to the stack. // It also updates SP to point to the start of the stored data. bool EmulateInstructionARM::EmulateVPUSH (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(13); address = SP - imm32; SP = SP - imm32; if single_regs then for r = 0 to regs-1 MemA[address,4] = S[d+r]; address = address+4; else for r = 0 to regs-1 // Store as two word-aligned words in the correct order for current endianness. MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>; MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>; address = address+8; } #endif bool success = false; bool conditional = false; if (ConditionPassed(opcode, &conditional)) { const uint32_t addr_byte_size = GetAddressByteSize(); const addr_t sp = ReadCoreReg (SP_REG, &success); if (!success) return false; bool single_regs; uint32_t d; // UInt(D:Vd) or UInt(Vd:D) starting register uint32_t imm32; // stack offset uint32_t regs; // number of registers switch (encoding) { case eEncodingT1: case eEncodingA1: single_regs = false; d = Bit32(opcode, 22) << 4 | Bits32(opcode, 15, 12); imm32 = Bits32(opcode, 7, 0) * addr_byte_size; // If UInt(imm8) is odd, see "FSTMX". regs = Bits32(opcode, 7, 0) / 2; // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; if (regs == 0 || regs > 16 || (d + regs) > 32) return false; break; case eEncodingT2: case eEncodingA2: single_regs = true; d = Bits32(opcode, 15, 12) << 1 | Bit32(opcode, 22); imm32 = Bits32(opcode, 7, 0) * addr_byte_size; regs = Bits32(opcode, 7, 0); // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; if (regs == 0 || regs > 16 || (d + regs) > 32) return false; break; default: return false; } uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; uint32_t reg_byte_size = single_regs ? addr_byte_size : addr_byte_size * 2; addr_t sp_offset = imm32; addr_t addr = sp - sp_offset; uint32_t i; EmulateInstruction::Context context; if (conditional) context.type = EmulateInstruction::eContextRegisterStore; else context.type = EmulateInstruction::eContextPushRegisterOnStack; RegisterInfo dwarf_reg; RegisterInfo sp_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); for (i=0; i 16 || (d+regs) > 32 then UNPREDICTABLE; if (regs == 0 || regs > 16 || (d + regs) > 32) return false; break; case eEncodingT2: case eEncodingA2: single_regs = true; d = Bits32(opcode, 15, 12) << 1 | Bit32(opcode, 22); imm32 = Bits32(opcode, 7, 0) * addr_byte_size; regs = Bits32(opcode, 7, 0); // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; if (regs == 0 || regs > 16 || (d + regs) > 32) return false; break; default: return false; } uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; uint32_t reg_byte_size = single_regs ? addr_byte_size : addr_byte_size * 2; addr_t sp_offset = imm32; addr_t addr = sp; uint32_t i; uint64_t data; // uint64_t to accomodate 64-bit registers. EmulateInstruction::Context context; if (conditional) context.type = EmulateInstruction::eContextRegisterLoad; else context.type = EmulateInstruction::eContextPopRegisterOffStack; RegisterInfo dwarf_reg; RegisterInfo sp_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); for (i=0; i = firstcond:mask; #endif m_it_session.InitIT(Bits32(opcode, 7, 0)); return true; } bool EmulateInstructionARM::EmulateNop (const uint32_t opcode, const ARMEncoding encoding) { // NOP, nothing to do... return true; } // Branch causes a branch to a target address. bool EmulateInstructionARM::EmulateB (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); BranchWritePC(PC + imm32); } #endif bool success = false; if (ConditionPassed(opcode)) { EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRelativeBranchImmediate; const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; addr_t target; // target address int32_t imm32; // PC-relative offset switch (encoding) { case eEncodingT1: // The 'cond' field is handled in EmulateInstructionARM::CurrentCond(). imm32 = llvm::SignExtend32<9>(Bits32(opcode, 7, 0) << 1); target = pc + imm32; context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); break; case eEncodingT2: imm32 = llvm::SignExtend32<12>(Bits32(opcode, 10, 0)); target = pc + imm32; context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); break; case eEncodingT3: // The 'cond' field is handled in EmulateInstructionARM::CurrentCond(). { uint32_t S = Bit32(opcode, 26); uint32_t imm6 = Bits32(opcode, 21, 16); uint32_t J1 = Bit32(opcode, 13); uint32_t J2 = Bit32(opcode, 11); uint32_t imm11 = Bits32(opcode, 10, 0); uint32_t imm21 = (S << 20) | (J2 << 19) | (J1 << 18) | (imm6 << 12) | (imm11 << 1); imm32 = llvm::SignExtend32<21>(imm21); target = pc + imm32; context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); break; } case eEncodingT4: { uint32_t S = Bit32(opcode, 26); uint32_t imm10 = Bits32(opcode, 25, 16); uint32_t J1 = Bit32(opcode, 13); uint32_t J2 = Bit32(opcode, 11); uint32_t imm11 = Bits32(opcode, 10, 0); uint32_t I1 = !(J1 ^ S); uint32_t I2 = !(J2 ^ S); uint32_t imm25 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1); imm32 = llvm::SignExtend32<25>(imm25); target = pc + imm32; context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); break; } case eEncodingA1: imm32 = llvm::SignExtend32<26>(Bits32(opcode, 23, 0) << 2); target = pc + imm32; context.SetISAAndImmediateSigned (eModeARM, 8 + imm32); break; default: return false; } if (!BranchWritePC(context, target)) return false; } return true; } // Compare and Branch on Nonzero and Compare and Branch on Zero compare the value in a register with // zero and conditionally branch forward a constant value. They do not affect the condition flags. // CBNZ, CBZ bool EmulateInstructionARM::EmulateCB (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... EncodingSpecificOperations(); if nonzero ^ IsZero(R[n]) then BranchWritePC(PC + imm32); #endif bool success = false; // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Bits32(opcode, 2, 0), &success); if (!success) return false; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRelativeBranchImmediate; const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; addr_t target; // target address uint32_t imm32; // PC-relative offset to branch forward bool nonzero; switch (encoding) { case eEncodingT1: imm32 = Bit32(opcode, 9) << 6 | Bits32(opcode, 7, 3) << 1; nonzero = BitIsSet(opcode, 11); target = pc + imm32; context.SetISAAndImmediateSigned (eModeThumb, 4 + imm32); break; default: return false; } if (nonzero ^ (reg_val == 0)) if (!BranchWritePC(context, target)) return false; return true; } // Table Branch Byte causes a PC-relative forward branch using a table of single byte offsets. // A base register provides a pointer to the table, and a second register supplies an index into the table. // The branch length is twice the value of the byte returned from the table. // // Table Branch Halfword causes a PC-relative forward branch using a table of single halfword offsets. // A base register provides a pointer to the table, and a second register supplies an index into the table. // The branch length is twice the value of the halfword returned from the table. // TBB, TBH bool EmulateInstructionARM::EmulateTB (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... EncodingSpecificOperations(); NullCheckIfThumbEE(n); if is_tbh then halfwords = UInt(MemU[R[n]+LSL(R[m],1), 2]); else halfwords = UInt(MemU[R[n]+R[m], 1]); BranchWritePC(PC + 2*halfwords); #endif bool success = false; uint32_t Rn; // the base register which contains the address of the table of branch lengths uint32_t Rm; // the index register which contains an integer pointing to a byte/halfword in the table bool is_tbh; // true if table branch halfword switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); is_tbh = BitIsSet(opcode, 4); if (Rn == 13 || BadReg(Rm)) return false; if (InITBlock() && !LastInITBlock()) return false; break; default: return false; } // Read the address of the table from the operand register Rn. // The PC can be used, in which case the table immediately follows this instruction. uint32_t base = ReadCoreReg(Rm, &success); if (!success) return false; // the table index uint32_t index = ReadCoreReg(Rm, &success); if (!success) return false; // the offsetted table address addr_t addr = base + (is_tbh ? index*2 : index); // PC-relative offset to branch forward EmulateInstruction::Context context; context.type = EmulateInstruction::eContextTableBranchReadMemory; uint32_t offset = MemURead(context, addr, is_tbh ? 2 : 1, 0, &success) * 2; if (!success) return false; const uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; // target address addr_t target = pc + offset; context.type = EmulateInstruction::eContextRelativeBranchImmediate; context.SetISAAndImmediateSigned (eModeThumb, 4 + offset); if (!BranchWritePC(context, target)) return false; return true; } // This instruction adds an immediate value to a register value, and writes the result to the destination register. // It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateADDImmThumb (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t n; bool setflags; uint32_t imm32; uint32_t carry_out; //EncodingSpecificOperations(); switch (encoding) { case eEncodingT1: // d = UInt(Rd); n = UInt(Rn); setflags = !InITBlock(); imm32 = ZeroExtend(imm3, 32); d = Bits32 (opcode, 2, 0); n = Bits32 (opcode, 5, 3); setflags = !InITBlock(); imm32 = Bits32 (opcode, 8,6); break; case eEncodingT2: // d = UInt(Rdn); n = UInt(Rdn); setflags = !InITBlock(); imm32 = ZeroExtend(imm8, 32); d = Bits32 (opcode, 10, 8); n = Bits32 (opcode, 10, 8); setflags = !InITBlock(); imm32 = Bits32 (opcode, 7, 0); break; case eEncodingT3: // if Rd == '1111' && S == '1' then SEE CMN (immediate); // if Rn == '1101' then SEE ADD (SP plus immediate); // d = UInt(Rd); n = UInt(Rn); setflags = (S == '1'); imm32 = ThumbExpandImm(i:imm3:imm8); d = Bits32 (opcode, 11, 8); n = Bits32 (opcode, 19, 16); setflags = BitIsSet (opcode, 20); imm32 = ThumbExpandImm_C (opcode, APSR_C, carry_out); // if BadReg(d) || n == 15 then UNPREDICTABLE; if (BadReg (d) || (n == 15)) return false; break; case eEncodingT4: { // if Rn == '1111' then SEE ADR; // if Rn == '1101' then SEE ADD (SP plus immediate); // d = UInt(Rd); n = UInt(Rn); setflags = FALSE; imm32 = ZeroExtend(i:imm3:imm8, 32); d = Bits32 (opcode, 11, 8); n = Bits32 (opcode, 19, 16); setflags = false; uint32_t i = Bit32 (opcode, 26); uint32_t imm3 = Bits32 (opcode, 14, 12); uint32_t imm8 = Bits32 (opcode, 7, 0); imm32 = (i << 11) | (imm3 << 8) | imm8; // if BadReg(d) then UNPREDICTABLE; if (BadReg (d)) return false; break; } default: return false; } uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; //(result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); AddWithCarryResult res = AddWithCarry (Rn, imm32, 0); RegisterInfo reg_n; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, reg_n); EmulateInstruction::Context context; context.type = eContextArithmetic; context.SetRegisterPlusOffset (reg_n, imm32); //R[d] = result; //if setflags then //APSR.N = result<31>; //APSR.Z = IsZeroBit(result); //APSR.C = carry; //APSR.V = overflow; if (!WriteCoreRegOptionalFlags (context, res.result, d, setflags, res.carry_out, res.overflow)) return false; } return true; } // This instruction adds an immediate value to a register value, and writes the result to the destination // register. It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateADDImmARM (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn; uint32_t imm32; // the immediate value to be added to the value obtained from Rn bool setflags; switch (encoding) { case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(val1, imm32, 0); EmulateInstruction::Context context; context.type = eContextArithmetic; RegisterInfo dwarf_reg; GetRegisterInfo (eRegisterKindDWARF, Rn, dwarf_reg); context.SetRegisterPlusOffset (dwarf_reg, imm32); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; } return true; } // This instruction adds a register value and an optionally-shifted register value, and writes the result // to the destination register. It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateADDReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(R[n], shifted, '0'); if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm bool setflags; switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 2, 0); Rn = Bits32(opcode, 5, 3); Rm = Bits32(opcode, 8, 6); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rd = Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0); Rm = Bits32(opcode, 6, 3); setflags = false; shift_t = SRType_LSL; shift_n = 0; if (Rn == 15 && Rm == 15) return false; if (Rd == 15 && InITBlock() && !LastInITBlock()) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(val1, shifted, 0); EmulateInstruction::Context context; context.type = eContextArithmetic; RegisterInfo op1_reg; RegisterInfo op2_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rn, op1_reg); GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rm, op2_reg); context.SetRegisterRegisterOperands (op1_reg, op2_reg); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; } return true; } // Compare Negative (immediate) adds a register value and an immediate value. // It updates the condition flags based on the result, and discards the result. bool EmulateInstructionARM::EmulateCMNImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], imm32, '0'); APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rn; // the first operand uint32_t imm32; // the immediate value to be compared with switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 19, 16); imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) if (Rn == 15) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) break; default: return false; } // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(reg_val, imm32, 0); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) return false; return true; } // Compare Negative (register) adds a register value and an optionally-shifted register value. // It updates the condition flags based on the result, and discards the result. bool EmulateInstructionARM::EmulateCMNReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(R[n], shifted, '0'); APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rn; // the first operand uint32_t Rm; // the second operand ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); shift_n = DecodeImmShiftThumb(opcode, shift_t); // if n == 15 || BadReg(m) then UNPREDICTABLE; if (Rn == 15 || BadReg(Rm)) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); shift_n = DecodeImmShiftARM(opcode, shift_t); break; default: return false; } // Read the register value from register Rn. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the register value from register Rm. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(val1, shifted, 0); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) return false; return true; } // Compare (immediate) subtracts an immediate value from a register value. // It updates the condition flags based on the result, and discards the result. bool EmulateInstructionARM::EmulateCMPImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1'); APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rn; // the first operand uint32_t imm32; // the immediate value to be compared with switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 10, 8); imm32 = Bits32(opcode, 7, 0); break; case eEncodingT2: Rn = Bits32(opcode, 19, 16); imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) if (Rn == 15) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) break; default: return false; } // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) return false; return true; } // Compare (register) subtracts an optionally-shifted register value from a register value. // It updates the condition flags based on the result, and discards the result. bool EmulateInstructionARM::EmulateCMPReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), '1'); APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rn; // the first operand uint32_t Rm; // the second operand ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rn = Bit32(opcode, 7) << 3 | Bits32(opcode, 2, 0); Rm = Bits32(opcode, 6, 3); shift_t = SRType_LSL; shift_n = 0; if (Rn < 8 && Rm < 8) return false; if (Rn == 15 || Rm == 15) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); shift_n = DecodeImmShiftARM(opcode, shift_t); break; default: return false; } // Read the register value from register Rn. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the register value from register Rm. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(val1, ~shifted, 1); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteFlags(context, res.result, res.carry_out, res.overflow)) return false; return true; } // Arithmetic Shift Right (immediate) shifts a register value right by an immediate number of bits, // shifting in copies of its sign bit, and writes the result to the destination register. It can // optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateASRImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry) = Shift_C(R[m], SRType_ASR, shift_n, APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftImm (opcode, encoding, SRType_ASR); } // Arithmetic Shift Right (register) shifts a register value right by a variable number of bits, // shifting in copies of its sign bit, and writes the result to the destination register. // The variable number of bits is read from the bottom byte of a register. It can optionally update // the condition flags based on the result. bool EmulateInstructionARM::EmulateASRReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shift_n = UInt(R[m]<7:0>); (result, carry) = Shift_C(R[m], SRType_ASR, shift_n, APSR.C); R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftReg (opcode, encoding, SRType_ASR); } // Logical Shift Left (immediate) shifts a register value left by an immediate number of bits, // shifting in zeros, and writes the result to the destination register. It can optionally // update the condition flags based on the result. bool EmulateInstructionARM::EmulateLSLImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry) = Shift_C(R[m], SRType_LSL, shift_n, APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftImm (opcode, encoding, SRType_LSL); } // Logical Shift Left (register) shifts a register value left by a variable number of bits, // shifting in zeros, and writes the result to the destination register. The variable number // of bits is read from the bottom byte of a register. It can optionally update the condition // flags based on the result. bool EmulateInstructionARM::EmulateLSLReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shift_n = UInt(R[m]<7:0>); (result, carry) = Shift_C(R[m], SRType_LSL, shift_n, APSR.C); R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftReg (opcode, encoding, SRType_LSL); } // Logical Shift Right (immediate) shifts a register value right by an immediate number of bits, // shifting in zeros, and writes the result to the destination register. It can optionally // update the condition flags based on the result. bool EmulateInstructionARM::EmulateLSRImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry) = Shift_C(R[m], SRType_LSR, shift_n, APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftImm (opcode, encoding, SRType_LSR); } // Logical Shift Right (register) shifts a register value right by a variable number of bits, // shifting in zeros, and writes the result to the destination register. The variable number // of bits is read from the bottom byte of a register. It can optionally update the condition // flags based on the result. bool EmulateInstructionARM::EmulateLSRReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shift_n = UInt(R[m]<7:0>); (result, carry) = Shift_C(R[m], SRType_LSR, shift_n, APSR.C); R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftReg (opcode, encoding, SRType_LSR); } // Rotate Right (immediate) provides the value of the contents of a register rotated by a constant value. // The bits that are rotated off the right end are inserted into the vacated bit positions on the left. // It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateRORImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry) = Shift_C(R[m], SRType_ROR, shift_n, APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftImm (opcode, encoding, SRType_ROR); } // Rotate Right (register) provides the value of the contents of a register rotated by a variable number of bits. // The bits that are rotated off the right end are inserted into the vacated bit positions on the left. // The variable number of bits is read from the bottom byte of a register. It can optionally update the condition // flags based on the result. bool EmulateInstructionARM::EmulateRORReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shift_n = UInt(R[m]<7:0>); (result, carry) = Shift_C(R[m], SRType_ROR, shift_n, APSR.C); R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftReg (opcode, encoding, SRType_ROR); } // Rotate Right with Extend provides the value of the contents of a register shifted right by one place, // with the carry flag shifted into bit [31]. // // RRX can optionally update the condition flags based on the result. // In that case, bit [0] is shifted into the carry flag. bool EmulateInstructionARM::EmulateRRX (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry) = Shift_C(R[m], SRType_RRX, 1, APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif return EmulateShiftImm (opcode, encoding, SRType_RRX); } bool EmulateInstructionARM::EmulateShiftImm (const uint32_t opcode, const ARMEncoding encoding, ARM_ShifterType shift_type) { // assert(shift_type == SRType_ASR // || shift_type == SRType_LSL // || shift_type == SRType_LSR // || shift_type == SRType_ROR // || shift_type == SRType_RRX); bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd; // the destination register uint32_t Rm; // the first operand register uint32_t imm5; // encoding for the shift amount uint32_t carry; // the carry bit after the shift operation bool setflags; // Special case handling! // A8.6.139 ROR (immediate) -- Encoding T1 ARMEncoding use_encoding = encoding; if (shift_type == SRType_ROR && use_encoding == eEncodingT1) { // Morph the T1 encoding from the ARM Architecture Manual into T2 encoding to // have the same decoding of bit fields as the other Thumb2 shift operations. use_encoding = eEncodingT2; } switch (use_encoding) { case eEncodingT1: // Due to the above special case handling! if (shift_type == SRType_ROR) return false; Rd = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); imm5 = Bits32(opcode, 10, 6); break; case eEncodingT2: // A8.6.141 RRX // There's no imm form of RRX instructions. if (shift_type == SRType_RRX) return false; Rd = Bits32(opcode, 11, 8); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); imm5 = Bits32(opcode, 14, 12) << 2 | Bits32(opcode, 7, 6); if (BadReg(Rd) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); imm5 = Bits32(opcode, 11, 7); break; default: return false; } // A8.6.139 ROR (immediate) if (shift_type == SRType_ROR && imm5 == 0) shift_type = SRType_RRX; // Get the first operand. uint32_t value = ReadCoreReg (Rm, &success); if (!success) return false; // Decode the shift amount if not RRX. uint32_t amt = (shift_type == SRType_RRX ? 1 : DecodeImmShift(shift_type, imm5)); uint32_t result = Shift_C(value, shift_type, amt, APSR_C, carry, &success); if (!success) return false; // The context specifies that an immediate is to be moved into Rd. EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } bool EmulateInstructionARM::EmulateShiftReg (const uint32_t opcode, const ARMEncoding encoding, ARM_ShifterType shift_type) { // assert(shift_type == SRType_ASR // || shift_type == SRType_LSL // || shift_type == SRType_LSR // || shift_type == SRType_ROR); bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd; // the destination register uint32_t Rn; // the first operand register uint32_t Rm; // the register whose bottom byte contains the amount to shift by uint32_t carry; // the carry bit after the shift operation bool setflags; switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 2, 0); Rn = Rd; Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 3, 0); Rm = Bits32(opcode, 11, 8); setflags = BitIsSet(opcode, 20); if (Rd == 15 || Rn == 15 || Rm == 15) return false; break; default: return false; } // Get the first operand. uint32_t value = ReadCoreReg (Rn, &success); if (!success) return false; // Get the Rm register content. uint32_t val = ReadCoreReg (Rm, &success); if (!success) return false; // Get the shift amount. uint32_t amt = Bits32(val, 7, 0); uint32_t result = Shift_C(value, shift_type, amt, APSR_C, carry, &success); if (!success) return false; // The context specifies that an immediate is to be moved into Rd. EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // LDM loads multiple registers from consecutive memory locations, using an // address from a base register. Optionally the address just above the highest of those locations // can be written back to the base register. bool EmulateInstructionARM::EmulateLDM (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() EncodingSpecificOperations(); NullCheckIfThumbEE (n); address = R[n]; for i = 0 to 14 if registers == '1' then R[i] = MemA[address, 4]; address = address + 4; if registers<15> == '1' then LoadWritePC (MemA[address, 4]); if wback && registers == '0' then R[n] = R[n] + 4 * BitCount (registers); if wback && registers == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 #endif bool success = false; bool conditional = false; if (ConditionPassed(opcode, &conditional)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); switch (encoding) { case eEncodingT1: // n = UInt(Rn); registers = '00000000':register_list; wback = (registers == '0'); n = Bits32 (opcode, 10, 8); registers = Bits32 (opcode, 7, 0); registers = registers & 0x00ff; // Make sure the top 8 bits are zeros. wback = BitIsClear (registers, n); // if BitCount(registers) < 1 then UNPREDICTABLE; if (BitCount(registers) < 1) return false; break; case eEncodingT2: // if W == '1' && Rn == '1101' then SEE POP; // n = UInt(Rn); registers = P:M:'0':register_list; wback = (W == '1'); n = Bits32 (opcode, 19, 16); registers = Bits32 (opcode, 15, 0); registers = registers & 0xdfff; // Make sure bit 13 is zero. wback = BitIsSet (opcode, 21); // if n == 15 || BitCount(registers) < 2 || (P == '1' && M == '1') then UNPREDICTABLE; if ((n == 15) || (BitCount (registers) < 2) || (BitIsSet (opcode, 14) && BitIsSet (opcode, 15))) return false; // if registers<15> == '1' && InITBlock() && !LastInITBlock() then UNPREDICTABLE; if (BitIsSet (registers, 15) && InITBlock() && !LastInITBlock()) return false; // if wback && registers == '1' then UNPREDICTABLE; if (wback && BitIsSet (registers, n)) return false; break; case eEncodingA1: n = Bits32 (opcode, 19, 16); registers = Bits32 (opcode, 15, 0); wback = BitIsSet (opcode, 21); if ((n == 15) || (BitCount (registers) < 1)) return false; break; default: return false; } int32_t offset = 0; const addr_t base_address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterPlusOffset; RegisterInfo dwarf_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg); context.SetRegisterPlusOffset (dwarf_reg, offset); for (int i = 0; i < 14; ++i) { if (BitIsSet (registers, i)) { context.type = EmulateInstruction::eContextRegisterPlusOffset; context.SetRegisterPlusOffset (dwarf_reg, offset); if (wback && (n == 13)) // Pop Instruction { if (conditional) context.type = EmulateInstruction::eContextRegisterLoad; else context.type = EmulateInstruction::eContextPopRegisterOffStack; } // R[i] = MemA [address, 4]; address = address + 4; uint32_t data = MemARead (context, base_address + offset, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + i, data)) return false; offset += addr_byte_size; } } if (BitIsSet (registers, 15)) { //LoadWritePC (MemA [address, 4]); context.type = EmulateInstruction::eContextRegisterPlusOffset; context.SetRegisterPlusOffset (dwarf_reg, offset); uint32_t data = MemARead (context, base_address + offset, addr_byte_size, 0, &success); if (!success) return false; // In ARMv5T and above, this is an interworking branch. if (!LoadWritePC(context, data)) return false; } if (wback && BitIsClear (registers, n)) { // R[n] = R[n] + 4 * BitCount (registers) int32_t offset = addr_byte_size * BitCount (registers); context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetRegisterPlusOffset (dwarf_reg, offset); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, base_address + offset)) return false; } if (wback && BitIsSet (registers, n)) // R[n] bits(32) UNKNOWN; return WriteBits32Unknown (n); } return true; } // LDMDA loads multiple registers from consecutive memory locations using an address from a base register. // The consecutive memory locations end at this address and the address just below the lowest of those locations // can optionally be written back to the base register. bool EmulateInstructionARM::EmulateLDMDA (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); address = R[n] - 4*BitCount(registers) + 4; for i = 0 to 14 if registers == '1' then R[i] = MemA[address,4]; address = address + 4; if registers<15> == '1' then LoadWritePC(MemA[address,4]); if wback && registers == '0' then R[n] = R[n] - 4*BitCount(registers); if wback && registers == '1' then R[n] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); // EncodingSpecificOperations(); switch (encoding) { case eEncodingA1: // n = UInt(Rn); registers = register_list; wback = (W == '1'); n = Bits32 (opcode, 19, 16); registers = Bits32 (opcode, 15, 0); wback = BitIsSet (opcode, 21); // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; if ((n == 15) || (BitCount (registers) < 1)) return false; break; default: return false; } // address = R[n] - 4*BitCount(registers) + 4; int32_t offset = 0; addr_t Rn = ReadCoreReg (n, &success); if (!success) return false; addr_t address = Rn - (addr_byte_size * BitCount (registers)) + addr_byte_size; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterPlusOffset; RegisterInfo dwarf_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg); context.SetRegisterPlusOffset (dwarf_reg, offset); // for i = 0 to 14 for (int i = 0; i < 14; ++i) { // if registers == '1' then if (BitIsSet (registers, i)) { // R[i] = MemA[address,4]; address = address + 4; context.SetRegisterPlusOffset (dwarf_reg, Rn - (address + offset)); uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + i, data)) return false; offset += addr_byte_size; } } // if registers<15> == '1' then // LoadWritePC(MemA[address,4]); if (BitIsSet (registers, 15)) { context.SetRegisterPlusOffset (dwarf_reg, offset); uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); if (!success) return false; // In ARMv5T and above, this is an interworking branch. if (!LoadWritePC(context, data)) return false; } // if wback && registers == '0' then R[n] = R[n] - 4*BitCount(registers); if (wback && BitIsClear (registers, n)) { if (!success) return false; offset = (addr_byte_size * BitCount (registers)) * -1; context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetImmediateSigned (offset); addr_t addr = Rn + offset; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, addr)) return false; } // if wback && registers == '1' then R[n] = bits(32) UNKNOWN; if (wback && BitIsSet (registers, n)) return WriteBits32Unknown (n); } return true; } // LDMDB loads multiple registers from consecutive memory locations using an address from a base register. The // consecutive memory lcoations end just below this address, and the address of the lowest of those locations can // be optionally written back to the base register. bool EmulateInstructionARM::EmulateLDMDB (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); address = R[n] - 4*BitCount(registers); for i = 0 to 14 if registers == '1' then R[i] = MemA[address,4]; address = address + 4; if registers<15> == '1' then LoadWritePC(MemA[address,4]); if wback && registers == '0' then R[n] = R[n] - 4*BitCount(registers); if wback && registers == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); switch (encoding) { case eEncodingT1: // n = UInt(Rn); registers = P:M:'0':register_list; wback = (W == '1'); n = Bits32 (opcode, 19, 16); registers = Bits32 (opcode, 15, 0); registers = registers & 0xdfff; // Make sure bit 13 is a zero. wback = BitIsSet (opcode, 21); // if n == 15 || BitCount(registers) < 2 || (P == '1' && M == '1') then UNPREDICTABLE; if ((n == 15) || (BitCount (registers) < 2) || (BitIsSet (opcode, 14) && BitIsSet (opcode, 15))) return false; // if registers<15> == '1' && InITBlock() && !LastInITBlock() then UNPREDICTABLE; if (BitIsSet (registers, 15) && InITBlock() && !LastInITBlock()) return false; // if wback && registers == '1' then UNPREDICTABLE; if (wback && BitIsSet (registers, n)) return false; break; case eEncodingA1: // n = UInt(Rn); registers = register_list; wback = (W == '1'); n = Bits32 (opcode, 19, 16); registers = Bits32 (opcode, 15, 0); wback = BitIsSet (opcode, 21); // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; if ((n == 15) || (BitCount (registers) < 1)) return false; break; default: return false; } // address = R[n] - 4*BitCount(registers); int32_t offset = 0; addr_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t address = Rn - (addr_byte_size * BitCount (registers)); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterPlusOffset; RegisterInfo dwarf_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg); context.SetRegisterPlusOffset (dwarf_reg, Rn - address); for (int i = 0; i < 14; ++i) { if (BitIsSet (registers, i)) { // R[i] = MemA[address,4]; address = address + 4; context.SetRegisterPlusOffset (dwarf_reg, Rn - (address + offset)); uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + i, data)) return false; offset += addr_byte_size; } } // if registers<15> == '1' then // LoadWritePC(MemA[address,4]); if (BitIsSet (registers, 15)) { context.SetRegisterPlusOffset (dwarf_reg, offset); uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); if (!success) return false; // In ARMv5T and above, this is an interworking branch. if (!LoadWritePC(context, data)) return false; } // if wback && registers == '0' then R[n] = R[n] - 4*BitCount(registers); if (wback && BitIsClear (registers, n)) { if (!success) return false; offset = (addr_byte_size * BitCount (registers)) * -1; context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetImmediateSigned (offset); addr_t addr = Rn + offset; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, addr)) return false; } // if wback && registers == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 if (wback && BitIsSet (registers, n)) return WriteBits32Unknown (n); } return true; } // LDMIB loads multiple registers from consecutive memory locations using an address from a base register. The // consecutive memory locations start just above this address, and thea ddress of the last of those locations can // optinoally be written back to the base register. bool EmulateInstructionARM::EmulateLDMIB (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); address = R[n] + 4; for i = 0 to 14 if registers == '1' then R[i] = MemA[address,4]; address = address + 4; if registers<15> == '1' then LoadWritePC(MemA[address,4]); if wback && registers == '0' then R[n] = R[n] + 4*BitCount(registers); if wback && registers == '1' then R[n] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); switch (encoding) { case eEncodingA1: // n = UInt(Rn); registers = register_list; wback = (W == '1'); n = Bits32 (opcode, 19, 16); registers = Bits32 (opcode, 15, 0); wback = BitIsSet (opcode, 21); // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; if ((n == 15) || (BitCount (registers) < 1)) return false; break; default: return false; } // address = R[n] + 4; int32_t offset = 0; addr_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t address = Rn + addr_byte_size; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterPlusOffset; RegisterInfo dwarf_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, dwarf_reg); context.SetRegisterPlusOffset (dwarf_reg, offset); for (int i = 0; i < 14; ++i) { if (BitIsSet (registers, i)) { // R[i] = MemA[address,4]; address = address + 4; context.SetRegisterPlusOffset (dwarf_reg, offset + addr_byte_size); uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + i, data)) return false; offset += addr_byte_size; } } // if registers<15> == '1' then // LoadWritePC(MemA[address,4]); if (BitIsSet (registers, 15)) { context.SetRegisterPlusOffset (dwarf_reg, offset); uint32_t data = MemARead (context, address + offset, addr_byte_size, 0, &success); if (!success) return false; // In ARMv5T and above, this is an interworking branch. if (!LoadWritePC(context, data)) return false; } // if wback && registers == '0' then R[n] = R[n] + 4*BitCount(registers); if (wback && BitIsClear (registers, n)) { if (!success) return false; offset = addr_byte_size * BitCount (registers); context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetImmediateSigned (offset); addr_t addr = Rn + offset; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, addr)) return false; } // if wback && registers == '1' then R[n] = bits(32) UNKNOWN; // Only possible for encoding A1 if (wback && BitIsSet (registers, n)) return WriteBits32Unknown (n); } return true; } // Load Register (immediate) calculates an address from a base register value and // an immediate offset, loads a word from memory, and writes to a register. // LDR (immediate, Thumb) bool EmulateInstructionARM::EmulateLDRRtRnImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if (ConditionPassed()) { EncodingSpecificOperations(); NullCheckIfThumbEE(15); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; data = MemU[address,4]; if wback then R[n] = offset_addr; if t == 15 then if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; elsif UnalignedSupport() || address<1:0> = '00' then R[t] = data; else R[t] = bits(32) UNKNOWN; // Can only apply before ARMv7 } #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rt; // the destination register uint32_t Rn; // the base register uint32_t imm32; // the immediate offset used to form the address addr_t offset_addr; // the offset address addr_t address; // the calculated address uint32_t data; // the literal data value from memory load bool add, index, wback; switch (encoding) { case eEncodingT1: Rt = Bits32(opcode, 2, 0); Rn = Bits32(opcode, 5, 3); imm32 = Bits32(opcode, 10, 6) << 2; // imm32 = ZeroExtend(imm5:'00', 32); // index = TRUE; add = TRUE; wback = FALSE add = true; index = true; wback = false; break; case eEncodingT2: // t = UInt(Rt); n = 13; imm32 = ZeroExtend(imm8:'00', 32); Rt = Bits32 (opcode, 10, 8); Rn = 13; imm32 = Bits32 (opcode, 7, 0) << 2; // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; break; case eEncodingT3: // if Rn == '1111' then SEE LDR (literal); // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); Rt = Bits32 (opcode, 15, 12); Rn = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 11, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // if t == 15 && InITBlock() && !LastInITBlock() then UNPREDICTABLE; if ((Rt == 15) && InITBlock() && !LastInITBlock()) return false; break; case eEncodingT4: // if Rn == '1111' then SEE LDR (literal); // if P == '1' && U == '1' && W == '0' then SEE LDRT; // if Rn == '1101' && P == '0' && U == '1' && W == '1' && imm8 == '00000100' then SEE POP; // if P == '0' && W == '0' then UNDEFINED; if (BitIsClear (opcode, 10) && BitIsClear (opcode, 8)) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); Rt = Bits32 (opcode, 15, 12); Rn = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 7, 0); // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet (opcode, 10); add = BitIsSet (opcode, 9); wback = BitIsSet (opcode, 8); // if (wback && n == t) || (t == 15 && InITBlock() && !LastInITBlock()) then UNPREDICTABLE; if ((wback && (Rn == Rt)) || ((Rt == 15) && InITBlock() && !LastInITBlock())) return false; break; default: return false; } uint32_t base = ReadCoreReg (Rn, &success); if (!success) return false; if (add) offset_addr = base + imm32; else offset_addr = base - imm32; address = (index ? offset_addr : base); RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + Rn, base_reg); if (wback) { EmulateInstruction::Context ctx; ctx.type = EmulateInstruction::eContextAdjustBaseRegister; ctx.SetRegisterPlusOffset (base_reg, (int32_t) (offset_addr - base)); if (!WriteRegisterUnsigned (ctx, eRegisterKindDWARF, dwarf_r0 + Rn, offset_addr)) return false; } // Prepare to write to the Rt register. EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, (int32_t) (offset_addr - base)); // Read memory from the address. data = MemURead(context, address, 4, 0, &success); if (!success) return false; if (Rt == 15) { if (Bits32(address, 1, 0) == 0) { if (!LoadWritePC(context, data)) return false; } else return false; } else if (UnalignedSupport() || Bits32(address, 1, 0) == 0) { if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + Rt, data)) return false; } else WriteBits32Unknown (Rt); } return true; } // STM (Store Multiple Increment After) stores multiple registers to consecutive memory locations using an address // from a base register. The consecutive memory locations start at this address, and teh address just above the last // of those locations can optionally be written back to the base register. bool EmulateInstructionARM::EmulateSTM (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); address = R[n]; for i = 0 to 14 if registers == '1' then if i == n && wback && i != LowestSetBit(registers) then MemA[address,4] = bits(32) UNKNOWN; // Only possible for encodings T1 and A1 else MemA[address,4] = R[i]; address = address + 4; if registers<15> == '1' then // Only possible for encoding A1 MemA[address,4] = PCStoreValue(); if wback then R[n] = R[n] + 4*BitCount(registers); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // n = UInt(Rn); registers = '00000000':register_list; wback = TRUE; n = Bits32 (opcode, 10, 8); registers = Bits32 (opcode, 7, 0); registers = registers & 0x00ff; // Make sure the top 8 bits are zeros. wback = true; // if BitCount(registers) < 1 then UNPREDICTABLE; if (BitCount (registers) < 1) return false; break; case eEncodingT2: // n = UInt(Rn); registers = '0':M:'0':register_list; wback = (W == '1'); n = Bits32 (opcode, 19, 16); registers = Bits32 (opcode, 15, 0); registers = registers & 0x5fff; // Make sure bits 15 & 13 are zeros. wback = BitIsSet (opcode, 21); // if n == 15 || BitCount(registers) < 2 then UNPREDICTABLE; if ((n == 15) || (BitCount (registers) < 2)) return false; // if wback && registers == '1' then UNPREDICTABLE; if (wback && BitIsSet (registers, n)) return false; break; case eEncodingA1: // n = UInt(Rn); registers = register_list; wback = (W == '1'); n = Bits32 (opcode, 19, 16); registers = Bits32 (opcode, 15, 0); wback = BitIsSet (opcode, 21); // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; if ((n == 15) || (BitCount (registers) < 1)) return false; break; default: return false; } // address = R[n]; int32_t offset = 0; const addr_t address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterStore; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); // for i = 0 to 14 uint32_t lowest_set_bit = 14; for (uint32_t i = 0; i < 14; ++i) { // if registers == '1' then if (BitIsSet (registers, i)) { if (i < lowest_set_bit) lowest_set_bit = i; // if i == n && wback && i != LowestSetBit(registers) then if ((i == n) && wback && (i != lowest_set_bit)) // MemA[address,4] = bits(32) UNKNOWN; // Only possible for encodings T1 and A1 WriteBits32UnknownToMemory (address + offset); else { // MemA[address,4] = R[i]; uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + i, 0, &success); if (!success) return false; RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg); context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, offset); if (!MemAWrite (context, address + offset, data, addr_byte_size)) return false; } // address = address + 4; offset += addr_byte_size; } } // if registers<15> == '1' then // Only possible for encoding A1 // MemA[address,4] = PCStoreValue(); if (BitIsSet (registers, 15)) { RegisterInfo pc_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg); context.SetRegisterPlusOffset (pc_reg, 8); const uint32_t pc = ReadCoreReg (PC_REG, &success); if (!success) return false; if (!MemAWrite (context, address + offset, pc, addr_byte_size)) return false; } // if wback then R[n] = R[n] + 4*BitCount(registers); if (wback) { offset = addr_byte_size * BitCount (registers); context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetImmediateSigned (offset); addr_t data = address + offset; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data)) return false; } } return true; } // STMDA (Store Multiple Decrement After) stores multiple registers to consecutive memory locations using an address // from a base register. The consecutive memory locations end at this address, and the address just below the lowest // of those locations can optionally be written back to the base register. bool EmulateInstructionARM::EmulateSTMDA (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); address = R[n] - 4*BitCount(registers) + 4; for i = 0 to 14 if registers == '1' then if i == n && wback && i != LowestSetBit(registers) then MemA[address,4] = bits(32) UNKNOWN; else MemA[address,4] = R[i]; address = address + 4; if registers<15> == '1' then MemA[address,4] = PCStoreValue(); if wback then R[n] = R[n] - 4*BitCount(registers); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); // EncodingSpecificOperations(); switch (encoding) { case eEncodingA1: // n = UInt(Rn); registers = register_list; wback = (W == '1'); n = Bits32 (opcode, 19, 16); registers = Bits32 (opcode, 15, 0); wback = BitIsSet (opcode, 21); // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; if ((n == 15) || (BitCount (registers) < 1)) return false; break; default: return false; } // address = R[n] - 4*BitCount(registers) + 4; int32_t offset = 0; addr_t Rn = ReadCoreReg (n, &success); if (!success) return false; addr_t address = Rn - (addr_byte_size * BitCount (registers)) + 4; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterStore; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); // for i = 0 to 14 uint32_t lowest_bit_set = 14; for (uint32_t i = 0; i < 14; ++i) { // if registers == '1' then if (BitIsSet (registers, i)) { if (i < lowest_bit_set) lowest_bit_set = i; //if i == n && wback && i != LowestSetBit(registers) then if ((i == n) && wback && (i != lowest_bit_set)) // MemA[address,4] = bits(32) UNKNOWN; WriteBits32UnknownToMemory (address + offset); else { // MemA[address,4] = R[i]; uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + i, 0, &success); if (!success) return false; RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg); context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, Rn - (address + offset)); if (!MemAWrite (context, address + offset, data, addr_byte_size)) return false; } // address = address + 4; offset += addr_byte_size; } } // if registers<15> == '1' then // MemA[address,4] = PCStoreValue(); if (BitIsSet (registers, 15)) { RegisterInfo pc_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg); context.SetRegisterPlusOffset (pc_reg, 8); const uint32_t pc = ReadCoreReg (PC_REG, &success); if (!success) return false; if (!MemAWrite (context, address + offset, pc, addr_byte_size)) return false; } // if wback then R[n] = R[n] - 4*BitCount(registers); if (wback) { offset = (addr_byte_size * BitCount (registers)) * -1; context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetImmediateSigned (offset); addr_t data = Rn + offset; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data)) return false; } } return true; } // STMDB (Store Multiple Decrement Before) stores multiple registers to consecutive memory locations using an address // from a base register. The consecutive memory locations end just below this address, and the address of the first of // those locations can optionally be written back to the base register. bool EmulateInstructionARM::EmulateSTMDB (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); address = R[n] - 4*BitCount(registers); for i = 0 to 14 if registers == '1' then if i == n && wback && i != LowestSetBit(registers) then MemA[address,4] = bits(32) UNKNOWN; // Only possible for encoding A1 else MemA[address,4] = R[i]; address = address + 4; if registers<15> == '1' then // Only possible for encoding A1 MemA[address,4] = PCStoreValue(); if wback then R[n] = R[n] - 4*BitCount(registers); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // if W == '1' && Rn == '1101' then SEE PUSH; if ((BitIsSet (opcode, 21)) && (Bits32 (opcode, 19, 16) == 13)) { // See PUSH } // n = UInt(Rn); registers = '0':M:'0':register_list; wback = (W == '1'); n = Bits32 (opcode, 19, 16); registers = Bits32 (opcode, 15, 0); registers = registers & 0x5fff; // Make sure bits 15 & 13 are zeros. wback = BitIsSet (opcode, 21); // if n == 15 || BitCount(registers) < 2 then UNPREDICTABLE; if ((n == 15) || BitCount (registers) < 2) return false; // if wback && registers == '1' then UNPREDICTABLE; if (wback && BitIsSet (registers, n)) return false; break; case eEncodingA1: // if W == '1' && Rn == '1101Õ && BitCount(register_list) >= 2 then SEE PUSH; if (BitIsSet (opcode, 21) && (Bits32 (opcode, 19, 16) == 13) && BitCount (Bits32 (opcode, 15, 0)) >= 2) { // See Push } // n = UInt(Rn); registers = register_list; wback = (W == '1'); n = Bits32 (opcode, 19, 16); registers = Bits32 (opcode, 15, 0); wback = BitIsSet (opcode, 21); // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; if ((n == 15) || BitCount (registers) < 1) return false; break; default: return false; } // address = R[n] - 4*BitCount(registers); int32_t offset = 0; addr_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t address = Rn - (addr_byte_size * BitCount (registers)); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterStore; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); // for i = 0 to 14 uint32_t lowest_set_bit = 14; for (uint32_t i = 0; i < 14; ++i) { // if registers == '1' then if (BitIsSet (registers, i)) { if (i < lowest_set_bit) lowest_set_bit = i; // if i == n && wback && i != LowestSetBit(registers) then if ((i == n) && wback && (i != lowest_set_bit)) // MemA[address,4] = bits(32) UNKNOWN; // Only possible for encoding A1 WriteBits32UnknownToMemory (address + offset); else { // MemA[address,4] = R[i]; uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + i, 0, &success); if (!success) return false; RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg); context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, Rn - (address + offset)); if (!MemAWrite (context, address + offset, data, addr_byte_size)) return false; } // address = address + 4; offset += addr_byte_size; } } // if registers<15> == '1' then // Only possible for encoding A1 // MemA[address,4] = PCStoreValue(); if (BitIsSet (registers, 15)) { RegisterInfo pc_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg); context.SetRegisterPlusOffset (pc_reg, 8); const uint32_t pc = ReadCoreReg (PC_REG, &success); if (!success) return false; if (!MemAWrite (context, address + offset, pc, addr_byte_size)) return false; } // if wback then R[n] = R[n] - 4*BitCount(registers); if (wback) { offset = (addr_byte_size * BitCount (registers)) * -1; context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetImmediateSigned (offset); addr_t data = Rn + offset; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data)) return false; } } return true; } // STMIB (Store Multiple Increment Before) stores multiple registers to consecutive memory locations using an address // from a base register. The consecutive memory locations start just above this address, and the address of the last // of those locations can optionally be written back to the base register. bool EmulateInstructionARM::EmulateSTMIB (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); address = R[n] + 4; for i = 0 to 14 if registers == '1' then if i == n && wback && i != LowestSetBit(registers) then MemA[address,4] = bits(32) UNKNOWN; else MemA[address,4] = R[i]; address = address + 4; if registers<15> == '1' then MemA[address,4] = PCStoreValue(); if wback then R[n] = R[n] + 4*BitCount(registers); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; uint32_t registers = 0; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); // EncodingSpecificOperations(); switch (encoding) { case eEncodingA1: // n = UInt(Rn); registers = register_list; wback = (W == '1'); n = Bits32 (opcode, 19, 16); registers = Bits32 (opcode, 15, 0); wback = BitIsSet (opcode, 21); // if n == 15 || BitCount(registers) < 1 then UNPREDICTABLE; if ((n == 15) && (BitCount (registers) < 1)) return false; break; default: return false; } // address = R[n] + 4; int32_t offset = 0; addr_t Rn = ReadCoreReg (n, &success); if (!success) return false; addr_t address = Rn + addr_byte_size; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextRegisterStore; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); uint32_t lowest_set_bit = 14; // for i = 0 to 14 for (uint32_t i = 0; i < 14; ++i) { // if registers == '1' then if (BitIsSet (registers, i)) { if (i < lowest_set_bit) lowest_set_bit = i; // if i == n && wback && i != LowestSetBit(registers) then if ((i == n) && wback && (i != lowest_set_bit)) // MemA[address,4] = bits(32) UNKNOWN; WriteBits32UnknownToMemory (address + offset); // else else { // MemA[address,4] = R[i]; uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + i, 0, &success); if (!success) return false; RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + i, data_reg); context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, offset + addr_byte_size); if (!MemAWrite (context, address + offset, data, addr_byte_size)) return false; } // address = address + 4; offset += addr_byte_size; } } // if registers<15> == '1' then // MemA[address,4] = PCStoreValue(); if (BitIsSet (registers, 15)) { RegisterInfo pc_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_pc, pc_reg); context.SetRegisterPlusOffset (pc_reg, 8); const uint32_t pc = ReadCoreReg (PC_REG, &success); if (!success) return false; if (!MemAWrite (context, address + offset, pc, addr_byte_size)) return false; } // if wback then R[n] = R[n] + 4*BitCount(registers); if (wback) { offset = addr_byte_size * BitCount (registers); context.type = EmulateInstruction::eContextAdjustBaseRegister; context.SetImmediateSigned (offset); addr_t data = Rn + offset; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, data)) return false; } } return true; } // STR (store immediate) calcualtes an address from a base register value and an immediate offset, and stores a word // from a register to memory. It can use offset, post-indexed, or pre-indexed addressing. bool EmulateInstructionARM::EmulateSTRThumb (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; if UnalignedSupport() || address<1:0> == '00' then MemU[address,4] = R[t]; else // Can only occur before ARMv7 MemU[address,4] = bits(32) UNKNOWN; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; // EncodingSpecificOperations (); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5:'00', 32); t = Bits32 (opcode, 2, 0); n = Bits32 (opcode, 5, 3); imm32 = Bits32 (opcode, 10, 6) << 2; // index = TRUE; add = TRUE; wback = FALSE; index = true; add = false; wback = false; break; case eEncodingT2: // t = UInt(Rt); n = 13; imm32 = ZeroExtend(imm8:'00', 32); t = Bits32 (opcode, 10, 8); n = 13; imm32 = Bits32 (opcode, 7, 0) << 2; // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; break; case eEncodingT3: // if Rn == '1111' then UNDEFINED; if (Bits32 (opcode, 19, 16) == 15) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 11, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // if t == 15 then UNPREDICTABLE; if (t == 15) return false; break; case eEncodingT4: // if P == '1' && U == '1' && W == '0' then SEE STRT; // if Rn == '1101' && P == '1' && U == '0' && W == '1' && imm8 == '00000100' then SEE PUSH; // if Rn == '1111' || (P == '0' && W == '0') then UNDEFINED; if ((Bits32 (opcode, 19, 16) == 15) || (BitIsClear (opcode, 10) && BitIsClear (opcode, 8))) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 7, 0); // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet (opcode, 10); add = BitIsSet (opcode, 9); wback = BitIsSet (opcode, 8); // if t == 15 || (wback && n == t) then UNPREDICTABLE; if ((t == 15) || (wback && (n == t))) return false; break; default: return false; } addr_t offset_addr; addr_t address; // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); uint32_t base_address = ReadCoreReg (n, &success); if (!success) return false; if (add) offset_addr = base_address + imm32; else offset_addr = base_address - imm32; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = base_address; EmulateInstruction::Context context; context.type = eContextRegisterStore; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); // if UnalignedSupport() || address<1:0> == '00' then if (UnalignedSupport () || (BitIsClear (address, 1) && BitIsClear (address, 0))) { // MemU[address,4] = R[t]; uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + t, 0, &success); if (!success) return false; RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); int32_t offset = address - base_address; context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, offset); if (!MemUWrite (context, address, data, addr_byte_size)) return false; } else { // MemU[address,4] = bits(32) UNKNOWN; WriteBits32UnknownToMemory (address); } // if wback then R[n] = offset_addr; if (wback) { context.type = eContextRegisterLoad; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // STR (Store Register) calculates an address from a base register value and an offset register value, stores a // word from a register to memory. The offset register value can optionally be shifted. bool EmulateInstructionARM::EmulateSTRRegister (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset = Shift(R[m], shift_t, shift_n, APSR.C); offset_addr = if add then (R[n] + offset) else (R[n] - offset); address = if index then offset_addr else R[n]; if t == 15 then // Only possible for encoding A1 data = PCStoreValue(); else data = R[t]; if UnalignedSupport() || address<1:0> == '00' || CurrentInstrSet() == InstrSet_ARM then MemU[address,4] = data; else // Can only occur before ARMv7 MemU[address,4] = bits(32) UNKNOWN; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t t; uint32_t n; uint32_t m; ARM_ShifterType shift_t; uint32_t shift_n; bool index; bool add; bool wback; // EncodingSpecificOperations (); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation in ThumbEE"; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 2, 0); n = Bits32 (opcode, 5, 3); m = Bits32 (opcode, 8, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // if Rn == '1111' then UNDEFINED; if (Bits32 (opcode, 19, 16) == 15) return false; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); shift_t = SRType_LSL; shift_n = Bits32 (opcode, 5, 4); // if t == 15 || BadReg(m) then UNPREDICTABLE; if ((t == 15) || (BadReg (m))) return false; break; case eEncodingA1: { // if P == '0' && W == '1' then SEE STRT; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); // (shift_t, shift_n) = DecodeImmShift(type, imm5); uint32_t typ = Bits32 (opcode, 6, 5); uint32_t imm5 = Bits32 (opcode, 11, 7); shift_n = DecodeImmShift(typ, imm5, shift_t); // if m == 15 then UNPREDICTABLE; if (m == 15) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; break; } default: return false; } addr_t offset_addr; addr_t address; int32_t offset = 0; addr_t base_address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; uint32_t Rm_data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; // offset = Shift(R[m], shift_t, shift_n, APSR.C); offset = Shift (Rm_data, shift_t, shift_n, APSR_C, &success); if (!success) return false; // offset_addr = if add then (R[n] + offset) else (R[n] - offset); if (add) offset_addr = base_address + offset; else offset_addr = base_address - offset; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = base_address; uint32_t data; // if t == 15 then // Only possible for encoding A1 if (t == 15) // data = PCStoreValue(); data = ReadCoreReg (PC_REG, &success); else // data = R[t]; data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + t, 0, &success); if (!success) return false; EmulateInstruction::Context context; context.type = eContextRegisterStore; // if UnalignedSupport() || address<1:0> == '00' || CurrentInstrSet() == InstrSet_ARM then if (UnalignedSupport () || (BitIsClear (address, 1) && BitIsClear (address, 0)) || CurrentInstrSet() == eModeARM) { // MemU[address,4] = data; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - base_address); if (!MemUWrite (context, address, data, addr_byte_size)) return false; } else // MemU[address,4] = bits(32) UNKNOWN; WriteBits32UnknownToMemory (address); // if wback then R[n] = offset_addr; if (wback) { context.type = eContextRegisterLoad; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } bool EmulateInstructionARM::EmulateSTRBThumb (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; MemU[address,1] = R[t]<7:0>; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5, 32); t = Bits32 (opcode, 2, 0); n = Bits32 (opcode, 5, 3); imm32 = Bits32 (opcode, 10, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; break; case eEncodingT2: // if Rn == '1111' then UNDEFINED; if (Bits32 (opcode, 19, 16) == 15) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 11, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // if BadReg(t) then UNPREDICTABLE; if (BadReg (t)) return false; break; case eEncodingT3: // if P == '1' && U == '1' && W == '0' then SEE STRBT; // if Rn == '1111' || (P == '0' && W == '0') then UNDEFINED; if (Bits32 (opcode, 19, 16) == 15) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 7, 0); // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet (opcode, 10); add = BitIsSet (opcode, 9); wback = BitIsSet (opcode, 8); // if BadReg(t) || (wback && n == t) then UNPREDICTABLE if ((BadReg (t)) || (wback && (n == t))) return false; break; default: return false; } addr_t offset_addr; addr_t address; addr_t base_address = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); if (add) offset_addr = base_address + imm32; else offset_addr = base_address - imm32; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = base_address; // MemU[address,1] = R[t]<7:0> RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); EmulateInstruction::Context context; context.type = eContextRegisterStore; context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - base_address); uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + t, 0, &success); if (!success) return false; data = Bits32 (data, 7, 0); if (!MemUWrite (context, address, data, 1)) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextRegisterLoad; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // STRH (register) calculates an address from a base register value and an offset register value, and stores a // halfword from a register to memory. The offset register alue can be shifted left by 0, 1, 2, or 3 bits. bool EmulateInstructionARM::EmulateSTRHRegister (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset = Shift(R[m], shift_t, shift_n, APSR.C); offset_addr = if add then (R[n] + offset) else (R[n] - offset); address = if index then offset_addr else R[n]; if UnalignedSupport() || address<0> == '0' then MemU[address,2] = R[t]<15:0>; else // Can only occur before ARMv7 MemU[address,2] = bits(16) UNKNOWN; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t m; bool index; bool add; bool wback; ARM_ShifterType shift_t; uint32_t shift_n; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation in ThumbEE"; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 2, 0); n = Bits32 (opcode, 5, 3); m = Bits32 (opcode, 8, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // if Rn == '1111' then UNDEFINED; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); if (n == 15) return false; // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); shift_t = SRType_LSL; shift_n = Bits32 (opcode, 5, 4); // if BadReg(t) || BadReg(m) then UNPREDICTABLE; if (BadReg (t) || BadReg (m)) return false; break; case eEncodingA1: // if P == '0' && W == '1' then SEE STRHT; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; // if t == 15 || m == 15 then UNPREDICTABLE; if ((t == 15) || (m == 15)) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; break; default: return false; } uint32_t Rm = ReadCoreReg (m, &success); if (!success) return false; uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; // offset = Shift(R[m], shift_t, shift_n, APSR.C); uint32_t offset = Shift (Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; // offset_addr = if add then (R[n] + offset) else (R[n] - offset); addr_t offset_addr; if (add) offset_addr = Rn + offset; else offset_addr = Rn - offset; // address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; EmulateInstruction::Context context; context.type = eContextRegisterStore; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); RegisterInfo offset_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); // if UnalignedSupport() || address<0> == '0' then if (UnalignedSupport() || BitIsClear (address, 0)) { // MemU[address,2] = R[t]<15:0>; uint32_t Rt = ReadCoreReg (t, &success); if (!success) return false; EmulateInstruction::Context context; context.type = eContextRegisterStore; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); RegisterInfo offset_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); context.SetRegisterToRegisterPlusIndirectOffset (base_reg, offset_reg, data_reg); if (!MemUWrite (context, address, Bits32 (Rt, 15, 0), 2)) return false; } else // Can only occur before ARMv7 { // MemU[address,2] = bits(16) UNKNOWN; } // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // Add with Carry (immediate) adds an immediate value and the carry flag value to a register value, // and writes the result to the destination register. It can optionally update the condition flags // based on the result. bool EmulateInstructionARM::EmulateADCImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], imm32, APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn; uint32_t imm32; // the immediate value to be added to the value obtained from Rn bool setflags; switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) if (BadReg(Rd) || BadReg(Rn)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the first operand. int32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(val1, imm32, APSR_C); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; } return true; } // Add with Carry (register) adds a register value, the carry flag value, and an optionally-shifted // register value, and writes the result to the destination register. It can optionally update the // condition flags based on the result. bool EmulateInstructionARM::EmulateADCReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(R[n], shifted, APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm bool setflags; switch (encoding) { case eEncodingT1: Rd = Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the first operand. int32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. int32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(val1, shifted, APSR_C); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; } return true; } // This instruction adds an immediate value to the PC value to form a PC-relative address, // and writes the result to the destination register. bool EmulateInstructionARM::EmulateADR (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); result = if add then (Align(PC,4) + imm32) else (Align(PC,4) - imm32); if d == 15 then // Can only occur for ARM encodings ALUWritePC(result); else R[d] = result; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd; uint32_t imm32; // the immediate value to be added/subtracted to/from the PC bool add; switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 10, 8); imm32 = ThumbImm8Scaled(opcode); // imm32 = ZeroExtend(imm8:'00', 32) add = true; break; case eEncodingT2: case eEncodingT3: Rd = Bits32(opcode, 11, 8); imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32) add = (Bits32(opcode, 24, 21) == 0); // 0b0000 => ADD; 0b0101 => SUB if (BadReg(Rd)) return false; break; case eEncodingA1: case eEncodingA2: Rd = Bits32(opcode, 15, 12); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) add = (Bits32(opcode, 24, 21) == 0x4); // 0b0100 => ADD; 0b0010 => SUB break; default: return false; } // Read the PC value. uint32_t pc = ReadCoreReg(PC_REG, &success); if (!success) return false; uint32_t result = (add ? Align(pc, 4) + imm32 : Align(pc, 4) - imm32); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreReg(context, result, Rd)) return false; } return true; } // This instruction performs a bitwise AND of a register value and an immediate value, and writes the result // to the destination register. It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateANDImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); result = R[n] AND imm32; if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn; uint32_t imm32; // the immediate value to be ANDed to the value obtained from Rn bool setflags; uint32_t carry; // the carry bit after ARM/Thumb Expand operation switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) // if Rd == '1111' && S == '1' then SEE TST (immediate); if (Rd == 15 && setflags) return EmulateTSTImm(opcode, eEncodingT1); if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; uint32_t result = val1 & imm32; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // This instruction performs a bitwise AND of a register value and an optionally-shifted register value, // and writes the result to the destination register. It can optionally update the condition flags // based on the result. bool EmulateInstructionARM::EmulateANDReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); result = R[n] AND shifted; if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm bool setflags; uint32_t carry; switch (encoding) { case eEncodingT1: Rd = Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); // if Rd == '1111' && S == '1' then SEE TST (register); if (Rd == 15 && setflags) return EmulateTSTReg(opcode, eEncodingT2); if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); if (!success) return false; uint32_t result = val1 & shifted; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // Bitwise Bit Clear (immediate) performs a bitwise AND of a register value and the complement of an // immediate value, and writes the result to the destination register. It can optionally update the // condition flags based on the result. bool EmulateInstructionARM::EmulateBICImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); result = R[n] AND NOT(imm32); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn; uint32_t imm32; // the immediate value to be bitwise inverted and ANDed to the value obtained from Rn bool setflags; uint32_t carry; // the carry bit after ARM/Thumb Expand operation switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) if (BadReg(Rd) || BadReg(Rn)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; uint32_t result = val1 & ~imm32; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // Bitwise Bit Clear (register) performs a bitwise AND of a register value and the complement of an // optionally-shifted register value, and writes the result to the destination register. // It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateBICReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); result = R[n] AND NOT(shifted); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm bool setflags; uint32_t carry; switch (encoding) { case eEncodingT1: Rd = Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); if (!success) return false; uint32_t result = val1 & ~shifted; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // LDR (immediate, ARM) calculates an address from a base register value and an immediate offset, loads a word // from memory, and writes it to a register. It can use offset, post-indexed, or pre-indexed addressing. bool EmulateInstructionARM::EmulateLDRImmediateARM (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; data = MemU[address,4]; if wback then R[n] = offset_addr; if t == 15 then if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; elsif UnalignedSupport() || address<1:0> = '00' then R[t] = data; else // Can only apply before ARMv7 R[t] = ROR(data, 8*UInt(address<1:0>)); #endif bool success = false; if (ConditionPassed(opcode)) { const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; switch (encoding) { case eEncodingA1: // if Rn == '1111' then SEE LDR (literal); // if P == '0' && W == '1' then SEE LDRT; // if Rn == '1101' && P == '0' && U == '1' && W == '0' && imm12 == '000000000100' then SEE POP; // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 11, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); // if wback && n == t then UNPREDICTABLE; if (wback && (n == t)) return false; break; default: return false; } addr_t address; addr_t offset_addr; addr_t base_address = ReadCoreReg (n, &success); if (!success) return false; // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); if (add) offset_addr = base_address + imm32; else offset_addr = base_address - imm32; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = base_address; // data = MemU[address,4]; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - base_address); uint64_t data = MemURead (context, address, addr_byte_size, 0, &success); if (!success) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } // if t == 15 then if (t == 15) { // if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; if (BitIsClear (address, 1) && BitIsClear (address, 0)) { // LoadWritePC (data); context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - base_address); LoadWritePC (context, data); } else return false; } // elsif UnalignedSupport() || address<1:0> = '00' then else if (UnalignedSupport() || (BitIsClear (address, 1) && BitIsClear (address, 0))) { // R[t] = data; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - base_address); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } // else // Can only apply before ARMv7 else { // R[t] = ROR(data, 8*UInt(address<1:0>)); data = ROR (data, Bits32 (address, 1, 0), &success); if (!success) return false; context.type = eContextRegisterLoad; context.SetImmediate (data); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } } return true; } // LDR (register) calculates an address from a base register value and an offset register value, loads a word // from memory, and writes it to a resgister. The offset register value can optionally be shifted. bool EmulateInstructionARM::EmulateLDRRegister (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset = Shift(R[m], shift_t, shift_n, APSR.C); offset_addr = if add then (R[n] + offset) else (R[n] - offset); address = if index then offset_addr else R[n]; data = MemU[address,4]; if wback then R[n] = offset_addr; if t == 15 then if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; elsif UnalignedSupport() || address<1:0> = '00' then R[t] = data; else // Can only apply before ARMv7 if CurrentInstrSet() == InstrSet_ARM then R[t] = ROR(data, 8*UInt(address<1:0>)); else R[t] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t t; uint32_t n; uint32_t m; bool index; bool add; bool wback; ARM_ShifterType shift_t; uint32_t shift_n; switch (encoding) { case eEncodingT1: // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation in ThumbEE"; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 2, 0); n = Bits32 (opcode, 5, 3); m = Bits32 (opcode, 8, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // if Rn == '1111' then SEE LDR (literal); // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); shift_t = SRType_LSL; shift_n = Bits32 (opcode, 5, 4); // if BadReg(m) then UNPREDICTABLE; if (BadReg (m)) return false; // if t == 15 && InITBlock() && !LastInITBlock() then UNPREDICTABLE; if ((t == 15) && InITBlock() && !LastInITBlock()) return false; break; case eEncodingA1: { // if P == '0' && W == '1' then SEE LDRT; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); // (shift_t, shift_n) = DecodeImmShift(type, imm5); uint32_t type = Bits32 (opcode, 6, 5); uint32_t imm5 = Bits32 (opcode, 11, 7); shift_n = DecodeImmShift (type, imm5, shift_t); // if m == 15 then UNPREDICTABLE; if (m == 15) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; } break; default: return false; } uint32_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; uint32_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t offset_addr; addr_t address; // offset = Shift(R[m], shift_t, shift_n, APSR.C); -- Note "The APSR is an application level alias for the CPSR". addr_t offset = Shift (Rm, shift_t, shift_n, Bit32 (m_opcode_cpsr, APSR_C), &success); if (!success) return false; // offset_addr = if add then (R[n] + offset) else (R[n] - offset); if (add) offset_addr = Rn + offset; else offset_addr = Rn - offset; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // data = MemU[address,4]; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - Rn); uint64_t data = MemURead (context, address, addr_byte_size, 0, &success); if (!success) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } // if t == 15 then if (t == 15) { // if address<1:0> == '00' then LoadWritePC(data); else UNPREDICTABLE; if (BitIsClear (address, 1) && BitIsClear (address, 0)) { context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - Rn); LoadWritePC (context, data); } else return false; } // elsif UnalignedSupport() || address<1:0> = '00' then else if (UnalignedSupport () || (BitIsClear (address, 1) && BitIsClear (address, 0))) { // R[t] = data; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - Rn); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } else // Can only apply before ARMv7 { // if CurrentInstrSet() == InstrSet_ARM then if (CurrentInstrSet () == eModeARM) { // R[t] = ROR(data, 8*UInt(address<1:0>)); data = ROR (data, Bits32 (address, 1, 0), &success); if (!success) return false; context.type = eContextRegisterLoad; context.SetImmediate (data); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } else { // R[t] = bits(32) UNKNOWN; WriteBits32Unknown (t); } } } return true; } // LDRB (immediate, Thumb) bool EmulateInstructionARM::EmulateLDRBImmediate (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; R[t] = ZeroExtend(MemU[address,1], 32); if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5, 32); t = Bits32 (opcode, 2, 0); n = Bits32 (opcode, 5, 3); imm32 = Bits32 (opcode, 10, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback= false; break; case eEncodingT2: // if Rt == '1111' then SEE PLD; // if Rn == '1111' then SEE LDRB (literal); // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 11, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingT3: // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLD; // if Rn == '1111' then SEE LDRB (literal); // if P == '1' && U == '1' && W == '0' then SEE LDRBT; // if P == '0' && W == '0' then UNDEFINED; if (BitIsClear (opcode, 10) && BitIsClear (opcode, 8)) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 7, 0); // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet (opcode, 10); add = BitIsSet (opcode, 9); wback = BitIsSet (opcode, 8); // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; if (BadReg (t) || (wback && (n == t))) return false; break; default: return false; } uint32_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t address; addr_t offset_addr; // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // R[t] = ZeroExtend(MemU[address,1], 32); RegisterInfo base_reg; RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); uint64_t data = MemURead (context, address, 1, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // LDRB (literal) calculates an address from the PC value and an immediate offset, loads a byte from memory, // zero-extends it to form a 32-bit word and writes it to a register. bool EmulateInstructionARM::EmulateLDRBLiteral (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(15); base = Align(PC,4); address = if add then (base + imm32) else (base - imm32); R[t] = ZeroExtend(MemU[address,1], 32); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t imm32; bool add; switch (encoding) { case eEncodingT1: // if Rt == '1111' then SEE PLD; // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); t = Bits32 (opcode, 15, 12); imm32 = Bits32 (opcode, 11, 0); add = BitIsSet (opcode, 23); // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingA1: // t == UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); t = Bits32 (opcode, 15, 12); imm32 = Bits32 (opcode, 11, 0); add = BitIsSet (opcode, 23); // if t == 15 then UNPREDICTABLE; if (t == 15) return false; break; default: return false; } // base = Align(PC,4); uint32_t pc_val = ReadCoreReg (PC_REG, &success); if (!success) return false; uint32_t base = AlignPC (pc_val); addr_t address; // address = if add then (base + imm32) else (base - imm32); if (add) address = base + imm32; else address = base - imm32; // R[t] = ZeroExtend(MemU[address,1], 32); EmulateInstruction::Context context; context.type = eContextRelativeBranchImmediate; context.SetImmediate (address - base); uint64_t data = MemURead (context, address, 1, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } return true; } // LDRB (register) calculates an address from a base register value and an offset rigister value, loads a byte from // memory, zero-extends it to form a 32-bit word, and writes it to a register. The offset register value can // optionally be shifted. bool EmulateInstructionARM::EmulateLDRBRegister (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset = Shift(R[m], shift_t, shift_n, APSR.C); offset_addr = if add then (R[n] + offset) else (R[n] - offset); address = if index then offset_addr else R[n]; R[t] = ZeroExtend(MemU[address,1],32); if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t m; bool index; bool add; bool wback; ARM_ShifterType shift_t; uint32_t shift_n; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 2, 0); n = Bits32 (opcode, 5, 3); m = Bits32 (opcode, 8, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // if Rt == '1111' then SEE PLD; // if Rn == '1111' then SEE LDRB (literal); // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); shift_t = SRType_LSL; shift_n = Bits32 (opcode, 5, 4); // if t == 13 || BadReg(m) then UNPREDICTABLE; if ((t == 13) || BadReg (m)) return false; break; case eEncodingA1: { // if P == '0' && W == '1' then SEE LDRBT; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); // (shift_t, shift_n) = DecodeImmShift(type, imm5); uint32_t type = Bits32 (opcode, 6, 5); uint32_t imm5 = Bits32 (opcode, 11, 7); shift_n = DecodeImmShift (type, imm5, shift_t); // if t == 15 || m == 15 then UNPREDICTABLE; if ((t == 15) || (m == 15)) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; } break; default: return false; } addr_t offset_addr; addr_t address; // offset = Shift(R[m], shift_t, shift_n, APSR.C); uint32_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; addr_t offset = Shift (Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; // offset_addr = if add then (R[n] + offset) else (R[n] - offset); uint32_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; if (add) offset_addr = Rn + offset; else offset_addr = Rn - offset; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // R[t] = ZeroExtend(MemU[address,1],32); RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - Rn); uint64_t data = MemURead (context, address, 1, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // LDRH (immediate, Thumb) calculates an address from a base register value and an immediate offset, loads a // halfword from memory, zero-extends it to form a 32-bit word, and writes it to a register. It can use offset, // post-indexed, or pre-indexed addressing. bool EmulateInstructionARM::EmulateLDRHImmediate (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; data = MemU[address,2]; if wback then R[n] = offset_addr; if UnalignedSupport() || address<0> = '0' then R[t] = ZeroExtend(data, 32); else // Can only apply before ARMv7 R[t] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm5:'0', 32); t = Bits32 (opcode, 2, 0); n = Bits32 (opcode, 5, 3); imm32 = Bits32 (opcode, 10, 6) << 1; // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; break; case eEncodingT2: // if Rt == '1111' then SEE "Unallocated memory hints"; // if Rn == '1111' then SEE LDRH (literal); // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 11, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingT3: // if Rn == '1111' then SEE LDRH (literal); // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE "Unallocated memory hints"; // if P == '1' && U == '1' && W == '0' then SEE LDRHT; // if P == '0' && W == '0' then UNDEFINED; if (BitIsClear (opcode, 10) && BitIsClear (opcode, 8)) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 7, 0); // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet (opcode, 10); add = BitIsSet (opcode, 9); wback = BitIsSet (opcode, 8); // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; if (BadReg (t) || (wback && (n == t))) return false; break; default: return false; } // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); uint32_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t offset_addr; addr_t address; if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // data = MemU[address,2]; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - Rn); uint64_t data = MemURead (context, address, 2, 0, &success); if (!success) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } // if UnalignedSupport() || address<0> = '0' then if (UnalignedSupport () || BitIsClear (address, 0)) { // R[t] = ZeroExtend(data, 32); context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - Rn); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } else // Can only apply before ARMv7 { // R[t] = bits(32) UNKNOWN; WriteBits32Unknown (t); } } return true; } // LDRH (literal) caculates an address from the PC value and an immediate offset, loads a halfword from memory, // zero-extends it to form a 32-bit word, and writes it to a register. bool EmulateInstructionARM::EmulateLDRHLiteral (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(15); base = Align(PC,4); address = if add then (base + imm32) else (base - imm32); data = MemU[address,2]; if UnalignedSupport() || address<0> = '0' then R[t] = ZeroExtend(data, 32); else // Can only apply before ARMv7 R[t] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t imm32; bool add; // EncodingSpecificOperations(); NullCheckIfThumbEE(15); switch (encoding) { case eEncodingT1: // if Rt == '1111' then SEE "Unallocated memory hints"; // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); t = Bits32 (opcode, 15, 12); imm32 = Bits32 (opcode, 11, 0); add = BitIsSet (opcode, 23); // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingA1: { uint32_t imm4H = Bits32 (opcode, 11, 8); uint32_t imm4L = Bits32 (opcode, 3, 0); // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1'); t = Bits32 (opcode, 15, 12); imm32 = (imm4H << 4) | imm4L; add = BitIsSet (opcode, 23); // if t == 15 then UNPREDICTABLE; if (t == 15) return false; break; } default: return false; } // base = Align(PC,4); uint64_t pc_value = ReadCoreReg (PC_REG, &success); if (!success) return false; addr_t base = AlignPC (pc_value); addr_t address; // address = if add then (base + imm32) else (base - imm32); if (add) address = base + imm32; else address = base - imm32; // data = MemU[address,2]; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - base); uint64_t data = MemURead (context, address, 2, 0, &success); if (!success) return false; // if UnalignedSupport() || address<0> = '0' then if (UnalignedSupport () || BitIsClear (address, 0)) { // R[t] = ZeroExtend(data, 32); context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - base); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } else // Can only apply before ARMv7 { // R[t] = bits(32) UNKNOWN; WriteBits32Unknown (t); } } return true; } // LDRH (literal) calculates an address from a base register value and an offset register value, loads a halfword // from memory, zero-extends it to form a 32-bit word, and writes it to a register. The offset register value can // be shifted left by 0, 1, 2, or 3 bits. bool EmulateInstructionARM::EmulateLDRHRegister (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset = Shift(R[m], shift_t, shift_n, APSR.C); offset_addr = if add then (R[n] + offset) else (R[n] - offset); address = if index then offset_addr else R[n]; data = MemU[address,2]; if wback then R[n] = offset_addr; if UnalignedSupport() || address<0> = '0' then R[t] = ZeroExtend(data, 32); else // Can only apply before ARMv7 R[t] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t m; bool index; bool add; bool wback; ARM_ShifterType shift_t; uint32_t shift_n; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation in ThumbEE"; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 2, 0); n = Bits32 (opcode, 5, 3); m = Bits32 (opcode, 8, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // if Rn == '1111' then SEE LDRH (literal); // if Rt == '1111' then SEE "Unallocated memory hints"; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); shift_t = SRType_LSL; shift_n = Bits32 (opcode, 5, 4); // if t == 13 || BadReg(m) then UNPREDICTABLE; if ((t == 13) || BadReg (m)) return false; break; case eEncodingA1: // if P == '0' && W == '1' then SEE LDRHT; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; // if t == 15 || m == 15 then UNPREDICTABLE; if ((t == 15) || (m == 15)) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; break; default: return false; } // offset = Shift(R[m], shift_t, shift_n, APSR.C); uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; addr_t offset = Shift (Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; addr_t offset_addr; addr_t address; // offset_addr = if add then (R[n] + offset) else (R[n] - offset); uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; if (add) offset_addr = Rn + offset; else offset_addr = Rn - offset; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // data = MemU[address,2]; RegisterInfo base_reg; RegisterInfo offset_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); uint64_t data = MemURead (context, address, 2, 0, &success); if (!success) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } // if UnalignedSupport() || address<0> = '0' then if (UnalignedSupport() || BitIsClear (address, 0)) { // R[t] = ZeroExtend(data, 32); context.type = eContextRegisterLoad; context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; } else // Can only apply before ARMv7 { // R[t] = bits(32) UNKNOWN; WriteBits32Unknown (t); } } return true; } // LDRSB (immediate) calculates an address from a base register value and an immediate offset, loads a byte from // memory, sign-extends it to form a 32-bit word, and writes it to a register. It can use offset, post-indexed, // or pre-indexed addressing. bool EmulateInstructionARM::EmulateLDRSBImmediate (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; R[t] = SignExtend(MemU[address,1], 32); if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // if Rt == '1111' then SEE PLI; // if Rn == '1111' then SEE LDRSB (literal); // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 11, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingT2: // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE PLI; // if Rn == '1111' then SEE LDRSB (literal); // if P == '1' && U == '1' && W == '0' then SEE LDRSBT; // if P == '0' && W == '0' then UNDEFINED; if (BitIsClear (opcode, 10) && BitIsClear (opcode, 8)) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 7, 0); // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet (opcode, 10); add = BitIsSet (opcode, 9); wback = BitIsSet (opcode, 8); // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; if (((t == 13) || ((t == 15) && (BitIsClear (opcode, 10) || BitIsSet (opcode, 9) || BitIsSet (opcode, 8)))) || (wback && (n == t))) return false; break; case eEncodingA1: { // if Rn == '1111' then SEE LDRSB (literal); // if P == '0' && W == '1' then SEE LDRSBT; // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); uint32_t imm4H = Bits32 (opcode, 11, 8); uint32_t imm4L = Bits32 (opcode, 3, 0); imm32 = (imm4H << 4) | imm4L; // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = (BitIsClear (opcode, 24) || BitIsSet (opcode, 21)); // if t == 15 || (wback && n == t) then UNPREDICTABLE; if ((t == 15) || (wback && (n == t))) return false; break; } default: return false; } uint64_t Rn = ReadCoreReg (n, &success); if (!success) return false; addr_t offset_addr; addr_t address; // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // R[t] = SignExtend(MemU[address,1], 32); RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - Rn); uint64_t unsigned_data = MemURead (context, address, 1, 0, &success); if (!success) return false; int64_t signed_data = llvm::SignExtend64<8>(unsigned_data); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // LDRSB (literal) calculates an address from the PC value and an immediate offset, loads a byte from memory, // sign-extends it to form a 32-bit word, and writes tit to a register. bool EmulateInstructionARM::EmulateLDRSBLiteral (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(15); base = Align(PC,4); address = if add then (base + imm32) else (base - imm32); R[t] = SignExtend(MemU[address,1], 32); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t imm32; bool add; // EncodingSpecificOperations(); NullCheckIfThumbEE(15); switch (encoding) { case eEncodingT1: // if Rt == '1111' then SEE PLI; // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); t = Bits32 (opcode, 15, 12); imm32 = Bits32 (opcode, 11, 0); add = BitIsSet (opcode, 23); // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingA1: { // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1'); t = Bits32 (opcode, 15, 12); uint32_t imm4H = Bits32 (opcode, 11, 8); uint32_t imm4L = Bits32 (opcode, 3, 0); imm32 = (imm4H << 4) | imm4L; add = BitIsSet (opcode, 23); // if t == 15 then UNPREDICTABLE; if (t == 15) return false; break; } default: return false; } // base = Align(PC,4); uint64_t pc_value = ReadCoreReg (PC_REG, &success); if (!success) return false; uint64_t base = AlignPC (pc_value); // address = if add then (base + imm32) else (base - imm32); addr_t address; if (add) address = base + imm32; else address = base - imm32; // R[t] = SignExtend(MemU[address,1], 32); RegisterInfo base_reg; GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - base); uint64_t unsigned_data = MemURead (context, address, 1, 0, &success); if (!success) return false; int64_t signed_data = llvm::SignExtend64<8>(unsigned_data); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) return false; } return true; } // LDRSB (register) calculates an address from a base register value and an offset register value, loadsa byte from // memory, sign-extends it to form a 32-bit word, and writes it to a register. The offset register value can be // shifted left by 0, 1, 2, or 3 bits. bool EmulateInstructionARM::EmulateLDRSBRegister (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset = Shift(R[m], shift_t, shift_n, APSR.C); offset_addr = if add then (R[n] + offset) else (R[n] - offset); address = if index then offset_addr else R[n]; R[t] = SignExtend(MemU[address,1], 32); if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t m; bool index; bool add; bool wback; ARM_ShifterType shift_t; uint32_t shift_n; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 2, 0); n = Bits32 (opcode, 5, 3); m = Bits32 (opcode, 8, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // if Rt == '1111' then SEE PLI; // if Rn == '1111' then SEE LDRSB (literal); // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); shift_t = SRType_LSL; shift_n = Bits32 (opcode, 5, 4); // if t == 13 || BadReg(m) then UNPREDICTABLE; if ((t == 13) || BadReg (m)) return false; break; case eEncodingA1: // if P == '0' && W == '1' then SEE LDRSBT; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; // if t == 15 || m == 15 then UNPREDICTABLE; if ((t == 15) || (m == 15)) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; break; default: return false; } uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; // offset = Shift(R[m], shift_t, shift_n, APSR.C); addr_t offset = Shift (Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; addr_t offset_addr; addr_t address; // offset_addr = if add then (R[n] + offset) else (R[n] - offset); uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; if (add) offset_addr = Rn + offset; else offset_addr = Rn - offset; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // R[t] = SignExtend(MemU[address,1], 32); RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); RegisterInfo offset_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); uint64_t unsigned_data = MemURead (context, address, 1, 0, &success); if (!success) return false; int64_t signed_data = llvm::SignExtend64<8>(unsigned_data); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // LDRSH (immediate) calculates an address from a base register value and an immediate offset, loads a halfword from // memory, sign-extends it to form a 32-bit word, and writes it to a register. It can use offset, post-indexed, or // pre-indexed addressing. bool EmulateInstructionARM::EmulateLDRSHImmediate (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; data = MemU[address,2]; if wback then R[n] = offset_addr; if UnalignedSupport() || address<0> = '0' then R[t] = SignExtend(data, 32); else // Can only apply before ARMv7 R[t] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // if Rn == '1111' then SEE LDRSH (literal); // if Rt == '1111' then SEE "Unallocated memory hints"; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 11, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingT2: // if Rn == '1111' then SEE LDRSH (literal); // if Rt == '1111' && P == '1' && U == '0' && W == '0' then SEE "Unallocated memory hints"; // if P == '1' && U == '1' && W == '0' then SEE LDRSHT; // if P == '0' && W == '0' then UNDEFINED; if (BitIsClear (opcode, 10) && BitIsClear (opcode, 8)) return false; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 7, 0); // index = (P == '1'); add = (U == '1'); wback = (W == '1'); index = BitIsSet (opcode, 10); add = BitIsSet (opcode, 9); wback = BitIsSet (opcode, 8); // if BadReg(t) || (wback && n == t) then UNPREDICTABLE; if (BadReg (t) || (wback && (n == t))) return false; break; case eEncodingA1: { // if Rn == '1111' then SEE LDRSH (literal); // if P == '0' && W == '1' then SEE LDRSHT; // t == UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); uint32_t imm4H = Bits32 (opcode, 11,8); uint32_t imm4L = Bits32 (opcode, 3, 0); imm32 = (imm4H << 4) | imm4L; // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); // if t == 15 || (wback && n == t) then UNPREDICTABLE; if ((t == 15) || (wback && (n == t))) return false; break; } default: return false; } // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t offset_addr; if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; // address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; // data = MemU[address,2]; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - Rn); uint64_t data = MemURead (context, address, 2, 0, &success); if (!success) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } // if UnalignedSupport() || address<0> = '0' then if (UnalignedSupport() || BitIsClear (address, 0)) { // R[t] = SignExtend(data, 32); int64_t signed_data = llvm::SignExtend64<16>(data); context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - Rn); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) return false; } else // Can only apply before ARMv7 { // R[t] = bits(32) UNKNOWN; WriteBits32Unknown (t); } } return true; } // LDRSH (literal) calculates an address from the PC value and an immediate offset, loads a halfword from memory, // sign-extends it to from a 32-bit word, and writes it to a register. bool EmulateInstructionARM::EmulateLDRSHLiteral (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(15); base = Align(PC,4); address = if add then (base + imm32) else (base - imm32); data = MemU[address,2]; if UnalignedSupport() || address<0> = '0' then R[t] = SignExtend(data, 32); else // Can only apply before ARMv7 R[t] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t imm32; bool add; // EncodingSpecificOperations(); NullCheckIfThumbEE(15); switch (encoding) { case eEncodingT1: // if Rt == '1111' then SEE "Unallocated memory hints"; // t = UInt(Rt); imm32 = ZeroExtend(imm12, 32); add = (U == '1'); t = Bits32 (opcode, 15, 12); imm32 = Bits32 (opcode, 11, 0); add = BitIsSet (opcode, 23); // if t == 13 then UNPREDICTABLE; if (t == 13) return false; break; case eEncodingA1: { // t == UInt(Rt); imm32 = ZeroExtend(imm4H:imm4L, 32); add = (U == '1'); t = Bits32 (opcode, 15, 12); uint32_t imm4H = Bits32 (opcode, 11, 8); uint32_t imm4L = Bits32 (opcode, 3, 0); imm32 = (imm4H << 4) | imm4L; add = BitIsSet (opcode, 23); // if t == 15 then UNPREDICTABLE; if (t == 15) return false; break; } default: return false; } // base = Align(PC,4); uint64_t pc_value = ReadCoreReg (PC_REG, &success); if (!success) return false; uint64_t base = AlignPC (pc_value); addr_t address; // address = if add then (base + imm32) else (base - imm32); if (add) address = base + imm32; else address = base - imm32; // data = MemU[address,2]; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindGeneric, LLDB_REGNUM_GENERIC_PC, base_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, imm32); uint64_t data = MemURead (context, address, 2, 0, &success); if (!success) return false; // if UnalignedSupport() || address<0> = '0' then if (UnalignedSupport() || BitIsClear (address, 0)) { // R[t] = SignExtend(data, 32); int64_t signed_data = llvm::SignExtend64<16>(data); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) return false; } else // Can only apply before ARMv7 { // R[t] = bits(32) UNKNOWN; WriteBits32Unknown (t); } } return true; } // LDRSH (register) calculates an address from a base register value and an offset register value, loads a halfword // from memory, sign-extends it to form a 32-bit word, and writes it to a register. The offset register value can be // shifted left by 0, 1, 2, or 3 bits. bool EmulateInstructionARM::EmulateLDRSHRegister (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset = Shift(R[m], shift_t, shift_n, APSR.C); offset_addr = if add then (R[n] + offset) else (R[n] - offset); address = if index then offset_addr else R[n]; data = MemU[address,2]; if wback then R[n] = offset_addr; if UnalignedSupport() || address<0> = '0' then R[t] = SignExtend(data, 32); else // Can only apply before ARMv7 R[t] = bits(32) UNKNOWN; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t m; bool index; bool add; bool wback; ARM_ShifterType shift_t; uint32_t shift_n; // EncodingSpecificOperations(); NullCheckIfThumbEE(n); switch (encoding) { case eEncodingT1: // if CurrentInstrSet() == InstrSet_ThumbEE then SEE "Modified operation in ThumbEE"; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 2, 0); n = Bits32 (opcode, 5, 3); m = Bits32 (opcode, 8, 6); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // if Rn == '1111' then SEE LDRSH (literal); // if Rt == '1111' then SEE "Unallocated memory hints"; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // index = TRUE; add = TRUE; wback = FALSE; index = true; add = true; wback = false; // (shift_t, shift_n) = (SRType_LSL, UInt(imm2)); shift_t = SRType_LSL; shift_n = Bits32 (opcode, 5, 4); // if t == 13 || BadReg(m) then UNPREDICTABLE; if ((t == 13) || BadReg (m)) return false; break; case eEncodingA1: // if P == '0' && W == '1' then SEE LDRSHT; // t = UInt(Rt); n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // index = (P == '1'); add = (U == '1'); wback = (P == '0') || (W == '1'); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; // if t == 15 || m == 15 then UNPREDICTABLE; if ((t == 15) || (m == 15)) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; break; default: return false; } uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; // offset = Shift(R[m], shift_t, shift_n, APSR.C); addr_t offset = Shift (Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; addr_t offset_addr; addr_t address; // offset_addr = if add then (R[n] + offset) else (R[n] - offset); if (add) offset_addr = Rn + offset; else offset_addr = Rn - offset; // address = if index then offset_addr else R[n]; if (index) address = offset_addr; else address = Rn; // data = MemU[address,2]; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); RegisterInfo offset_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); uint64_t data = MemURead (context, address, 2, 0, &success); if (!success) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } // if UnalignedSupport() || address<0> = '0' then if (UnalignedSupport() || BitIsClear (address, 0)) { // R[t] = SignExtend(data, 32); context.type = eContextRegisterLoad; context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); int64_t signed_data = llvm::SignExtend64<16>(data); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, (uint64_t) signed_data)) return false; } else // Can only apply before ARMv7 { // R[t] = bits(32) UNKNOWN; WriteBits32Unknown (t); } } return true; } // SXTB extracts an 8-bit value from a register, sign-extends it to 32 bits, and writes the result to the destination // register. You can specifiy a rotation by 0, 8, 16, or 24 bits before extracting the 8-bit value. bool EmulateInstructionARM::EmulateSXTB (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); rotated = ROR(R[m], rotation); R[d] = SignExtend(rotated<7:0>, 32); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t m; uint32_t rotation; // EncodingSpecificOperations(); switch (encoding) { case eEncodingT1: // d = UInt(Rd); m = UInt(Rm); rotation = 0; d = Bits32 (opcode, 2, 0); m = Bits32 (opcode, 5, 3); rotation = 0; break; case eEncodingT2: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32 (opcode, 11, 8); m = Bits32 (opcode, 3, 0); rotation = Bits32 (opcode, 5, 4) << 3; // if BadReg(d) || BadReg(m) then UNPREDICTABLE; if (BadReg (d) || BadReg (m)) return false; break; case eEncodingA1: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32 (opcode, 15, 12); m = Bits32 (opcode, 3, 0); rotation = Bits32 (opcode, 11, 10) << 3; // if d == 15 || m == 15 then UNPREDICTABLE; if ((d == 15) || (m == 15)) return false; break; default: return false; } uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; // rotated = ROR(R[m], rotation); uint64_t rotated = ROR (Rm, rotation, &success); if (!success) return false; // R[d] = SignExtend(rotated<7:0>, 32); int64_t data = llvm::SignExtend64<8>(rotated); RegisterInfo source_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegister (source_reg); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, (uint64_t) data)) return false; } return true; } // SXTH extracts a 16-bit value from a register, sign-extends it to 32 bits, and writes the result to the destination // register. You can specify a rotation by 0, 8, 16, or 24 bits before extracting the 16-bit value. bool EmulateInstructionARM::EmulateSXTH (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); rotated = ROR(R[m], rotation); R[d] = SignExtend(rotated<15:0>, 32); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t m; uint32_t rotation; // EncodingSpecificOperations(); switch (encoding) { case eEncodingT1: // d = UInt(Rd); m = UInt(Rm); rotation = 0; d = Bits32 (opcode, 2, 0); m = Bits32 (opcode, 5, 3); rotation = 0; break; case eEncodingT2: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32 (opcode, 11, 8); m = Bits32 (opcode, 3, 0); rotation = Bits32 (opcode, 5, 4) << 3; // if BadReg(d) || BadReg(m) then UNPREDICTABLE; if (BadReg (d) || BadReg (m)) return false; break; case eEncodingA1: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32 (opcode, 15, 12); m = Bits32 (opcode, 3, 0); rotation = Bits32 (opcode, 11, 10) << 3; // if d == 15 || m == 15 then UNPREDICTABLE; if ((d == 15) || (m == 15)) return false; break; default: return false; } uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; // rotated = ROR(R[m], rotation); uint64_t rotated = ROR (Rm, rotation, &success); if (!success) return false; // R[d] = SignExtend(rotated<15:0>, 32); RegisterInfo source_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegister (source_reg); int64_t data = llvm::SignExtend64<16> (rotated); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, (uint64_t) data)) return false; } return true; } // UXTB extracts an 8-bit value from a register, zero-extneds it to 32 bits, and writes the result to the destination // register. You can specify a rotation by 0, 8, 16, or 24 bits before extracting the 8-bit value. bool EmulateInstructionARM::EmulateUXTB (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); rotated = ROR(R[m], rotation); R[d] = ZeroExtend(rotated<7:0>, 32); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t m; uint32_t rotation; // EncodingSpecificOperations(); switch (encoding) { case eEncodingT1: // d = UInt(Rd); m = UInt(Rm); rotation = 0; d = Bits32 (opcode, 2, 0); m = Bits32 (opcode, 5, 3); rotation = 0; break; case eEncodingT2: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32 (opcode, 11, 8); m = Bits32 (opcode, 3, 0); rotation = Bits32 (opcode, 5, 4) << 3; // if BadReg(d) || BadReg(m) then UNPREDICTABLE; if (BadReg (d) || BadReg (m)) return false; break; case eEncodingA1: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32 (opcode, 15, 12); m = Bits32 (opcode, 3, 0); rotation = Bits32 (opcode, 11, 10) << 3; // if d == 15 || m == 15 then UNPREDICTABLE; if ((d == 15) || (m == 15)) return false; break; default: return false; } uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; // rotated = ROR(R[m], rotation); uint64_t rotated = ROR (Rm, rotation, &success); if (!success) return false; // R[d] = ZeroExtend(rotated<7:0>, 32); RegisterInfo source_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegister (source_reg); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, Bits32 (rotated, 7, 0))) return false; } return true; } // UXTH extracts a 16-bit value from a register, zero-extends it to 32 bits, and writes the result to the destination // register. You can specify a rotation by 0, 8, 16, or 24 bits before extracting the 16-bit value. bool EmulateInstructionARM::EmulateUXTH (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); rotated = ROR(R[m], rotation); R[d] = ZeroExtend(rotated<15:0>, 32); #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t m; uint32_t rotation; switch (encoding) { case eEncodingT1: // d = UInt(Rd); m = UInt(Rm); rotation = 0; d = Bits32 (opcode, 2, 0); m = Bits32 (opcode, 5, 3); rotation = 0; break; case eEncodingT2: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32 (opcode, 11, 8); m = Bits32 (opcode, 3, 0); rotation = Bits32 (opcode, 5, 4) << 3; // if BadReg(d) || BadReg(m) then UNPREDICTABLE; if (BadReg (d) || BadReg (m)) return false; break; case eEncodingA1: // d = UInt(Rd); m = UInt(Rm); rotation = UInt(rotate:'000'); d = Bits32 (opcode, 15, 12); m = Bits32 (opcode, 3, 0); rotation = Bits32 (opcode, 11, 10) << 3; // if d == 15 || m == 15 then UNPREDICTABLE; if ((d == 15) || (m == 15)) return false; break; default: return false; } uint64_t Rm = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + m, 0, &success); if (!success) return false; // rotated = ROR(R[m], rotation); uint64_t rotated = ROR (Rm, rotation, &success); if (!success) return false; // R[d] = ZeroExtend(rotated<15:0>, 32); RegisterInfo source_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, source_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegister (source_reg); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, Bits32 (rotated, 15, 0))) return false; } return true; } // RFE (Return From Exception) loads the PC and the CPSR from the word at the specified address and the following // word respectively. bool EmulateInstructionARM::EmulateRFE (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); if !CurrentModeIsPrivileged() || CurrentInstrSet() == InstrSet_ThumbEE then UNPREDICTABLE; else address = if increment then R[n] else R[n]-8; if wordhigher then address = address+4; CPSRWriteByInstr(MemA[address+4,4], '1111', TRUE); BranchWritePC(MemA[address,4]); if wback then R[n] = if increment then R[n]+8 else R[n]-8; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t n; bool wback; bool increment; bool wordhigher; // EncodingSpecificOperations(); switch (encoding) { case eEncodingT1: // n = UInt(Rn); wback = (W == '1'); increment = FALSE; wordhigher = FALSE; n = Bits32 (opcode, 19, 16); wback = BitIsSet (opcode, 21); increment = false; wordhigher = false; // if n == 15 then UNPREDICTABLE; if (n == 15) return false; // if InITBlock() && !LastInITBlock() then UNPREDICTABLE; if (InITBlock() && !LastInITBlock()) return false; break; case eEncodingT2: // n = UInt(Rn); wback = (W == '1'); increment = TRUE; wordhigher = FALSE; n = Bits32 (opcode, 19, 16); wback = BitIsSet (opcode, 21); increment = true; wordhigher = false; // if n == 15 then UNPREDICTABLE; if (n == 15) return false; // if InITBlock() && !LastInITBlock() then UNPREDICTABLE; if (InITBlock() && !LastInITBlock()) return false; break; case eEncodingA1: // n = UInt(Rn); n = Bits32 (opcode, 19, 16); // wback = (W == '1'); inc = (U == '1'); wordhigher = (P == U); wback = BitIsSet (opcode, 21); increment = BitIsSet (opcode, 23); wordhigher = (Bit32 (opcode, 24) == Bit32 (opcode, 23)); // if n == 15 then UNPREDICTABLE; if (n == 15) return false; break; default: return false; } // if !CurrentModeIsPrivileged() || CurrentInstrSet() == InstrSet_ThumbEE then if (!CurrentModeIsPrivileged ()) // UNPREDICTABLE; return false; else { uint64_t Rn = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + n, 0, &success); if (!success) return false; addr_t address; // address = if increment then R[n] else R[n]-8; if (increment) address = Rn; else address = Rn - 8; // if wordhigher then address = address+4; if (wordhigher) address = address + 4; // CPSRWriteByInstr(MemA[address+4,4], '1111', TRUE); RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); EmulateInstruction::Context context; context.type = eContextReturnFromException; context.SetRegisterPlusOffset (base_reg, address - Rn); uint64_t data = MemARead (context, address + 4, 4, 0, &success); if (!success) return false; CPSRWriteByInstr (data, 15, true); // BranchWritePC(MemA[address,4]); uint64_t data2 = MemARead (context, address, 4, 0, &success); if (!success) return false; BranchWritePC (context, data2); // if wback then R[n] = if increment then R[n]+8 else R[n]-8; if (wback) { context.type = eContextAdjustBaseRegister; if (increment) { context.SetOffset (8); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, Rn + 8)) return false; } else { context.SetOffset (-8); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, Rn - 8)) return false; } } // if wback } } // if ConditionPassed() return true; } // Bitwise Exclusive OR (immediate) performs a bitwise exclusive OR of a register value and an immediate value, // and writes the result to the destination register. It can optionally update the condition flags based on // the result. bool EmulateInstructionARM::EmulateEORImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); result = R[n] EOR imm32; if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn; uint32_t imm32; // the immediate value to be ORed to the value obtained from Rn bool setflags; uint32_t carry; // the carry bit after ARM/Thumb Expand operation switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) // if Rd == '1111' && S == '1' then SEE TEQ (immediate); if (Rd == 15 && setflags) return EmulateTEQImm (opcode, eEncodingT1); if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; uint32_t result = val1 ^ imm32; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // Bitwise Exclusive OR (register) performs a bitwise exclusive OR of a register value and an // optionally-shifted register value, and writes the result to the destination register. // It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateEORReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); result = R[n] EOR shifted; if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm bool setflags; uint32_t carry; switch (encoding) { case eEncodingT1: Rd = Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); // if Rd == '1111' && S == '1' then SEE TEQ (register); if (Rd == 15 && setflags) return EmulateTEQReg (opcode, eEncodingT1); if (Rd == 13 || (Rd == 15 && !setflags) || BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); if (!success) return false; uint32_t result = val1 ^ shifted; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // Bitwise OR (immediate) performs a bitwise (inclusive) OR of a register value and an immediate value, and // writes the result to the destination register. It can optionally update the condition flags based // on the result. bool EmulateInstructionARM::EmulateORRImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); result = R[n] OR imm32; if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn; uint32_t imm32; // the immediate value to be ORed to the value obtained from Rn bool setflags; uint32_t carry; // the carry bit after ARM/Thumb Expand operation switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) // if Rn == '1111' then SEE MOV (immediate); if (Rn == 15) return EmulateMOVRdImm (opcode, eEncodingT2); if (BadReg(Rd) || Rn == 13) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; uint32_t result = val1 | imm32; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // Bitwise OR (register) performs a bitwise (inclusive) OR of a register value and an optionally-shifted register // value, and writes the result to the destination register. It can optionally update the condition flags based // on the result. bool EmulateInstructionARM::EmulateORRReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); result = R[n] OR shifted; if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rd, Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm bool setflags; uint32_t carry; switch (encoding) { case eEncodingT1: Rd = Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); // if Rn == '1111' then SEE MOV (register); if (Rn == 15) return EmulateMOVRdRm (opcode, eEncodingT3); if (BadReg(Rd) || Rn == 13 || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); if (!success) return false; uint32_t result = val1 | shifted; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, result, Rd, setflags, carry)) return false; } return true; } // Reverse Subtract (immediate) subtracts a register value from an immediate value, and writes the result to // the destination register. It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateRSBImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(NOT(R[n]), imm32, '1'); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rd; // the destination register uint32_t Rn; // the first operand bool setflags; uint32_t imm32; // the immediate value to be added to the value obtained from Rn switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 2, 0); Rn = Bits32(opcode, 5, 3); setflags = !InITBlock(); imm32 = 0; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) if (BadReg(Rd) || BadReg(Rn)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(~reg_val, imm32, 1); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; return true; } // Reverse Subtract (register) subtracts a register value from an optionally-shifted register value, and writes the // result to the destination register. It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateRSBReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(NOT(R[n]), shifted, '1'); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rd; // the destination register uint32_t Rn; // the first operand uint32_t Rm; // the second operand bool setflags; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); // if (BadReg(d) || BadReg(m)) then UNPREDICTABLE; if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the register value from register Rn. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the register value from register Rm. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(~val1, shifted, 1); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; return true; } // Reverse Subtract with Carry (immediate) subtracts a register value and the value of NOT (Carry flag) from // an immediate value, and writes the result to the destination register. It can optionally update the condition // flags based on the result. bool EmulateInstructionARM::EmulateRSCImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(NOT(R[n]), imm32, APSR.C); if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rd; // the destination register uint32_t Rn; // the first operand bool setflags; uint32_t imm32; // the immediate value to be added to the value obtained from Rn switch (encoding) { case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(~reg_val, imm32, APSR_C); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; return true; } // Reverse Subtract with Carry (register) subtracts a register value and the value of NOT (Carry flag) from an // optionally-shifted register value, and writes the result to the destination register. It can optionally update the // condition flags based on the result. bool EmulateInstructionARM::EmulateRSCReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(NOT(R[n]), shifted, APSR.C); if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rd; // the destination register uint32_t Rn; // the first operand uint32_t Rm; // the second operand bool setflags; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm switch (encoding) { case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the register value from register Rn. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the register value from register Rm. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(~val1, shifted, APSR_C); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; return true; } // Subtract with Carry (immediate) subtracts an immediate value and the value of // NOT (Carry flag) from a register value, and writes the result to the destination register. // It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateSBCImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rd; // the destination register uint32_t Rn; // the first operand bool setflags; uint32_t imm32; // the immediate value to be added to the value obtained from Rn switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) if (BadReg(Rd) || BadReg(Rn)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, APSR_C); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; return true; } // Subtract with Carry (register) subtracts an optionally-shifted register value and the value of // NOT (Carry flag) from a register value, and writes the result to the destination register. // It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateSBCReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), APSR.C); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rd; // the destination register uint32_t Rn; // the first operand uint32_t Rm; // the second operand bool setflags; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm switch (encoding) { case eEncodingT1: Rd = Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); setflags = !InITBlock(); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftThumb(opcode, shift_t); if (BadReg(Rd) || BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); setflags = BitIsSet(opcode, 20); shift_n = DecodeImmShiftARM(opcode, shift_t); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the register value from register Rn. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the register value from register Rm. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift(val2, shift_t, shift_n, APSR_C, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(val1, ~shifted, APSR_C); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs(); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; return true; } // This instruction subtracts an immediate value from a register value, and writes the result // to the destination register. It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateSUBImmThumb (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1'); R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rd; // the destination register uint32_t Rn; // the first operand bool setflags; uint32_t imm32; // the immediate value to be subtracted from the value obtained from Rn switch (encoding) { case eEncodingT1: Rd = Bits32(opcode, 2, 0); Rn = Bits32(opcode, 5, 3); setflags = !InITBlock(); imm32 = Bits32(opcode, 8, 6); // imm32 = ZeroExtend(imm3, 32) break; case eEncodingT2: Rd = Rn = Bits32(opcode, 10, 8); setflags = !InITBlock(); imm32 = Bits32(opcode, 7, 0); // imm32 = ZeroExtend(imm8, 32) break; case eEncodingT3: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbExpandImm(opcode); // imm32 = ThumbExpandImm(i:imm3:imm8) // if Rd == '1111' && S == '1' then SEE CMP (immediate); if (Rd == 15 && setflags) return EmulateCMPImm (opcode, eEncodingT2); // if Rn == '1101' then SEE SUB (SP minus immediate); if (Rn == 13) return EmulateSUBSPImm (opcode, eEncodingT2); // if d == 13 || (d == 15 && S == '0') || n == 15 then UNPREDICTABLE; if (Rd == 13 || (Rd == 15 && !setflags) || Rn == 15) return false; break; case eEncodingT4: Rd = Bits32(opcode, 11, 8); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ThumbImm12(opcode); // imm32 = ZeroExtend(i:imm3:imm8, 32) // if Rn == '1111' then SEE ADR; if (Rn == 15) return EmulateADR (opcode, eEncodingT2); // if Rn == '1101' then SEE SUB (SP minus immediate); if (Rn == 13) return EmulateSUBSPImm (opcode, eEncodingT3); if (BadReg(Rd)) return false; break; default: return false; } // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; return true; } // This instruction subtracts an immediate value from a register value, and writes the result // to the destination register. It can optionally update the condition flags based on the result. bool EmulateInstructionARM::EmulateSUBImmARM (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (result, carry, overflow) = AddWithCarry(R[n], NOT(imm32), '1'); if d == 15 then ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; uint32_t Rd; // the destination register uint32_t Rn; // the first operand bool setflags; uint32_t imm32; // the immediate value to be subtracted from the value obtained from Rn switch (encoding) { case eEncodingA1: Rd = Bits32(opcode, 15, 12); Rn = Bits32(opcode, 19, 16); setflags = BitIsSet(opcode, 20); imm32 = ARMExpandImm(opcode); // imm32 = ARMExpandImm(imm12) // if Rn == '1111' && S == '0' then SEE ADR; if (Rn == 15 && !setflags) return EmulateADR (opcode, eEncodingA2); // if Rn == '1101' then SEE SUB (SP minus immediate); if (Rn == 13) return EmulateSUBSPImm (opcode, eEncodingA1); // if Rd == '1111' && S == '1' then SEE SUBS PC, LR and related instructions; if (Rd == 15 && setflags) return EmulateSUBSPcLrEtc (opcode, encoding); break; default: return false; } // Read the register value from the operand register Rn. uint32_t reg_val = ReadCoreReg(Rn, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry(reg_val, ~imm32, 1); EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteCoreRegOptionalFlags(context, res.result, Rd, setflags, res.carry_out, res.overflow)) return false; return true; } // Test Equivalence (immediate) performs a bitwise exclusive OR operation on a register value and an // immediate value. It updates the condition flags based on the result, and discards the result. bool EmulateInstructionARM::EmulateTEQImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); result = R[n] EOR imm32; APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rn; uint32_t imm32; // the immediate value to be ANDed to the value obtained from Rn uint32_t carry; // the carry bit after ARM/Thumb Expand operation switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 19, 16); imm32 = ThumbExpandImm_C (opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) if (BadReg(Rn)) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); imm32 = ARMExpandImm_C (opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; uint32_t result = val1 ^ imm32; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteFlags(context, result, carry)) return false; } return true; } // Test Equivalence (register) performs a bitwise exclusive OR operation on a register value and an // optionally-shifted register value. It updates the condition flags based on the result, and discards // the result. bool EmulateInstructionARM::EmulateTEQReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); result = R[n] EOR shifted; APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm uint32_t carry; switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); shift_n = DecodeImmShiftThumb(opcode, shift_t); if (BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); shift_n = DecodeImmShiftARM(opcode, shift_t); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); if (!success) return false; uint32_t result = val1 ^ shifted; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteFlags(context, result, carry)) return false; } return true; } // Test (immediate) performs a bitwise AND operation on a register value and an immediate value. // It updates the condition flags based on the result, and discards the result. bool EmulateInstructionARM::EmulateTSTImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); result = R[n] AND imm32; APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rn; uint32_t imm32; // the immediate value to be ANDed to the value obtained from Rn uint32_t carry; // the carry bit after ARM/Thumb Expand operation switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 19, 16); imm32 = ThumbExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ThumbExpandImm(i:imm3:imm8, APSR.C) if (BadReg(Rn)) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); imm32 = ARMExpandImm_C(opcode, APSR_C, carry); // (imm32, carry) = ARMExpandImm(imm12, APSR.C) break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; uint32_t result = val1 & imm32; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteFlags(context, result, carry)) return false; } return true; } // Test (register) performs a bitwise AND operation on a register value and an optionally-shifted register value. // It updates the condition flags based on the result, and discards the result. bool EmulateInstructionARM::EmulateTSTReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 // ARM pseudo code... if ConditionPassed() then EncodingSpecificOperations(); (shifted, carry) = Shift_C(R[m], shift_t, shift_n, APSR.C); result = R[n] AND shifted; APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; // APSR.V unchanged #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t Rn, Rm; ARM_ShifterType shift_t; uint32_t shift_n; // the shift applied to the value read from Rm uint32_t carry; switch (encoding) { case eEncodingT1: Rn = Bits32(opcode, 2, 0); Rm = Bits32(opcode, 5, 3); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); shift_n = DecodeImmShiftThumb(opcode, shift_t); if (BadReg(Rn) || BadReg(Rm)) return false; break; case eEncodingA1: Rn = Bits32(opcode, 19, 16); Rm = Bits32(opcode, 3, 0); shift_n = DecodeImmShiftARM(opcode, shift_t); break; default: return false; } // Read the first operand. uint32_t val1 = ReadCoreReg(Rn, &success); if (!success) return false; // Read the second operand. uint32_t val2 = ReadCoreReg(Rm, &success); if (!success) return false; uint32_t shifted = Shift_C(val2, shift_t, shift_n, APSR_C, carry, &success); if (!success) return false; uint32_t result = val1 & shifted; EmulateInstruction::Context context; context.type = EmulateInstruction::eContextImmediate; context.SetNoArgs (); if (!WriteFlags(context, result, carry)) return false; } return true; } // A8.6.216 SUB (SP minus register) bool EmulateInstructionARM::EmulateSUBSPReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(SP, NOT(shifted), Ô1Õ); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t m; bool setflags; ARM_ShifterType shift_t; uint32_t shift_n; switch (encoding) { case eEncodingT1: // d = UInt(Rd); m = UInt(Rm); setflags = (S == Ô1Õ); d = Bits32 (opcode, 11, 8); m = Bits32 (opcode, 3, 0); setflags = BitIsSet (opcode, 20); // (shift_t, shift_n) = DecodeImmShift(type, imm3:imm2); shift_n = DecodeImmShiftThumb (opcode, shift_t); // if d == 13 && (shift_t != SRType_LSL || shift_n > 3) then UNPREDICTABLE; if ((d == 13) && ((shift_t != SRType_LSL) || (shift_n > 3))) return false; // if d == 15 || BadReg(m) then UNPREDICTABLE; if ((d == 15) || BadReg (m)) return false; break; case eEncodingA1: // d = UInt(Rd); m = UInt(Rm); setflags = (S == Ô1Õ); d = Bits32 (opcode, 15, 12); m = Bits32 (opcode, 3, 0); setflags = BitIsSet (opcode, 20); // if Rd == Ô1111Õ && S == Ô1Õ then SEE SUBS PC, LR and related instructions; if (d == 15 && setflags) EmulateSUBSPcLrEtc (opcode, encoding); // (shift_t, shift_n) = DecodeImmShift(type, imm5); shift_n = DecodeImmShiftARM (opcode, shift_t); break; default: return false; } // shifted = Shift(R[m], shift_t, shift_n, APSR.C); uint32_t Rm = ReadCoreReg (m, &success); if (!success) return false; uint32_t shifted = Shift (Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; // (result, carry, overflow) = AddWithCarry(SP, NOT(shifted), Ô1Õ); uint32_t sp_val = ReadCoreReg (SP_REG, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry (sp_val, ~shifted, 1); EmulateInstruction::Context context; context.type = eContextArithmetic; RegisterInfo sp_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_sp, sp_reg); RegisterInfo dwarf_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, dwarf_reg); context.SetRegisterRegisterOperands (sp_reg, dwarf_reg); if (!WriteCoreRegOptionalFlags(context, res.result, dwarf_r0 + d, setflags, res.carry_out, res.overflow)) return false; } return true; } // A8.6.7 ADD (register-shifted register) bool EmulateInstructionARM::EmulateADDRegShift (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); shift_n = UInt(R[s]<7:0>); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(R[n], shifted, Ô0Õ); R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t n; uint32_t m; uint32_t s; bool setflags; ARM_ShifterType shift_t; switch (encoding) { case eEncodingA1: // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); s = UInt(Rs); d = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); s = Bits32 (opcode, 11, 8); // setflags = (S == Ô1Õ); shift_t = DecodeRegShift(type); setflags = BitIsSet (opcode, 20); shift_t = DecodeRegShift (Bits32 (opcode, 6, 5)); // if d == 15 || n == 15 || m == 15 || s == 15 then UNPREDICTABLE; if ((d == 15) || (m == 15) || (m == 15) || (s == 15)) return false; break; default: return false; } // shift_n = UInt(R[s]<7:0>); uint32_t Rs = ReadCoreReg (s, &success); if (!success) return false; uint32_t shift_n = Bits32 (Rs, 7, 0); // shifted = Shift(R[m], shift_t, shift_n, APSR.C); uint32_t Rm = ReadCoreReg (m, &success); if (!success) return false; uint32_t shifted = Shift (Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; // (result, carry, overflow) = AddWithCarry(R[n], shifted, Ô0Õ); uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry (Rn, shifted, 0); // R[d] = result; EmulateInstruction::Context context; context.type = eContextArithmetic; RegisterInfo reg_n; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, reg_n); RegisterInfo reg_m; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, reg_m); context.SetRegisterRegisterOperands (reg_n, reg_m); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, res.result)) return false; // if setflags then // APSR.N = result<31>; // APSR.Z = IsZeroBit(result); // APSR.C = carry; // APSR.V = overflow; if (setflags) return WriteFlags (context, res.result, res.carry_out, res.overflow); } return true; } // A8.6.213 SUB (register) bool EmulateInstructionARM::EmulateSUBReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); shifted = Shift(R[m], shift_t, shift_n, APSR.C); (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), Ô1Õ); if d == 15 then // Can only occur for ARM encoding ALUWritePC(result); // setflags is always FALSE here else R[d] = result; if setflags then APSR.N = result<31>; APSR.Z = IsZeroBit(result); APSR.C = carry; APSR.V = overflow; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t n; uint32_t m; bool setflags; ARM_ShifterType shift_t; uint32_t shift_n; switch (encoding) { case eEncodingT1: // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = !InITBlock(); d = Bits32 (opcode, 2, 0); n = Bits32 (opcode, 5, 3); m = Bits32 (opcode, 8, 6); setflags = !InITBlock(); // (shift_t, shift_n) = (SRType_LSL, 0); shift_t = SRType_LSL; shift_n = 0; break; case eEncodingT2: // if Rd == Ô1111Õ && S == Ô1Õ then SEE CMP (register); // if Rn == Ô1101Õ then SEE SUB (SP minus register); // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == Ô1Õ); d = Bits32 (opcode, 11, 8); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); setflags = BitIsSet (opcode, 20); // (shift_t, shift_n) = DecodeImmShift(type, imm3:imm2); shift_n = DecodeImmShiftThumb (opcode, shift_t); // if d == 13 || (d == 15 && S == '0') || n == 15 || BadReg(m) then UNPREDICTABLE; if ((d == 13) || ((d == 15) && BitIsClear (opcode, 20)) || (n == 15) || BadReg (m)) return false; break; case eEncodingA1: // if Rn == Ô1101Õ then SEE SUB (SP minus register); // d = UInt(Rd); n = UInt(Rn); m = UInt(Rm); setflags = (S == Ô1Õ); d = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); setflags = BitIsSet (opcode, 20); // if Rd == Ô1111Õ && S == Ô1Õ then SEE SUBS PC, LR and related instructions; if ((d == 15) && setflags) EmulateSUBSPcLrEtc (opcode, encoding); // (shift_t, shift_n) = DecodeImmShift(type, imm5); shift_n = DecodeImmShiftARM (opcode, shift_t); break; default: return false; } // shifted = Shift(R[m], shift_t, shift_n, APSR.C); uint32_t Rm = ReadCoreReg (m, &success); if (!success) return false; uint32_t shifted = Shift (Rm, shift_t, shift_n, APSR_C, &success); if (!success) return false; // (result, carry, overflow) = AddWithCarry(R[n], NOT(shifted), Ô1Õ); uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; AddWithCarryResult res = AddWithCarry (Rn, ~shifted, 1); // if d == 15 then // Can only occur for ARM encoding // ALUWritePC(result); // setflags is always FALSE here // else // R[d] = result; // if setflags then // APSR.N = result<31>; // APSR.Z = IsZeroBit(result); // APSR.C = carry; // APSR.V = overflow; EmulateInstruction::Context context; context.type = eContextArithmetic; RegisterInfo reg_n; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, reg_n); RegisterInfo reg_m; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, reg_m); context.SetRegisterRegisterOperands (reg_n, reg_m); if (!WriteCoreRegOptionalFlags (context, res.result, dwarf_r0 + d, setflags, res.carry_out, res.overflow)) return false; } return true; } // A8.6.202 STREX // Store Register Exclusive calculates an address from a base register value and an immediate offset, and stores a // word from a register to memory if the executing processor has exclusive access to the memory addressed. bool EmulateInstructionARM::EmulateSTREX (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); address = R[n] + imm32; if ExclusiveMonitorsPass(address,4) then MemA[address,4] = R[t]; R[d] = 0; else R[d] = 1; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t d; uint32_t t; uint32_t n; uint32_t imm32; const uint32_t addr_byte_size = GetAddressByteSize(); switch (encoding) { case eEncodingT1: // d = UInt(Rd); t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm8:Õ00Õ, 32); d = Bits32 (opcode, 11, 8); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 7, 0) << 2; // if BadReg(d) || BadReg(t) || n == 15 then UNPREDICTABLE; if (BadReg (d) || BadReg (t) || (n == 15)) return false; // if d == n || d == t then UNPREDICTABLE; if ((d == n) || (d == t)) return false; break; case eEncodingA1: // d = UInt(Rd); t = UInt(Rt); n = UInt(Rn); imm32 = Zeros(32); // Zero offset d = Bits32 (opcode, 15, 12); t = Bits32 (opcode, 3, 0); n = Bits32 (opcode, 19, 16); imm32 = 0; // if d == 15 || t == 15 || n == 15 then UNPREDICTABLE; if ((d == 15) || (t == 15) || (n == 15)) return false; // if d == n || d == t then UNPREDICTABLE; if ((d == n) || (d == t)) return false; break; default: return false; } // address = R[n] + imm32; uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; addr_t address = Rn + imm32; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); EmulateInstruction::Context context; context.type = eContextRegisterStore; context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, imm32); // if ExclusiveMonitorsPass(address,4) then // if (ExclusiveMonitorsPass (address, addr_byte_size)) -- For now, for the sake of emulation, we will say this // always return true. if (true) { // MemA[address,4] = R[t]; uint32_t Rt = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_r0 + t, 0, &success); if (!success) return false; if (!MemAWrite (context, address, Rt, addr_byte_size)) return false; // R[d] = 0; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, 0)) return false; } else { // R[d] = 1; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, 1)) return false; } } return true; } // A8.6.197 STRB (immediate, ARM) bool EmulateInstructionARM::EmulateSTRBImmARM (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; MemU[address,1] = R[t]<7:0>; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; switch (encoding) { case eEncodingA1: // if P == Ô0Õ && W == Ô1Õ then SEE STRBT; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 11, 0); // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); // if t == 15 then UNPREDICTABLE; if (t == 15) return false; // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; break; default: return false; } // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; addr_t offset_addr; if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; // address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; // MemU[address,1] = R[t]<7:0>; uint32_t Rt = ReadCoreReg (t, &success); if (!success) return false; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); EmulateInstruction::Context context; context.type = eContextRegisterStore; context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); if (!MemUWrite (context, address, Bits32 (Rt, 7, 0), 1)) return false; // if wback then R[n] = offset_addr; if (wback) { if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // A8.6.194 STR (immediate, ARM) bool EmulateInstructionARM::EmulateSTRImmARM (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; MemU[address,4] = if t == 15 then PCStoreValue() else R[t]; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; const uint32_t addr_byte_size = GetAddressByteSize(); switch (encoding) { case eEncodingA1: // if P == Ô0Õ && W == Ô1Õ then SEE STRT; // if Rn == Ô1101Õ && P == Ô1Õ && U == Ô0Õ && W == Ô1Õ && imm12 == Ô000000000100Õ then SEE PUSH; // t = UInt(Rt); n = UInt(Rn); imm32 = ZeroExtend(imm12, 32); t = Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 11, 0); // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); // if wback && (n == 15 || n == t) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t))) return false; break; default: return false; } // offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; addr_t offset_addr; if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; // address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); EmulateInstruction::Context context; context.type = eContextRegisterStore; context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); // MemU[address,4] = if t == 15 then PCStoreValue() else R[t]; uint32_t Rt = ReadCoreReg (t, &success); if (!success) return false; if (t == 15) { uint32_t pc_value = ReadCoreReg (PC_REG, &success); if (!success) return false; if (!MemUWrite (context, address, pc_value, addr_byte_size)) return false; } else { if (!MemUWrite (context, address, Rt, addr_byte_size)) return false; } // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetImmediate (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // A8.6.66 LDRD (immediate) // Load Register Dual (immediate) calculates an address from a base register value and an immediate offset, loads two // words from memory, and writes them to two registers. It can use offset, post-indexed, or pre-indexed addressing. bool EmulateInstructionARM::EmulateLDRDImmediate (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; R[t] = MemA[address,4]; R[t2] = MemA[address+4,4]; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t t2; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; switch (encoding) { case eEncodingT1: //if P == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; //if Rn == Ô1111Õ then SEE LDRD (literal); //t = UInt(Rt); t2 = UInt(Rt2); n = UInt(Rn); imm32 = ZeroExtend(imm8:Õ00Õ, 32); t = Bits32 (opcode, 15, 12); t2 = Bits32 (opcode, 11, 8); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 7, 0) << 2; //index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (W == Ô1Õ); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = BitIsSet (opcode, 21); //if wback && (n == t || n == t2) then UNPREDICTABLE; if (wback && ((n == t) || (n == t2))) return false; //if BadReg(t) || BadReg(t2) || t == t2 then UNPREDICTABLE; if (BadReg (t) || BadReg (t2) || (t == t2)) return false; break; case eEncodingA1: //if Rn == Ô1111Õ then SEE LDRD (literal); //if Rt<0> == Ô1Õ then UNPREDICTABLE; //t = UInt(Rt); t2 = t+1; n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); t = Bits32 (opcode, 15, 12); if (BitIsSet (t, 0)) return false; t2 = t + 1; n = Bits32 (opcode, 19, 16); imm32 = (Bits32 (opcode, 11, 8) << 4) | Bits32 (opcode, 3, 0); //index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); //if P == Ô0Õ && W == Ô1Õ then UNPREDICTABLE; if (BitIsClear (opcode, 24) && BitIsSet (opcode, 21)) return false; //if wback && (n == t || n == t2) then UNPREDICTABLE; if (wback && ((n == t) || (n == t2))) return false; //if t2 == 15 then UNPREDICTABLE; if (t2 == 15) return false; break; default: return false; } //offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; addr_t offset_addr; if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; //address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; //R[t] = MemA[address,4]; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - Rn); const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t data = MemARead (context, address, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; //R[t2] = MemA[address+4,4]; context.SetRegisterPlusOffset (base_reg, (address + 4) - Rn); data = MemARead (context, address + 4, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t2, data)) return false; //if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // A8.6.68 LDRD (register) // Load Register Dual (register) calculates an address from a base register value and a register offset, loads two // words from memory, and writes them to two registers. It can use offset, post-indexed or pre-indexed addressing. bool EmulateInstructionARM::EmulateLDRDRegister (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); address = if index then offset_addr else R[n]; R[t] = MemA[address,4]; R[t2] = MemA[address+4,4]; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t t2; uint32_t n; uint32_t m; bool index; bool add; bool wback; switch (encoding) { case eEncodingA1: // if Rt<0> == Ô1Õ then UNPREDICTABLE; // t = UInt(Rt); t2 = t+1; n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); if (BitIsSet (t, 0)) return false; t2 = t + 1; n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); // if P == Ô0Õ && W == Ô1Õ then UNPREDICTABLE; if (BitIsClear (opcode, 24) && BitIsSet (opcode, 21)) return false; // if t2 == 15 || m == 15 || m == t || m == t2 then UNPREDICTABLE; if ((t2 == 15) || (m == 15) || (m == t) || (m == t2)) return false; // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t) || (n == t2))) return false; // if ArchVersion() < 6 && wback && m == n then UNPREDICTABLE; if ((ArchVersion() < 6) && wback && (m == n)) return false; break; default: return false; } uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); uint32_t Rm = ReadCoreReg (m, &success); if (!success) return false; RegisterInfo offset_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); // offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); addr_t offset_addr; if (add) offset_addr = Rn + Rm; else offset_addr = Rn - Rm; // address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusIndirectOffset (base_reg, offset_reg); // R[t] = MemA[address,4]; const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t data = MemARead (context, address, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t, data)) return false; // R[t2] = MemA[address+4,4]; data = MemARead (context, address + 4, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + t2, data)) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // A8.6.200 STRD (immediate) // Store Register Dual (immediate) calculates an address from a base register value and an immediate offset, and // stores two words from two registers to memory. It can use offset, post-indexed, or pre-indexed addressing. bool EmulateInstructionARM::EmulateSTRDImm (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); NullCheckIfThumbEE(n); offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); address = if index then offset_addr else R[n]; MemA[address,4] = R[t]; MemA[address+4,4] = R[t2]; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t t2; uint32_t n; uint32_t imm32; bool index; bool add; bool wback; switch (encoding) { case eEncodingT1: // if P == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; // t = UInt(Rt); t2 = UInt(Rt2); n = UInt(Rn); imm32 = ZeroExtend(imm8:Õ00Õ, 32); t = Bits32 (opcode, 15, 12); t2 = Bits32 (opcode, 11, 8); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 7, 0) << 2; // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (W == Ô1Õ); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = BitIsSet (opcode, 21); // if wback && (n == t || n == t2) then UNPREDICTABLE; if (wback && ((n == t) || (n == t2))) return false; // if n == 15 || BadReg(t) || BadReg(t2) then UNPREDICTABLE; if ((n == 15) || BadReg (t) || BadReg (t2)) return false; break; case eEncodingA1: // if Rt<0> == Ô1Õ then UNPREDICTABLE; // t = UInt(Rt); t2 = t+1; n = UInt(Rn); imm32 = ZeroExtend(imm4H:imm4L, 32); t = Bits32 (opcode, 15, 12); if (BitIsSet (t, 0)) return false; t2 = t + 1; n = Bits32 (opcode, 19, 16); imm32 = (Bits32 (opcode, 11, 8) << 4) | Bits32 (opcode, 3, 0); // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); // if P == Ô0Õ && W == Ô1Õ then UNPREDICTABLE; if (BitIsClear (opcode, 24) && BitIsSet (opcode, 21)) return false; // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t) || (n == t2))) return false; // if t2 == 15 then UNPREDICTABLE; if (t2 == 15) return false; break; default: return false; } RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; //offset_addr = if add then (R[n] + imm32) else (R[n] - imm32); addr_t offset_addr; if (add) offset_addr = Rn + imm32; else offset_addr = Rn - imm32; //address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; //MemA[address,4] = R[t]; RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); uint32_t data = ReadCoreReg (t, &success); if (!success) return false; EmulateInstruction::Context context; context.type = eContextRegisterStore; context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); const uint32_t addr_byte_size = GetAddressByteSize(); if (!MemAWrite (context, address, data, addr_byte_size)) return false; //MemA[address+4,4] = R[t2]; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t2, data_reg); context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, (address + 4) - Rn); data = ReadCoreReg (t2, &success); if (!success) return false; if (!MemAWrite (context, address + 4, data, addr_byte_size)) return false; //if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // A8.6.201 STRD (register) bool EmulateInstructionARM::EmulateSTRDReg (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); address = if index then offset_addr else R[n]; MemA[address,4] = R[t]; MemA[address+4,4] = R[t2]; if wback then R[n] = offset_addr; #endif bool success = false; if (ConditionPassed(opcode)) { uint32_t t; uint32_t t2; uint32_t n; uint32_t m; bool index; bool add; bool wback; switch (encoding) { case eEncodingA1: // if Rt<0> == Ô1Õ then UNPREDICTABLE; // t = UInt(Rt); t2 = t+1; n = UInt(Rn); m = UInt(Rm); t = Bits32 (opcode, 15, 12); if (BitIsSet (t, 0)) return false; t2 = t+1; n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // index = (P == Ô1Õ); add = (U == Ô1Õ); wback = (P == Ô0Õ) || (W == Ô1Õ); index = BitIsSet (opcode, 24); add = BitIsSet (opcode, 23); wback = BitIsClear (opcode, 24) || BitIsSet (opcode, 21); // if P == Ô0Õ && W == Ô1Õ then UNPREDICTABLE; if (BitIsClear (opcode, 24) && BitIsSet (opcode, 21)) return false; // if t2 == 15 || m == 15 then UNPREDICTABLE; if ((t2 == 15) || (m == 15)) return false; // if wback && (n == 15 || n == t || n == t2) then UNPREDICTABLE; if (wback && ((n == 15) || (n == t) || (n == t2))) return false; // if ArchVersion() < 6 && wback && m == n then UNPREDICTABLE; if ((ArchVersion() < 6) && wback && (m == n)) return false; break; default: return false; } RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); RegisterInfo offset_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + m, offset_reg); RegisterInfo data_reg; uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; uint32_t Rm = ReadCoreReg (m, &success); if (!success) return false; // offset_addr = if add then (R[n] + R[m]) else (R[n] - R[m]); addr_t offset_addr; if (add) offset_addr = Rn + Rm; else offset_addr = Rn - Rm; // address = if index then offset_addr else R[n]; addr_t address; if (index) address = offset_addr; else address = Rn; // MemA[address,4] = R[t]; uint32_t Rt = ReadCoreReg (t, &success); if (!success) return false; EmulateInstruction::Context context; context.type = eContextRegisterStore; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t, data_reg); context.SetRegisterToRegisterPlusIndirectOffset (base_reg, offset_reg, data_reg); const uint32_t addr_byte_size = GetAddressByteSize(); if (!MemAWrite (context, address, Rt, addr_byte_size)) return false; // MemA[address+4,4] = R[t2]; uint32_t Rt2 = ReadCoreReg (t2, &success); if (!success) return false; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + t2, data_reg); context.SetRegisterToRegisterPlusIndirectOffset (base_reg, offset_reg, data_reg); if (!MemAWrite (context, address + 4, Rt2, addr_byte_size)) return false; // if wback then R[n] = offset_addr; if (wback) { context.type = eContextAdjustBaseRegister; context.SetAddress (offset_addr); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, offset_addr)) return false; } } return true; } // A8.6.319 VLDM // Vector Load Multiple loads multiple extension registers from consecutive memory locations using an address from // an ARM core register. bool EmulateInstructionARM::EmulateVLDM (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); address = if add then R[n] else R[n]-imm32; if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; for r = 0 to regs-1 if single_regs then S[d+r] = MemA[address,4]; address = address+4; else word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = address+8; // Combine the word-aligned words in the correct order for current endianness. D[d+r] = if BigEndian() then word1:word2 else word2:word1; #endif bool success = false; if (ConditionPassed(opcode)) { bool single_regs; bool add; bool wback; uint32_t d; uint32_t n; uint32_t imm32; uint32_t regs; switch (encoding) { case eEncodingT1: case eEncodingA1: // if P == Ô0Õ && U == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; // if P == Ô0Õ && U == Ô1Õ && W == Ô1Õ && Rn == Ô1101Õ then SEE VPOP; // if P == Ô1Õ && W == Ô0Õ then SEE VLDR; // if P == U && W == Ô1Õ then UNDEFINED; if ((Bit32 (opcode, 24) == Bit32 (opcode, 23)) && BitIsSet (opcode, 21)) return false; // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with !), 101 (DB with !) // single_regs = FALSE; add = (U == Ô1Õ); wback = (W == Ô1Õ); single_regs = false; add = BitIsSet (opcode, 23); wback = BitIsSet (opcode, 21); // d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:Õ00Õ, 32); d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 7, 0) << 2; // regs = UInt(imm8) DIV 2; // If UInt(imm8) is odd, see ÒFLDMXÓ. regs = Bits32 (opcode, 7, 0) / 2; // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then UNPREDICTABLE; if (n == 15 && (wback || CurrentInstrSet() != eModeARM)) return false; // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; if ((regs == 0) || (regs > 16) || ((d + regs) > 32)) return false; break; case eEncodingT2: case eEncodingA2: // if P == Ô0Õ && U == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; // if P == Ô0Õ && U == Ô1Õ && W == Ô1Õ && Rn == Ô1101Õ then SEE VPOP; // if P == Ô1Õ && W == Ô0Õ then SEE VLDR; // if P == U && W == Ô1Õ then UNDEFINED; if ((Bit32 (opcode, 24) == Bit32 (opcode, 23)) && BitIsSet (opcode, 21)) return false; // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with !), 101 (DB with !) // single_regs = TRUE; add = (U == Ô1Õ); wback = (W == Ô1Õ); d = UInt(Vd:D); n = UInt(Rn); single_regs = true; add = BitIsSet (opcode, 23); wback = BitIsSet (opcode, 21); d = (Bits32 (opcode, 15, 12) << 1) | Bit32 (opcode, 22); n = Bits32 (opcode, 19, 16); // imm32 = ZeroExtend(imm8:Õ00Õ, 32); regs = UInt(imm8); imm32 = Bits32 (opcode, 7, 0) << 2; regs = Bits32 (opcode, 7, 0); // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then UNPREDICTABLE; if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM))) return false; // if regs == 0 || (d+regs) > 32 then UNPREDICTABLE; if ((regs == 0) || ((d + regs) > 32)) return false; break; default: return false; } RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; // address = if add then R[n] else R[n]-imm32; addr_t address; if (add) address = Rn; else address = Rn - imm32; // if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; EmulateInstruction::Context context; if (wback) { uint32_t value; if (add) value = Rn + imm32; else value = Rn - imm32; context.type = eContextAdjustBaseRegister; context.SetImmediateSigned (value - Rn); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, value)) return false; } const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; context.type = eContextRegisterLoad; // for r = 0 to regs-1 for (uint32_t r = 0; r < regs; ++r) { if (single_regs) { // S[d+r] = MemA[address,4]; address = address+4; context.SetRegisterPlusOffset (base_reg, address - Rn); uint32_t data = MemARead (context, address, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, start_reg + d + r, data)) return false; address = address + 4; } else { // word1 = MemA[address,4]; word2 = MemA[address+4,4]; address = address+8; context.SetRegisterPlusOffset (base_reg, address - Rn); uint32_t word1 = MemARead (context, address, addr_byte_size, 0, &success); if (!success) return false; context.SetRegisterPlusOffset (base_reg, (address + 4) - Rn); uint32_t word2 = MemARead (context, address + 4, addr_byte_size, 0, &success); if (!success) return false; address = address + 8; // // Combine the word-aligned words in the correct order for current endianness. // D[d+r] = if BigEndian() then word1:word2 else word2:word1; uint64_t data; if (GetByteOrder() == eByteOrderBig) { data = word1; data = (data << 32) | word2; } else { data = word2; data = (data << 32) | word1; } if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, start_reg + d + r, data)) return false; } } } return true; } // A8.6.399 VSTM // Vector Store Multiple stores multiple extension registers to consecutive memory locations using an address from an // ARM core register. bool EmulateInstructionARM::EmulateVSTM (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); address = if add then R[n] else R[n]-imm32; if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; for r = 0 to regs-1 if single_regs then MemA[address,4] = S[d+r]; address = address+4; else // Store as two word-aligned words in the correct order for current endianness. MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>; MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>; address = address+8; #endif bool success = false; if (ConditionPassed (opcode)) { bool single_regs; bool add; bool wback; uint32_t d; uint32_t n; uint32_t imm32; uint32_t regs; switch (encoding) { case eEncodingT1: case eEncodingA1: // if P == Ô0Õ && U == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; // if P == Ô1Õ && U == Ô0Õ && W == Ô1Õ && Rn == Ô1101Õ then SEE VPUSH; // if P == Ô1Õ && W == Ô0Õ then SEE VSTR; // if P == U && W == Ô1Õ then UNDEFINED; if ((Bit32 (opcode, 24) == Bit32 (opcode, 23)) && BitIsSet (opcode, 21)) return false; // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with !), 101 (DB with !) // single_regs = FALSE; add = (U == Ô1Õ); wback = (W == Ô1Õ); single_regs = false; add = BitIsSet (opcode, 23); wback = BitIsSet (opcode, 21); // d = UInt(D:Vd); n = UInt(Rn); imm32 = ZeroExtend(imm8:Õ00Õ, 32); d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); imm32 = Bits32 (opcode, 7, 0) << 2; // regs = UInt(imm8) DIV 2; // If UInt(imm8) is odd, see ÒFSTMXÓ. regs = Bits32 (opcode, 7, 0) / 2; // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then UNPREDICTABLE; if ((n == 15) && (wback || (CurrentInstrSet() != eModeARM))) return false; // if regs == 0 || regs > 16 || (d+regs) > 32 then UNPREDICTABLE; if ((regs == 0) || (regs > 16) || ((d + regs) > 32)) return false; break; case eEncodingT2: case eEncodingA2: // if P == Ô0Õ && U == Ô0Õ && W == Ô0Õ then SEE ÒRelated encodingsÓ; // if P == Ô1Õ && U == Ô0Õ && W == Ô1Õ && Rn == Ô1101Õ then SEE VPUSH; // if P == Ô1Õ && W == Ô0Õ then SEE VSTR; // if P == U && W == Ô1Õ then UNDEFINED; if ((Bit32 (opcode, 24) == Bit32 (opcode, 23)) && BitIsSet (opcode, 21)) return false; // // Remaining combinations are PUW = 010 (IA without !), 011 (IA with !), 101 (DB with !) // single_regs = TRUE; add = (U == Ô1Õ); wback = (W == Ô1Õ); d = UInt(Vd:D); n = UInt(Rn); single_regs = true; add = BitIsSet (opcode, 23); wback = BitIsSet (opcode, 21); d = (Bits32 (opcode, 15, 12) << 1) | Bit32 (opcode, 22); n = Bits32 (opcode, 19, 16); // imm32 = ZeroExtend(imm8:Õ00Õ, 32); regs = UInt(imm8); imm32 = Bits32 (opcode, 7, 0) << 2; regs = Bits32 (opcode, 7, 0); // if n == 15 && (wback || CurrentInstrSet() != InstrSet_ARM) then UNPREDICTABLE; if ((n == 15) && (wback || (CurrentInstrSet () != eModeARM))) return false; // if regs == 0 || (d+regs) > 32 then UNPREDICTABLE; if ((regs == 0) || ((d + regs) > 32)) return false; break; default: return false; } RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; // address = if add then R[n] else R[n]-imm32; addr_t address; if (add) address = Rn; else address = Rn - imm32; EmulateInstruction::Context context; // if wback then R[n] = if add then R[n]+imm32 else R[n]-imm32; if (wback) { uint32_t value; if (add) value = Rn + imm32; else value = Rn - imm32; context.type = eContextAdjustBaseRegister; context.SetRegisterPlusOffset (base_reg, value - Rn); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, value)) return false; } const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t start_reg = single_regs ? dwarf_s0 : dwarf_d0; context.type = eContextRegisterStore; // for r = 0 to regs-1 for (uint32_t r = 0; r < regs; ++r) { if (single_regs) { // MemA[address,4] = S[d+r]; address = address+4; uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, start_reg + d + r, 0, &success); if (!success) return false; RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, start_reg + d + r, data_reg); context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); if (!MemAWrite (context, address, data, addr_byte_size)) return false; address = address + 4; } else { // // Store as two word-aligned words in the correct order for current endianness. // MemA[address,4] = if BigEndian() then D[d+r]<63:32> else D[d+r]<31:0>; // MemA[address+4,4] = if BigEndian() then D[d+r]<31:0> else D[d+r]<63:32>; uint64_t data = ReadRegisterUnsigned (eRegisterKindDWARF, start_reg + d + r, 0, &success); if (!success) return false; RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, start_reg + d + r, data_reg); if (GetByteOrder() == eByteOrderBig) { context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); if (!MemAWrite (context, address, Bits64 (data, 63, 32), addr_byte_size)) return false; context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, (address + 4) - Rn); if (!MemAWrite (context, address+ 4, Bits64 (data, 31, 0), addr_byte_size)) return false; } else { context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); if (!MemAWrite (context, address, Bits64 (data, 31, 0), addr_byte_size)) return false; context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, (address + 4) - Rn); if (!MemAWrite (context, address + 4, Bits64 (data, 63, 32), addr_byte_size)) return false; } // address = address+8; address = address + 8; } } } return true; } // A8.6.320 // This instruciton loads a single extension register fronm memory, using an address from an ARM core register, with // an optional offset. bool EmulateInstructionARM::EmulateVLDR (const uint32_t opcode, ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); base = if n == 15 then Align(PC,4) else R[n]; address = if add then (base + imm32) else (base - imm32); if single_reg then S[d] = MemA[address,4]; else word1 = MemA[address,4]; word2 = MemA[address+4,4]; // Combine the word-aligned words in the correct order for current endianness. D[d] = if BigEndian() then word1:word2 else word2:word1; #endif bool success = false; if (ConditionPassed (opcode)) { bool single_reg; bool add; uint32_t imm32; uint32_t d; uint32_t n; switch (encoding) { case eEncodingT1: case eEncodingA1: // single_reg = FALSE; add = (U == Ô1Õ); imm32 = ZeroExtend(imm8:Õ00Õ, 32); single_reg = false; add = BitIsSet (opcode, 23); imm32 = Bits32 (opcode, 7, 0) << 2; // d = UInt(D:Vd); n = UInt(Rn); d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); break; case eEncodingT2: case eEncodingA2: // single_reg = TRUE; add = (U == Ô1Õ); imm32 = ZeroExtend(imm8:Õ00Õ, 32); single_reg = true; add = BitIsSet (opcode, 23); imm32 = Bits32 (opcode, 7, 0) << 2; // d = UInt(Vd:D); n = UInt(Rn); d = (Bits32 (opcode, 15, 12) << 1) | Bit32 (opcode, 22); n = Bits32 (opcode, 19, 16); break; default: return false; } RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; // base = if n == 15 then Align(PC,4) else R[n]; uint32_t base; if (n == 15) base = AlignPC (Rn); else base = Rn; // address = if add then (base + imm32) else (base - imm32); addr_t address; if (add) address = base + imm32; else address = base - imm32; const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t start_reg = single_reg ? dwarf_s0 : dwarf_d0; EmulateInstruction::Context context; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - base); if (single_reg) { // S[d] = MemA[address,4]; uint32_t data = MemARead (context, address, addr_byte_size, 0, &success); if (!success) return false; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, start_reg + d, data)) return false; } else { // word1 = MemA[address,4]; word2 = MemA[address+4,4]; uint32_t word1 = MemARead (context, address, addr_byte_size, 0, &success); if (!success) return false; context.SetRegisterPlusOffset (base_reg, (address + 4) - base); uint32_t word2 = MemARead (context, address + 4, addr_byte_size, 0, &success); if (!success) return false; // // Combine the word-aligned words in the correct order for current endianness. // D[d] = if BigEndian() then word1:word2 else word2:word1; uint64_t data64; if (GetByteOrder() == eByteOrderBig) { data64 = word1; data64 = (data64 << 32) | word2; } else { data64 = word2; data64 = (data64 << 32) | word1; } if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, start_reg + d, data64)) return false; } } return true; } // A8.6.400 VSTR // This instruction stores a signle extension register to memory, using an address from an ARM core register, with an // optional offset. bool EmulateInstructionARM::EmulateVSTR (const uint32_t opcode, ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckVFPEnabled(TRUE); NullCheckIfThumbEE(n); address = if add then (R[n] + imm32) else (R[n] - imm32); if single_reg then MemA[address,4] = S[d]; else // Store as two word-aligned words in the correct order for current endianness. MemA[address,4] = if BigEndian() then D[d]<63:32> else D[d]<31:0>; MemA[address+4,4] = if BigEndian() then D[d]<31:0> else D[d]<63:32>; #endif bool success = false; if (ConditionPassed (opcode)) { bool single_reg; bool add; uint32_t imm32; uint32_t d; uint32_t n; switch (encoding) { case eEncodingT1: case eEncodingA1: // single_reg = FALSE; add = (U == Ô1Õ); imm32 = ZeroExtend(imm8:Õ00Õ, 32); single_reg = false; add = BitIsSet (opcode, 23); imm32 = Bits32 (opcode, 7, 0) << 2; // d = UInt(D:Vd); n = UInt(Rn); d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); // if n == 15 && CurrentInstrSet() != InstrSet_ARM then UNPREDICTABLE; if ((n == 15) && (CurrentInstrSet() != eModeARM)) return false; break; case eEncodingT2: case eEncodingA2: // single_reg = TRUE; add = (U == Ô1Õ); imm32 = ZeroExtend(imm8:Õ00Õ, 32); single_reg = true; add = BitIsSet (opcode, 23); imm32 = Bits32 (opcode, 7, 0) << 2; // d = UInt(Vd:D); n = UInt(Rn); d = (Bits32 (opcode, 15, 12) << 1) | Bit32 (opcode, 22); n = Bits32 (opcode, 19, 16); // if n == 15 && CurrentInstrSet() != InstrSet_ARM then UNPREDICTABLE; if ((n == 15) && (CurrentInstrSet() != eModeARM)) return false; break; default: return false; } RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; // address = if add then (R[n] + imm32) else (R[n] - imm32); addr_t address; if (add) address = Rn + imm32; else address = Rn - imm32; const uint32_t addr_byte_size = GetAddressByteSize(); uint32_t start_reg = single_reg ? dwarf_s0 : dwarf_d0; RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, start_reg + d, data_reg); EmulateInstruction::Context context; context.type = eContextRegisterStore; context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); if (single_reg) { // MemA[address,4] = S[d]; uint32_t data = ReadRegisterUnsigned (eRegisterKindDWARF, start_reg + d, 0, &success); if (!success) return false; if (!MemAWrite (context, address, data, addr_byte_size)) return false; } else { // // Store as two word-aligned words in the correct order for current endianness. // MemA[address,4] = if BigEndian() then D[d]<63:32> else D[d]<31:0>; // MemA[address+4,4] = if BigEndian() then D[d]<31:0> else D[d]<63:32>; uint64_t data = ReadRegisterUnsigned (eRegisterKindDWARF, start_reg + d, 0, &success); if (!success) return false; if (GetByteOrder() == eByteOrderBig) { if (!MemAWrite (context, address, Bits64 (data, 63, 32), addr_byte_size)) return false; context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, (address + 4) - Rn); if (!MemAWrite (context, address + 4, Bits64 (data, 31, 0), addr_byte_size)) return false; } else { if (!MemAWrite (context, address, Bits64 (data, 31, 0), addr_byte_size)) return false; context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, (address + 4) - Rn); if (!MemAWrite (context, address + 4, Bits64 (data, 63, 32), addr_byte_size)) return false; } } } return true; } // A8.6.307 VLDI1 (multiple single elements) // This instruction loads elements from memory into one, two, three or four registers, without de-interleaving. Every // element of each register is loaded. bool EmulateInstructionARM::EmulateVLD1Multiple (const uint32_t opcode, ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); for r = 0 to regs-1 for e = 0 to elements-1 Elem[D[d+r],e,esize] = MemU[address,ebytes]; address = address + ebytes; #endif bool success = false; if (ConditionPassed (opcode)) { uint32_t regs; uint32_t alignment; uint32_t ebytes; uint32_t esize; uint32_t elements; uint32_t d; uint32_t n; uint32_t m; bool wback; bool register_index; switch (encoding) { case eEncodingT1: case eEncodingA1: { // case type of // when Ô0111Õ // regs = 1; if align<1> == Ô1Õ then UNDEFINED; // when Ô1010Õ // regs = 2; if align == Ô11Õ then UNDEFINED; // when Ô0110Õ // regs = 3; if align<1> == Ô1Õ then UNDEFINED; // when Ô0010Õ // regs = 4; // otherwise // SEE ÒRelated encodingsÓ; uint32_t type = Bits32 (opcode, 11, 8); uint32_t align = Bits32 (opcode, 5, 4); if (type == 7) // '0111' { regs = 1; if (BitIsSet (align, 1)) return false; } else if (type == 10) // '1010' { regs = 2; if (align == 3) return false; } else if (type == 6) // '0110' { regs = 3; if (BitIsSet (align, 1)) return false; } else if (type == 2) // '0010' { regs = 4; } else return false; // alignment = if align == Ô00Õ then 1 else 4 << UInt(align); if (align == 0) alignment = 1; else alignment = 4 << align; // ebytes = 1 << UInt(size); esize = 8 * ebytes; elements = 8 DIV ebytes; ebytes = 1 << Bits32 (opcode, 7, 6); esize = 8 * ebytes; elements = 8 / ebytes; // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 15); m = Bits32 (opcode, 3, 0); // wback = (m != 15); register_index = (m != 15 && m != 13); wback = (m != 15); register_index = ((m != 15) && (m != 13)); // if d+regs > 32 then UNPREDICTABLE; if ((d + regs) > 32) return false; } break; default: return false; } RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; // address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); addr_t address = Rn; if ((address % alignment) != 0) return false; EmulateInstruction::Context context; // if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); if (wback) { uint32_t Rm = ReadCoreReg (m, &success); if (!success) return false; uint32_t offset; if (register_index) offset = Rm; else offset = 8 * regs; uint32_t value = Rn + offset; context.type = eContextAdjustBaseRegister; context.SetRegisterPlusOffset (base_reg, offset); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, value)) return false; } // for r = 0 to regs-1 for (uint32_t r = 0; r < regs; ++r) { // for e = 0 to elements-1 uint64_t assembled_data = 0; for (uint32_t e = 0; e < elements; ++e) { // Elem[D[d+r],e,esize] = MemU[address,ebytes]; context.type = eContextRegisterLoad; context.SetRegisterPlusOffset (base_reg, address - Rn); uint64_t data = MemURead (context, address, ebytes, 0, &success); if (!success) return false; assembled_data = (data << (e * esize)) | assembled_data; // New data goes to the left of existing data // address = address + ebytes; address = address + ebytes; } if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_d0 + d + r, assembled_data)) return false; } } return true; } // A8.6.308 VLD1 (single element to one lane) // bool EmulateInstructionARM::EmulateVLD1Single (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); Elem[D[d],index,esize] = MemU[address,ebytes]; #endif bool success = false; if (ConditionPassed (opcode)) { uint32_t ebytes; uint32_t esize; uint32_t index; uint32_t alignment; uint32_t d; uint32_t n; uint32_t m; bool wback; bool register_index; switch (encoding) { case eEncodingT1: case eEncodingA1: { uint32_t size = Bits32 (opcode, 11, 10); uint32_t index_align = Bits32 (opcode, 7, 4); // if size == Ô11Õ then SEE VLD1 (single element to all lanes); if (size == 3) return EmulateVLD1SingleAll (opcode, encoding); // case size of if (size == 0) // when '00' { // if index_align<0> != Ô0Õ then UNDEFINED; if (BitIsClear (index_align, 0)) return false; // ebytes = 1; esize = 8; index = UInt(index_align<3:1>); alignment = 1; ebytes = 1; esize = 8; index = Bits32 (index_align, 3, 1); alignment = 1; } else if (size == 1) // when Ô01Õ { // if index_align<1> != Ô0Õ then UNDEFINED; if (BitIsClear (index_align, 1)) return false; // ebytes = 2; esize = 16; index = UInt(index_align<3:2>); ebytes = 2; esize = 16; index = Bits32 (index_align, 3, 2); // alignment = if index_align<0> == Ô0Õ then 1 else 2; if (BitIsClear (index_align, 0)) alignment = 1; else alignment = 2; } else if (size == 2) // when Ô10Õ { // if index_align<2> != Ô0Õ then UNDEFINED; if (BitIsClear (index_align, 2)) return false; // if index_align<1:0> != Ô00Õ && index_align<1:0> != Ô11Õ then UNDEFINED; if ((Bits32 (index_align, 1, 0) != 0) && (Bits32 (index_align, 1, 0) != 3)) return false; // ebytes = 4; esize = 32; index = UInt(index_align<3>); ebytes = 4; esize = 32; index = Bit32 (index_align, 3); // alignment = if index_align<1:0> == Ô00Õ then 1 else 4; if (Bits32 (index_align, 1, 0) == 0) alignment = 1; else alignment = 4; } else { return false; } // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // wback = (m != 15); register_index = (m != 15 && m != 13); if n == 15 then UNPREDICTABLE; wback = (m != 15); register_index = ((m != 15) && (m != 13)); if (n == 15) return false; } break; default: return false; } RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; // address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); addr_t address = Rn; if ((address % alignment) != 0) return false; EmulateInstruction::Context context; // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); if (wback) { uint32_t Rm = ReadCoreReg (m, &success); if (!success) return false; uint32_t offset; if (register_index) offset = Rm; else offset = ebytes; uint32_t value = Rn + offset; context.type = eContextAdjustBaseRegister; context.SetRegisterPlusOffset (base_reg, offset); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, value)) return false; } // Elem[D[d],index,esize] = MemU[address,ebytes]; uint32_t element = MemURead (context, address, esize, 0, &success); if (!success) return false; element = element << (index * esize); uint64_t reg_data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_d0 + d, 0, &success); if (!success) return false; uint64_t all_ones = -1; uint64_t mask = all_ones << ((index+1) * esize); // mask is all 1's to left of where 'element' goes, & all 0's // at element & to the right of element. if (index > 0) mask = mask | Bits64 (all_ones, (index * esize) - 1, 0); // add 1's to the right of where 'element' goes. // now mask should be 0's where element goes & 1's // everywhere else. uint64_t masked_reg = reg_data & mask; // Take original reg value & zero out 'element' bits reg_data = masked_reg & element; // Put 'element' into those bits in reg_data. context.type = eContextRegisterLoad; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + d, reg_data)) return false; } return true; } // A8.6.391 VST1 (multiple single elements) // Vector Store (multiple single elements) stores elements to memory from one, two, three, or four regsiters, without // interleaving. Every element of each register is stored. bool EmulateInstructionARM::EmulateVST1Multiple (const uint32_t opcode, ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); for r = 0 to regs-1 for e = 0 to elements-1 MemU[address,ebytes] = Elem[D[d+r],e,esize]; address = address + ebytes; #endif bool success = false; if (ConditionPassed (opcode)) { uint32_t regs; uint32_t alignment; uint32_t ebytes; uint32_t esize; uint32_t elements; uint32_t d; uint32_t n; uint32_t m; bool wback; bool register_index; switch (encoding) { case eEncodingT1: case eEncodingA1: { uint32_t type = Bits32 (opcode, 11, 8); uint32_t align = Bits32 (opcode, 5, 4); // case type of if (type == 7) // when Ô0111Õ { // regs = 1; if align<1> == Ô1Õ then UNDEFINED; regs = 1; if (BitIsSet (align, 1)) return false; } else if (type == 10) // when Ô1010Õ { // regs = 2; if align == Ô11Õ then UNDEFINED; regs = 2; if (align == 3) return false; } else if (type == 6) // when Ô0110Õ { // regs = 3; if align<1> == Ô1Õ then UNDEFINED; regs = 3; if (BitIsSet (align, 1)) return false; } else if (type == 2) // when Ô0010Õ // regs = 4; regs = 4; else // otherwise // SEE ÒRelated encodingsÓ; return false; // alignment = if align == Ô00Õ then 1 else 4 << UInt(align); if (align == 0) alignment = 1; else alignment = 4 << align; // ebytes = 1 << UInt(size); esize = 8 * ebytes; elements = 8 DIV ebytes; ebytes = 1 << Bits32 (opcode,7, 6); esize = 8 * ebytes; elements = 8 / ebytes; // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // wback = (m != 15); register_index = (m != 15 && m != 13); wback = (m != 15); register_index = ((m != 15) && (m != 13)); // if d+regs > 32 then UNPREDICTABLE; if n == 15 then UNPREDICTABLE; if ((d + regs) > 32) return false; if (n == 15) return false; } break; default: return false; } RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; // address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); addr_t address = Rn; if ((address % alignment) != 0) return false; EmulateInstruction::Context context; // if wback then R[n] = R[n] + (if register_index then R[m] else 8*regs); if (wback) { uint32_t Rm = ReadCoreReg (m, &success); if (!success) return false; uint32_t offset; if (register_index) offset = Rm; else offset = 8 * regs; context.type = eContextAdjustBaseRegister; context.SetRegisterPlusOffset (base_reg, offset); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, Rn + offset)) return false; } RegisterInfo data_reg; context.type = eContextRegisterStore; // for r = 0 to regs-1 for (uint32_t r = 0; r < regs; ++r) { GetRegisterInfo (eRegisterKindDWARF, dwarf_d0 + d + r, data_reg); uint64_t register_data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_d0 + d + r, 0, &success); if (!success) return false; // for e = 0 to elements-1 for (uint32_t e = 0; e < elements; ++e) { // MemU[address,ebytes] = Elem[D[d+r],e,esize]; uint64_t word = Bits64 (register_data, ((e + 1) * esize) - 1, e * esize); context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); if (!MemUWrite (context, address, word, ebytes)) return false; // address = address + ebytes; address = address + ebytes; } } } return true; } // A8.6.392 VST1 (single element from one lane) // This instruction stores one element to memory from one element of a register. bool EmulateInstructionARM::EmulateVST1Single (const uint32_t opcode, ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); MemU[address,ebytes] = Elem[D[d],index,esize]; #endif bool success = false; if (ConditionPassed (opcode)) { uint32_t ebytes; uint32_t esize; uint32_t index; uint32_t alignment; uint32_t d; uint32_t n; uint32_t m; bool wback; bool register_index; switch (encoding) { case eEncodingT1: case eEncodingA1: { uint32_t size = Bits32 (opcode, 11, 10); uint32_t index_align = Bits32 (opcode, 7, 4); // if size == Ô11Õ then UNDEFINED; if (size == 3) return false; // case size of if (size == 0) // when Ô00Õ { // if index_align<0> != Ô0Õ then UNDEFINED; if (BitIsClear (index_align, 0)) return false; // ebytes = 1; esize = 8; index = UInt(index_align<3:1>); alignment = 1; ebytes = 1; esize = 8; index = Bits32 (index_align, 3, 1); alignment = 1; } else if (size == 1) // when Ô01Õ { // if index_align<1> != Ô0Õ then UNDEFINED; if (BitIsClear (index_align, 1)) return false; // ebytes = 2; esize = 16; index = UInt(index_align<3:2>); ebytes = 2; esize = 16; index = Bits32 (index_align, 3, 2); // alignment = if index_align<0> == Ô0Õ then 1 else 2; if (BitIsClear (index_align, 0)) alignment = 1; else alignment = 2; } else if (size == 2) // when Ô10Õ { // if index_align<2> != Ô0Õ then UNDEFINED; if (BitIsClear (index_align, 2)) return false; // if index_align<1:0> != Ô00Õ && index_align<1:0> != Ô11Õ then UNDEFINED; if ((Bits32 (index_align, 1, 0) != 0) && (Bits32 (index_align, 1, 0) != 3)) return false; // ebytes = 4; esize = 32; index = UInt(index_align<3>); ebytes = 4; esize = 32; index = Bit32 (index_align, 3); // alignment = if index_align<1:0> == Ô00Õ then 1 else 4; if (Bits32 (index_align, 1, 0) == 0) alignment = 1; else alignment = 4; } else { return false; } // d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); // wback = (m != 15); register_index = (m != 15 && m != 13); if n == 15 then UNPREDICTABLE; wback = (m != 15); register_index = ((m != 15) && (m != 13)); if (n == 15) return false; } break; default: return false; } RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; // address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); addr_t address = Rn; if ((address % alignment) != 0) return false; EmulateInstruction::Context context; // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); if (wback) { uint32_t Rm = ReadCoreReg (m, &success); if (!success) return false; uint32_t offset; if (register_index) offset = Rm; else offset = ebytes; context.type = eContextAdjustBaseRegister; context.SetRegisterPlusOffset (base_reg, offset); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, Rn + offset)) return false; } // MemU[address,ebytes] = Elem[D[d],index,esize]; uint64_t register_data = ReadRegisterUnsigned (eRegisterKindDWARF, dwarf_d0 + d, 0, &success); if (!success) return false; uint64_t word = Bits64 (register_data, ((index + 1) * esize) - 1, index * esize); RegisterInfo data_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_d0 + d, data_reg); context.type = eContextRegisterStore; context.SetRegisterToRegisterPlusOffset (data_reg, base_reg, address - Rn); if (!MemUWrite (context, address, word, ebytes)) return false; } return true; } // A8.6.309 VLD1 (single element to all lanes) // This instruction loads one element from memory into every element of one or two vectors. bool EmulateInstructionARM::EmulateVLD1SingleAll (const uint32_t opcode, const ARMEncoding encoding) { #if 0 if ConditionPassed() then EncodingSpecificOperations(); CheckAdvSIMDEnabled(); NullCheckIfThumbEE(n); address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); replicated_element = Replicate(MemU[address,ebytes], elements); for r = 0 to regs-1 D[d+r] = replicated_element; #endif bool success = false; if (ConditionPassed (opcode)) { uint32_t ebytes; uint32_t elements; uint32_t regs; uint32_t alignment; uint32_t d; uint32_t n; uint32_t m; bool wback; bool register_index; switch (encoding) { case eEncodingT1: case eEncodingA1: { //if size == Ô11Õ || (size == Ô00Õ && a == Ô1Õ) then UNDEFINED; uint32_t size = Bits32 (opcode, 7, 6); if ((size == 3) || ((size == 0) && BitIsSet (opcode, 4))) return false; //ebytes = 1 << UInt(size); elements = 8 DIV ebytes; regs = if T == Ô0Õ then 1 else 2; ebytes = 1 << size; elements = 8 / ebytes; if (BitIsClear (opcode, 5)) regs = 1; else regs = 2; //alignment = if a == Ô0Õ then 1 else ebytes; if (BitIsClear (opcode, 4)) alignment = 1; else alignment = ebytes; //d = UInt(D:Vd); n = UInt(Rn); m = UInt(Rm); d = (Bit32 (opcode, 22) << 4) | Bits32 (opcode, 15, 12); n = Bits32 (opcode, 19, 16); m = Bits32 (opcode, 3, 0); //wback = (m != 15); register_index = (m != 15 && m != 13); wback = (m != 15); register_index = ((m != 15) && (m != 13)); //if d+regs > 32 then UNPREDICTABLE; if n == 15 then UNPREDICTABLE; if ((d + regs) > 32) return false; if (n == 15) return false; } break; default: return false; } RegisterInfo base_reg; GetRegisterInfo (eRegisterKindDWARF, dwarf_r0 + n, base_reg); uint32_t Rn = ReadCoreReg (n, &success); if (!success) return false; // address = R[n]; if (address MOD alignment) != 0 then GenerateAlignmentException(); addr_t address = Rn; if ((address % alignment) != 0) return false; EmulateInstruction::Context context; // if wback then R[n] = R[n] + (if register_index then R[m] else ebytes); if (wback) { uint32_t Rm = ReadCoreReg (m, &success); if (!success) return false; uint32_t offset; if (register_index) offset = Rm; else offset = ebytes; context.type = eContextAdjustBaseRegister; context.SetRegisterPlusOffset (base_reg, offset); if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_r0 + n, Rn + offset)) return false; } // replicated_element = Replicate(MemU[address,ebytes], elements); context.type = eContextRegisterLoad; uint64_t word = MemURead (context, address, ebytes, 0, &success); if (!success) return false; uint64_t replicated_element = 0; uint32_t esize = ebytes * 8; for (uint32_t e = 0; e < elements; ++e) replicated_element = (replicated_element << esize) | Bits64 (word, esize - 1, 0); // for r = 0 to regs-1 for (uint32_t r = 0; r < regs; ++r) { // D[d+r] = replicated_element; if (!WriteRegisterUnsigned (context, eRegisterKindDWARF, dwarf_d0 + d + r, replicated_element)) return false; } } return true; } // B6.2.13 SUBS PC, LR and related instructions //The SUBS PC, LR, #" }, { 0x0fff0fff, 0x052d0004, ARMvAll, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulatePUSH, "push " }, // set r7 to point to a stack offset { 0x0ffff000, 0x028d7000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADDRdSPImm, "add r7, sp, #" }, { 0x0ffff000, 0x024c7000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBR7IPImm, "sub r7, ip, #"}, // copy the stack pointer to ip { 0x0fffffff, 0x01a0c00d, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateMOVRdSP, "mov ip, sp" }, { 0x0ffff000, 0x028dc000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateADDRdSPImm, "add ip, sp, #" }, { 0x0ffff000, 0x024dc000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBIPSPImm, "sub ip, sp, #"}, // adjust the stack pointer { 0x0ffff000, 0x024dd000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPImm, "sub sp, sp, #"}, { 0x0fef0010, 0x004d0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSUBSPReg, "sub{s} , sp, {,}" }, // push one register // if Rn == '1101' && imm12 == '000000000100' then SEE PUSH; { 0x0e5f0000, 0x040d0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSTRRtSP, "str Rt, [sp, #-imm12]!" }, // vector push consecutive extension register(s) { 0x0fbf0f00, 0x0d2d0b00, ARMV6T2_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPUSH, "vpush.64 "}, { 0x0fbf0f00, 0x0d2d0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPUSH, "vpush.32 "}, //---------------------------------------------------------------------- // Epilogue instructions //---------------------------------------------------------------------- { 0x0fff0000, 0x08bd0000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulatePOP, "pop "}, { 0x0fff0fff, 0x049d0004, ARMvAll, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulatePOP, "pop "}, { 0x0fbf0f00, 0x0cbd0b00, ARMV6T2_ABOVE, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.64 "}, { 0x0fbf0f00, 0x0cbd0a00, ARMV6T2_ABOVE, eEncodingA2, No_VFP, eSize32, &EmulateInstructionARM::EmulateVPOP, "vpop.32 "}, //---------------------------------------------------------------------- // Supervisor Call (previously Software Interrupt) //---------------------------------------------------------------------- { 0x0f000000, 0x0f000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateSVC, "svc #imm24"}, //---------------------------------------------------------------------- // Branch instructions //---------------------------------------------------------------------- { 0x0f000000, 0x0a000000, ARMvAll, eEncodingA1, No_VFP, eSize32, &EmulateInstructionARM::EmulateB, "b #imm24"}, // To resolve ambiguity, "blx