mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-28 08:15:29 +03:00
Native Code Execution (NCE)
It's finally here, we're executing code completely natively. This is something that would completely break the project if it wasn't possible.
This commit is contained in:
parent
182729b247
commit
92c1491a84
@ -10,7 +10,6 @@ add_subdirectory("libraries/fmt")
|
||||
|
||||
set(source_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp)
|
||||
|
||||
include_directories(libraries/unicorn/include)
|
||||
include_directories(${source_DIR})
|
||||
|
||||
add_library(lightswitch SHARED
|
||||
@ -24,5 +23,5 @@ add_library(lightswitch SHARED
|
||||
${source_DIR}/switch/common.cpp
|
||||
${source_DIR}/switch/loader/nro.cpp
|
||||
)
|
||||
target_link_libraries(lightswitch ${CMAKE_SOURCE_DIR}/libraries/unicorn/libunicorn.a fmt tinyxml2)
|
||||
target_link_libraries(lightswitch fmt tinyxml2)
|
||||
target_compile_options(lightswitch PRIVATE -Wno-c++17-extensions)
|
||||
|
@ -1,307 +0,0 @@
|
||||
/* Unicorn Emulator Engine */
|
||||
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015-2017 */
|
||||
/* This file is released under LGPL2.
|
||||
See COPYING.LGPL2 in root directory for more details
|
||||
*/
|
||||
|
||||
#ifndef UNICORN_ARM64_H
|
||||
#define UNICORN_ARM64_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4201)
|
||||
#endif
|
||||
|
||||
//> ARM64 registers
|
||||
typedef enum uc_arm64_reg {
|
||||
UC_ARM64_REG_INVALID = 0,
|
||||
|
||||
UC_ARM64_REG_X29,
|
||||
UC_ARM64_REG_X30,
|
||||
UC_ARM64_REG_NZCV,
|
||||
UC_ARM64_REG_SP,
|
||||
UC_ARM64_REG_WSP,
|
||||
UC_ARM64_REG_WZR,
|
||||
UC_ARM64_REG_XZR,
|
||||
UC_ARM64_REG_B0,
|
||||
UC_ARM64_REG_B1,
|
||||
UC_ARM64_REG_B2,
|
||||
UC_ARM64_REG_B3,
|
||||
UC_ARM64_REG_B4,
|
||||
UC_ARM64_REG_B5,
|
||||
UC_ARM64_REG_B6,
|
||||
UC_ARM64_REG_B7,
|
||||
UC_ARM64_REG_B8,
|
||||
UC_ARM64_REG_B9,
|
||||
UC_ARM64_REG_B10,
|
||||
UC_ARM64_REG_B11,
|
||||
UC_ARM64_REG_B12,
|
||||
UC_ARM64_REG_B13,
|
||||
UC_ARM64_REG_B14,
|
||||
UC_ARM64_REG_B15,
|
||||
UC_ARM64_REG_B16,
|
||||
UC_ARM64_REG_B17,
|
||||
UC_ARM64_REG_B18,
|
||||
UC_ARM64_REG_B19,
|
||||
UC_ARM64_REG_B20,
|
||||
UC_ARM64_REG_B21,
|
||||
UC_ARM64_REG_B22,
|
||||
UC_ARM64_REG_B23,
|
||||
UC_ARM64_REG_B24,
|
||||
UC_ARM64_REG_B25,
|
||||
UC_ARM64_REG_B26,
|
||||
UC_ARM64_REG_B27,
|
||||
UC_ARM64_REG_B28,
|
||||
UC_ARM64_REG_B29,
|
||||
UC_ARM64_REG_B30,
|
||||
UC_ARM64_REG_B31,
|
||||
UC_ARM64_REG_D0,
|
||||
UC_ARM64_REG_D1,
|
||||
UC_ARM64_REG_D2,
|
||||
UC_ARM64_REG_D3,
|
||||
UC_ARM64_REG_D4,
|
||||
UC_ARM64_REG_D5,
|
||||
UC_ARM64_REG_D6,
|
||||
UC_ARM64_REG_D7,
|
||||
UC_ARM64_REG_D8,
|
||||
UC_ARM64_REG_D9,
|
||||
UC_ARM64_REG_D10,
|
||||
UC_ARM64_REG_D11,
|
||||
UC_ARM64_REG_D12,
|
||||
UC_ARM64_REG_D13,
|
||||
UC_ARM64_REG_D14,
|
||||
UC_ARM64_REG_D15,
|
||||
UC_ARM64_REG_D16,
|
||||
UC_ARM64_REG_D17,
|
||||
UC_ARM64_REG_D18,
|
||||
UC_ARM64_REG_D19,
|
||||
UC_ARM64_REG_D20,
|
||||
UC_ARM64_REG_D21,
|
||||
UC_ARM64_REG_D22,
|
||||
UC_ARM64_REG_D23,
|
||||
UC_ARM64_REG_D24,
|
||||
UC_ARM64_REG_D25,
|
||||
UC_ARM64_REG_D26,
|
||||
UC_ARM64_REG_D27,
|
||||
UC_ARM64_REG_D28,
|
||||
UC_ARM64_REG_D29,
|
||||
UC_ARM64_REG_D30,
|
||||
UC_ARM64_REG_D31,
|
||||
UC_ARM64_REG_H0,
|
||||
UC_ARM64_REG_H1,
|
||||
UC_ARM64_REG_H2,
|
||||
UC_ARM64_REG_H3,
|
||||
UC_ARM64_REG_H4,
|
||||
UC_ARM64_REG_H5,
|
||||
UC_ARM64_REG_H6,
|
||||
UC_ARM64_REG_H7,
|
||||
UC_ARM64_REG_H8,
|
||||
UC_ARM64_REG_H9,
|
||||
UC_ARM64_REG_H10,
|
||||
UC_ARM64_REG_H11,
|
||||
UC_ARM64_REG_H12,
|
||||
UC_ARM64_REG_H13,
|
||||
UC_ARM64_REG_H14,
|
||||
UC_ARM64_REG_H15,
|
||||
UC_ARM64_REG_H16,
|
||||
UC_ARM64_REG_H17,
|
||||
UC_ARM64_REG_H18,
|
||||
UC_ARM64_REG_H19,
|
||||
UC_ARM64_REG_H20,
|
||||
UC_ARM64_REG_H21,
|
||||
UC_ARM64_REG_H22,
|
||||
UC_ARM64_REG_H23,
|
||||
UC_ARM64_REG_H24,
|
||||
UC_ARM64_REG_H25,
|
||||
UC_ARM64_REG_H26,
|
||||
UC_ARM64_REG_H27,
|
||||
UC_ARM64_REG_H28,
|
||||
UC_ARM64_REG_H29,
|
||||
UC_ARM64_REG_H30,
|
||||
UC_ARM64_REG_H31,
|
||||
UC_ARM64_REG_Q0,
|
||||
UC_ARM64_REG_Q1,
|
||||
UC_ARM64_REG_Q2,
|
||||
UC_ARM64_REG_Q3,
|
||||
UC_ARM64_REG_Q4,
|
||||
UC_ARM64_REG_Q5,
|
||||
UC_ARM64_REG_Q6,
|
||||
UC_ARM64_REG_Q7,
|
||||
UC_ARM64_REG_Q8,
|
||||
UC_ARM64_REG_Q9,
|
||||
UC_ARM64_REG_Q10,
|
||||
UC_ARM64_REG_Q11,
|
||||
UC_ARM64_REG_Q12,
|
||||
UC_ARM64_REG_Q13,
|
||||
UC_ARM64_REG_Q14,
|
||||
UC_ARM64_REG_Q15,
|
||||
UC_ARM64_REG_Q16,
|
||||
UC_ARM64_REG_Q17,
|
||||
UC_ARM64_REG_Q18,
|
||||
UC_ARM64_REG_Q19,
|
||||
UC_ARM64_REG_Q20,
|
||||
UC_ARM64_REG_Q21,
|
||||
UC_ARM64_REG_Q22,
|
||||
UC_ARM64_REG_Q23,
|
||||
UC_ARM64_REG_Q24,
|
||||
UC_ARM64_REG_Q25,
|
||||
UC_ARM64_REG_Q26,
|
||||
UC_ARM64_REG_Q27,
|
||||
UC_ARM64_REG_Q28,
|
||||
UC_ARM64_REG_Q29,
|
||||
UC_ARM64_REG_Q30,
|
||||
UC_ARM64_REG_Q31,
|
||||
UC_ARM64_REG_S0,
|
||||
UC_ARM64_REG_S1,
|
||||
UC_ARM64_REG_S2,
|
||||
UC_ARM64_REG_S3,
|
||||
UC_ARM64_REG_S4,
|
||||
UC_ARM64_REG_S5,
|
||||
UC_ARM64_REG_S6,
|
||||
UC_ARM64_REG_S7,
|
||||
UC_ARM64_REG_S8,
|
||||
UC_ARM64_REG_S9,
|
||||
UC_ARM64_REG_S10,
|
||||
UC_ARM64_REG_S11,
|
||||
UC_ARM64_REG_S12,
|
||||
UC_ARM64_REG_S13,
|
||||
UC_ARM64_REG_S14,
|
||||
UC_ARM64_REG_S15,
|
||||
UC_ARM64_REG_S16,
|
||||
UC_ARM64_REG_S17,
|
||||
UC_ARM64_REG_S18,
|
||||
UC_ARM64_REG_S19,
|
||||
UC_ARM64_REG_S20,
|
||||
UC_ARM64_REG_S21,
|
||||
UC_ARM64_REG_S22,
|
||||
UC_ARM64_REG_S23,
|
||||
UC_ARM64_REG_S24,
|
||||
UC_ARM64_REG_S25,
|
||||
UC_ARM64_REG_S26,
|
||||
UC_ARM64_REG_S27,
|
||||
UC_ARM64_REG_S28,
|
||||
UC_ARM64_REG_S29,
|
||||
UC_ARM64_REG_S30,
|
||||
UC_ARM64_REG_S31,
|
||||
UC_ARM64_REG_W0,
|
||||
UC_ARM64_REG_W1,
|
||||
UC_ARM64_REG_W2,
|
||||
UC_ARM64_REG_W3,
|
||||
UC_ARM64_REG_W4,
|
||||
UC_ARM64_REG_W5,
|
||||
UC_ARM64_REG_W6,
|
||||
UC_ARM64_REG_W7,
|
||||
UC_ARM64_REG_W8,
|
||||
UC_ARM64_REG_W9,
|
||||
UC_ARM64_REG_W10,
|
||||
UC_ARM64_REG_W11,
|
||||
UC_ARM64_REG_W12,
|
||||
UC_ARM64_REG_W13,
|
||||
UC_ARM64_REG_W14,
|
||||
UC_ARM64_REG_W15,
|
||||
UC_ARM64_REG_W16,
|
||||
UC_ARM64_REG_W17,
|
||||
UC_ARM64_REG_W18,
|
||||
UC_ARM64_REG_W19,
|
||||
UC_ARM64_REG_W20,
|
||||
UC_ARM64_REG_W21,
|
||||
UC_ARM64_REG_W22,
|
||||
UC_ARM64_REG_W23,
|
||||
UC_ARM64_REG_W24,
|
||||
UC_ARM64_REG_W25,
|
||||
UC_ARM64_REG_W26,
|
||||
UC_ARM64_REG_W27,
|
||||
UC_ARM64_REG_W28,
|
||||
UC_ARM64_REG_W29,
|
||||
UC_ARM64_REG_W30,
|
||||
UC_ARM64_REG_X0,
|
||||
UC_ARM64_REG_X1,
|
||||
UC_ARM64_REG_X2,
|
||||
UC_ARM64_REG_X3,
|
||||
UC_ARM64_REG_X4,
|
||||
UC_ARM64_REG_X5,
|
||||
UC_ARM64_REG_X6,
|
||||
UC_ARM64_REG_X7,
|
||||
UC_ARM64_REG_X8,
|
||||
UC_ARM64_REG_X9,
|
||||
UC_ARM64_REG_X10,
|
||||
UC_ARM64_REG_X11,
|
||||
UC_ARM64_REG_X12,
|
||||
UC_ARM64_REG_X13,
|
||||
UC_ARM64_REG_X14,
|
||||
UC_ARM64_REG_X15,
|
||||
UC_ARM64_REG_X16,
|
||||
UC_ARM64_REG_X17,
|
||||
UC_ARM64_REG_X18,
|
||||
UC_ARM64_REG_X19,
|
||||
UC_ARM64_REG_X20,
|
||||
UC_ARM64_REG_X21,
|
||||
UC_ARM64_REG_X22,
|
||||
UC_ARM64_REG_X23,
|
||||
UC_ARM64_REG_X24,
|
||||
UC_ARM64_REG_X25,
|
||||
UC_ARM64_REG_X26,
|
||||
UC_ARM64_REG_X27,
|
||||
UC_ARM64_REG_X28,
|
||||
|
||||
UC_ARM64_REG_V0,
|
||||
UC_ARM64_REG_V1,
|
||||
UC_ARM64_REG_V2,
|
||||
UC_ARM64_REG_V3,
|
||||
UC_ARM64_REG_V4,
|
||||
UC_ARM64_REG_V5,
|
||||
UC_ARM64_REG_V6,
|
||||
UC_ARM64_REG_V7,
|
||||
UC_ARM64_REG_V8,
|
||||
UC_ARM64_REG_V9,
|
||||
UC_ARM64_REG_V10,
|
||||
UC_ARM64_REG_V11,
|
||||
UC_ARM64_REG_V12,
|
||||
UC_ARM64_REG_V13,
|
||||
UC_ARM64_REG_V14,
|
||||
UC_ARM64_REG_V15,
|
||||
UC_ARM64_REG_V16,
|
||||
UC_ARM64_REG_V17,
|
||||
UC_ARM64_REG_V18,
|
||||
UC_ARM64_REG_V19,
|
||||
UC_ARM64_REG_V20,
|
||||
UC_ARM64_REG_V21,
|
||||
UC_ARM64_REG_V22,
|
||||
UC_ARM64_REG_V23,
|
||||
UC_ARM64_REG_V24,
|
||||
UC_ARM64_REG_V25,
|
||||
UC_ARM64_REG_V26,
|
||||
UC_ARM64_REG_V27,
|
||||
UC_ARM64_REG_V28,
|
||||
UC_ARM64_REG_V29,
|
||||
UC_ARM64_REG_V30,
|
||||
UC_ARM64_REG_V31,
|
||||
|
||||
//> pseudo registers
|
||||
UC_ARM64_REG_PC, // program counter register
|
||||
|
||||
UC_ARM64_REG_CPACR_EL1,
|
||||
|
||||
//> thread registers
|
||||
UC_ARM64_REG_TPIDR_EL0,
|
||||
UC_ARM64_REG_TPIDRRO_EL0,
|
||||
UC_ARM64_REG_TPIDR_EL1,
|
||||
|
||||
UC_ARM64_REG_ENDING, // <-- mark the end of the list of registers
|
||||
|
||||
//> alias registers
|
||||
|
||||
UC_ARM64_REG_IP0 = UC_ARM64_REG_X16,
|
||||
UC_ARM64_REG_IP1 = UC_ARM64_REG_X17,
|
||||
UC_ARM64_REG_FP = UC_ARM64_REG_X29,
|
||||
UC_ARM64_REG_LR = UC_ARM64_REG_X30,
|
||||
} uc_arm64_reg;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
@ -1,219 +0,0 @@
|
||||
/* This file is released under LGPL2.
|
||||
See COPYING.LGPL2 in root directory for more details
|
||||
*/
|
||||
|
||||
/*
|
||||
This file is to support header files that are missing in MSVC and
|
||||
other non-standard compilers.
|
||||
*/
|
||||
#ifndef UNICORN_PLATFORM_H
|
||||
#define UNICORN_PLATFORM_H
|
||||
|
||||
/*
|
||||
These are the various MSVC versions as given by _MSC_VER:
|
||||
MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
|
||||
MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
|
||||
MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
|
||||
MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
|
||||
MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008)
|
||||
MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005)
|
||||
MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio 2003)
|
||||
MSVC++ 7.0 _MSC_VER == 1300
|
||||
MSVC++ 6.0 _MSC_VER == 1200
|
||||
MSVC++ 5.0 _MSC_VER == 1100
|
||||
*/
|
||||
#define MSC_VER_VS2003 1310
|
||||
#define MSC_VER_VS2005 1400
|
||||
#define MSC_VER_VS2008 1500
|
||||
#define MSC_VER_VS2010 1600
|
||||
#define MSC_VER_VS2012 1700
|
||||
#define MSC_VER_VS2013 1800
|
||||
#define MSC_VER_VS2015 1900
|
||||
|
||||
// handle stdbool.h compatibility
|
||||
#if !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__) && (defined (WIN32) || defined (WIN64) || defined (_WIN32) || defined (_WIN64))
|
||||
// MSVC
|
||||
|
||||
// stdbool.h
|
||||
#if (_MSC_VER < MSC_VER_VS2013) || defined(_KERNEL_MODE)
|
||||
// this system does not have stdbool.h
|
||||
#ifndef __cplusplus
|
||||
typedef unsigned char bool;
|
||||
#define false 0
|
||||
#define true 1
|
||||
#endif // __cplusplus
|
||||
|
||||
#else
|
||||
// VisualStudio 2013+ -> C99 is supported
|
||||
#include <stdbool.h>
|
||||
#endif // (_MSC_VER < MSC_VER_VS2013) || defined(_KERNEL_MODE)
|
||||
|
||||
#else
|
||||
// not MSVC -> C99 is supported
|
||||
#include <stdbool.h>
|
||||
#endif // !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__MINGW64__) && (defined (WIN32) || defined (WIN64) || defined (_WIN32) || defined (_WIN64))
|
||||
|
||||
#if (defined(_MSC_VER) && (_MSC_VER < MSC_VER_VS2010)) || defined(_KERNEL_MODE)
|
||||
// this system does not have stdint.h
|
||||
typedef signed char int8_t;
|
||||
typedef signed short int16_t;
|
||||
typedef signed int int32_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef signed long long int64_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
|
||||
#ifndef _INTPTR_T_DEFINED
|
||||
#define _INTPTR_T_DEFINED
|
||||
#ifdef _WIN64
|
||||
typedef long long intptr_t;
|
||||
#else /* _WIN64 */
|
||||
typedef _W64 int intptr_t;
|
||||
#endif /* _WIN64 */
|
||||
#endif /* _INTPTR_T_DEFINED */
|
||||
|
||||
#ifndef _UINTPTR_T_DEFINED
|
||||
#define _UINTPTR_T_DEFINED
|
||||
#ifdef _WIN64
|
||||
typedef unsigned long long uintptr_t;
|
||||
#else /* _WIN64 */
|
||||
typedef _W64 unsigned int uintptr_t;
|
||||
#endif /* _WIN64 */
|
||||
#endif /* _UINTPTR_T_DEFINED */
|
||||
|
||||
#define INT8_MIN (-127i8 - 1)
|
||||
#define INT16_MIN (-32767i16 - 1)
|
||||
#define INT32_MIN (-2147483647i32 - 1)
|
||||
#define INT64_MIN (-9223372036854775807i64 - 1)
|
||||
#define INT8_MAX 127i8
|
||||
#define INT16_MAX 32767i16
|
||||
#define INT32_MAX 2147483647i32
|
||||
#define INT64_MAX 9223372036854775807i64
|
||||
#define UINT8_MAX 0xffui8
|
||||
#define UINT16_MAX 0xffffui16
|
||||
#define UINT32_MAX 0xffffffffui32
|
||||
#define UINT64_MAX 0xffffffffffffffffui64
|
||||
#else // this system has stdint.h
|
||||
#include <stdint.h>
|
||||
#endif // (defined(_MSC_VER) && (_MSC_VER < MSC_VER_VS2010)) || defined(_KERNEL_MODE)
|
||||
|
||||
// handle inttypes.h compatibility
|
||||
#if (defined(_MSC_VER) && (_MSC_VER < MSC_VER_VS2013)) || defined(_KERNEL_MODE)
|
||||
// this system does not have inttypes.h
|
||||
|
||||
#define __PRI_8_LENGTH_MODIFIER__ "hh"
|
||||
#define __PRI_64_LENGTH_MODIFIER__ "ll"
|
||||
|
||||
#define PRId8 __PRI_8_LENGTH_MODIFIER__ "d"
|
||||
#define PRIi8 __PRI_8_LENGTH_MODIFIER__ "i"
|
||||
#define PRIo8 __PRI_8_LENGTH_MODIFIER__ "o"
|
||||
#define PRIu8 __PRI_8_LENGTH_MODIFIER__ "u"
|
||||
#define PRIx8 __PRI_8_LENGTH_MODIFIER__ "x"
|
||||
#define PRIX8 __PRI_8_LENGTH_MODIFIER__ "X"
|
||||
|
||||
#define PRId16 "hd"
|
||||
#define PRIi16 "hi"
|
||||
#define PRIo16 "ho"
|
||||
#define PRIu16 "hu"
|
||||
#define PRIx16 "hx"
|
||||
#define PRIX16 "hX"
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER <= MSC_VER_VS2012)
|
||||
#define PRId32 "ld"
|
||||
#define PRIi32 "li"
|
||||
#define PRIo32 "lo"
|
||||
#define PRIu32 "lu"
|
||||
#define PRIx32 "lx"
|
||||
#define PRIX32 "lX"
|
||||
#else // OSX
|
||||
#define PRId32 "d"
|
||||
#define PRIi32 "i"
|
||||
#define PRIo32 "o"
|
||||
#define PRIu32 "u"
|
||||
#define PRIx32 "x"
|
||||
#define PRIX32 "X"
|
||||
#endif // defined(_MSC_VER) && (_MSC_VER <= MSC_VER_VS2012)
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER <= MSC_VER_VS2012)
|
||||
// redefine functions from inttypes.h used in cstool
|
||||
#define strtoull _strtoui64
|
||||
#endif
|
||||
|
||||
#define PRId64 __PRI_64_LENGTH_MODIFIER__ "d"
|
||||
#define PRIi64 __PRI_64_LENGTH_MODIFIER__ "i"
|
||||
#define PRIo64 __PRI_64_LENGTH_MODIFIER__ "o"
|
||||
#define PRIu64 __PRI_64_LENGTH_MODIFIER__ "u"
|
||||
#define PRIx64 __PRI_64_LENGTH_MODIFIER__ "x"
|
||||
#define PRIX64 __PRI_64_LENGTH_MODIFIER__ "X"
|
||||
|
||||
#else
|
||||
// this system has inttypes.h by default
|
||||
#include <inttypes.h>
|
||||
#endif // #if defined(_MSC_VER) && (_MSC_VER < MSC_VER_VS2013) || defined(_KERNEL_MODE)
|
||||
|
||||
// sys/time.h compatibility
|
||||
#if defined(_MSC_VER)
|
||||
#include <sys/timeb.h>
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
|
||||
static int gettimeofday(struct timeval* t, void* timezone)
|
||||
{
|
||||
struct _timeb timebuffer;
|
||||
_ftime( &timebuffer );
|
||||
t->tv_sec = (long)timebuffer.time;
|
||||
t->tv_usec = 1000*timebuffer.millitm;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
// unistd.h compatibility
|
||||
#if defined(_MSC_VER)
|
||||
|
||||
static int usleep(uint32_t usec)
|
||||
{
|
||||
HANDLE timer;
|
||||
LARGE_INTEGER due;
|
||||
|
||||
timer = CreateWaitableTimer(NULL, TRUE, NULL);
|
||||
if (!timer)
|
||||
return -1;
|
||||
|
||||
due.QuadPart = (-((int64_t) usec)) * 10LL;
|
||||
if (!SetWaitableTimer(timer, &due, 0, NULL, NULL, 0)) {
|
||||
CloseHandle(timer);
|
||||
return -1;
|
||||
}
|
||||
WaitForSingleObject(timer, INFINITE);
|
||||
CloseHandle(timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
// misc support
|
||||
#if defined(_MSC_VER)
|
||||
#ifdef _WIN64
|
||||
typedef signed __int64 ssize_t;
|
||||
#else
|
||||
typedef _W64 signed int ssize_t;
|
||||
#endif
|
||||
|
||||
#define va_copy(d,s) ((d) = (s))
|
||||
#define strcasecmp _stricmp
|
||||
#if (_MSC_VER < MSC_VER_VS2015)
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
#if (_MSC_VER <= MSC_VER_VS2013)
|
||||
#define strtoll _strtoi64
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#endif // UNICORN_PLATFORM_H
|
@ -1,725 +0,0 @@
|
||||
/* Unicorn Emulator Engine */
|
||||
/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2015-2017 */
|
||||
/* This file is released under LGPL2.
|
||||
See COPYING.LGPL2 in root directory for more details
|
||||
*/
|
||||
|
||||
#ifndef UNICORN_ENGINE_H
|
||||
#define UNICORN_ENGINE_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "platform.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
#if defined(UNICORN_HAS_OSXKERNEL)
|
||||
#include <libkern/libkern.h>
|
||||
#else
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
|
||||
struct uc_struct;
|
||||
typedef struct uc_struct uc_engine;
|
||||
|
||||
typedef size_t uc_hook;
|
||||
|
||||
#include "arm64.h"
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define DEFAULT_VISIBILITY __attribute__((visibility("default")))
|
||||
#else
|
||||
#define DEFAULT_VISIBILITY
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(disable:4201)
|
||||
#pragma warning(disable:4100)
|
||||
#ifdef UNICORN_SHARED
|
||||
#define UNICORN_EXPORT __declspec(dllexport)
|
||||
#else // defined(UNICORN_STATIC)
|
||||
#define UNICORN_EXPORT
|
||||
#endif
|
||||
#else
|
||||
#ifdef __GNUC__
|
||||
#define UNICORN_EXPORT __attribute__((visibility("default")))
|
||||
#else
|
||||
#define UNICORN_EXPORT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#define UNICORN_DEPRECATED __attribute__((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
#define UNICORN_DEPRECATED __declspec(deprecated)
|
||||
#else
|
||||
#pragma message("WARNING: You need to implement UNICORN_DEPRECATED for this compiler")
|
||||
#define UNICORN_DEPRECATED
|
||||
#endif
|
||||
|
||||
// Unicorn API version
|
||||
#define UC_API_MAJOR 1
|
||||
#define UC_API_MINOR 0
|
||||
|
||||
// Unicorn package version
|
||||
#define UC_VERSION_MAJOR UC_API_MAJOR
|
||||
#define UC_VERSION_MINOR UC_API_MINOR
|
||||
#define UC_VERSION_EXTRA 2
|
||||
|
||||
|
||||
/*
|
||||
Macro to create combined version which can be compared to
|
||||
result of uc_version() API.
|
||||
*/
|
||||
#define UC_MAKE_VERSION(major, minor) ((major << 8) + minor)
|
||||
|
||||
// Scales to calculate timeout on microsecond unit
|
||||
// 1 second = 1000,000 microseconds
|
||||
#define UC_SECOND_SCALE 1000000
|
||||
// 1 milisecond = 1000 nanoseconds
|
||||
#define UC_MILISECOND_SCALE 1000
|
||||
|
||||
// Architecture type
|
||||
typedef enum uc_arch {
|
||||
UC_ARCH_ARM = 1, // ARM architecture (including Thumb, Thumb-2)
|
||||
UC_ARCH_ARM64, // ARM-64, also called AArch64
|
||||
UC_ARCH_MIPS, // Mips architecture
|
||||
UC_ARCH_X86, // X86 architecture (including x86 & x86-64)
|
||||
UC_ARCH_PPC, // PowerPC architecture (currently unsupported)
|
||||
UC_ARCH_SPARC, // Sparc architecture
|
||||
UC_ARCH_M68K, // M68K architecture
|
||||
UC_ARCH_MAX,
|
||||
} uc_arch;
|
||||
|
||||
// Mode type
|
||||
typedef enum uc_mode {
|
||||
UC_MODE_LITTLE_ENDIAN = 0, // little-endian mode (default mode)
|
||||
UC_MODE_BIG_ENDIAN = 1 << 30, // big-endian mode
|
||||
// arm / arm64
|
||||
UC_MODE_ARM = 0, // ARM mode
|
||||
UC_MODE_THUMB = 1 << 4, // THUMB mode (including Thumb-2)
|
||||
UC_MODE_MCLASS = 1 << 5, // ARM's Cortex-M series (currently unsupported)
|
||||
UC_MODE_V8 = 1 << 6, // ARMv8 A32 encodings for ARM (currently unsupported)
|
||||
// mips
|
||||
UC_MODE_MICRO = 1 << 4, // MicroMips mode (currently unsupported)
|
||||
UC_MODE_MIPS3 = 1 << 5, // Mips III ISA (currently unsupported)
|
||||
UC_MODE_MIPS32R6 = 1 << 6, // Mips32r6 ISA (currently unsupported)
|
||||
UC_MODE_MIPS32 = 1 << 2, // Mips32 ISA
|
||||
UC_MODE_MIPS64 = 1 << 3, // Mips64 ISA
|
||||
// x86 / x64
|
||||
UC_MODE_16 = 1 << 1, // 16-bit mode
|
||||
UC_MODE_32 = 1 << 2, // 32-bit mode
|
||||
UC_MODE_64 = 1 << 3, // 64-bit mode
|
||||
// ppc
|
||||
UC_MODE_PPC32 = 1 << 2, // 32-bit mode (currently unsupported)
|
||||
UC_MODE_PPC64 = 1 << 3, // 64-bit mode (currently unsupported)
|
||||
UC_MODE_QPX = 1 << 4, // Quad Processing eXtensions mode (currently unsupported)
|
||||
// sparc
|
||||
UC_MODE_SPARC32 = 1 << 2, // 32-bit mode
|
||||
UC_MODE_SPARC64 = 1 << 3, // 64-bit mode
|
||||
UC_MODE_V9 = 1 << 4, // SparcV9 mode (currently unsupported)
|
||||
// m68k
|
||||
} uc_mode;
|
||||
|
||||
// All type of errors encountered by Unicorn API.
|
||||
// These are values returned by uc_errno()
|
||||
typedef enum uc_err {
|
||||
UC_ERR_OK = 0, // No error: everything was fine
|
||||
UC_ERR_NOMEM, // Out-Of-Memory error: uc_open(), uc_emulate()
|
||||
UC_ERR_ARCH, // Unsupported architecture: uc_open()
|
||||
UC_ERR_HANDLE, // Invalid handle
|
||||
UC_ERR_MODE, // Invalid/unsupported mode: uc_open()
|
||||
UC_ERR_VERSION, // Unsupported version (bindings)
|
||||
UC_ERR_READ_UNMAPPED, // Quit emulation due to READ on unmapped memory: uc_emu_start()
|
||||
UC_ERR_WRITE_UNMAPPED, // Quit emulation due to WRITE on unmapped memory: uc_emu_start()
|
||||
UC_ERR_FETCH_UNMAPPED, // Quit emulation due to FETCH on unmapped memory: uc_emu_start()
|
||||
UC_ERR_HOOK, // Invalid hook type: uc_hook_add()
|
||||
UC_ERR_INSN_INVALID, // Quit emulation due to invalid instruction: uc_emu_start()
|
||||
UC_ERR_MAP, // Invalid memory mapping: uc_mem_map()
|
||||
UC_ERR_WRITE_PROT, // Quit emulation due to UC_MEM_WRITE_PROT violation: uc_emu_start()
|
||||
UC_ERR_READ_PROT, // Quit emulation due to UC_MEM_READ_PROT violation: uc_emu_start()
|
||||
UC_ERR_FETCH_PROT, // Quit emulation due to UC_MEM_FETCH_PROT violation: uc_emu_start()
|
||||
UC_ERR_ARG, // Inavalid argument provided to uc_xxx function (See specific function API)
|
||||
UC_ERR_READ_UNALIGNED, // Unaligned read
|
||||
UC_ERR_WRITE_UNALIGNED, // Unaligned write
|
||||
UC_ERR_FETCH_UNALIGNED, // Unaligned fetch
|
||||
UC_ERR_HOOK_EXIST, // hook for this event already existed
|
||||
UC_ERR_RESOURCE, // Insufficient resource: uc_emu_start()
|
||||
UC_ERR_EXCEPTION // Unhandled CPU exception
|
||||
} uc_err;
|
||||
|
||||
|
||||
/*
|
||||
Callback function for tracing code (UC_HOOK_CODE & UC_HOOK_BLOCK)
|
||||
|
||||
@address: address where the code is being executed
|
||||
@size: size of machine instruction(s) being executed, or 0 when size is unknown
|
||||
@user_data: user data passed to tracing APIs.
|
||||
*/
|
||||
typedef void (*uc_cb_hookcode_t)(uc_engine *uc, uint64_t address, uint32_t size, void *user_data);
|
||||
|
||||
/*
|
||||
Callback function for tracing interrupts (for uc_hook_intr())
|
||||
|
||||
@intno: interrupt number
|
||||
@user_data: user data passed to tracing APIs.
|
||||
*/
|
||||
typedef void (*uc_cb_hookintr_t)(uc_engine *uc, uint32_t intno, void *user_data);
|
||||
|
||||
/*
|
||||
Callback function for tracing IN instruction of X86
|
||||
|
||||
@port: port number
|
||||
@size: data size (1/2/4) to be read from this port
|
||||
@user_data: user data passed to tracing APIs.
|
||||
*/
|
||||
typedef uint32_t (*uc_cb_insn_in_t)(uc_engine *uc, uint32_t port, int size, void *user_data);
|
||||
|
||||
/*
|
||||
Callback function for OUT instruction of X86
|
||||
|
||||
@port: port number
|
||||
@size: data size (1/2/4) to be written to this port
|
||||
@value: data value to be written to this port
|
||||
*/
|
||||
typedef void (*uc_cb_insn_out_t)(uc_engine *uc, uint32_t port, int size, uint32_t value, void *user_data);
|
||||
|
||||
// All type of memory accesses for UC_HOOK_MEM_*
|
||||
typedef enum uc_mem_type {
|
||||
UC_MEM_READ = 16, // Memory is read from
|
||||
UC_MEM_WRITE, // Memory is written to
|
||||
UC_MEM_FETCH, // Memory is fetched
|
||||
UC_MEM_READ_UNMAPPED, // Unmapped memory is read from
|
||||
UC_MEM_WRITE_UNMAPPED, // Unmapped memory is written to
|
||||
UC_MEM_FETCH_UNMAPPED, // Unmapped memory is fetched
|
||||
UC_MEM_WRITE_PROT, // Write to write protected, but mapped, memory
|
||||
UC_MEM_READ_PROT, // Read from read protected, but mapped, memory
|
||||
UC_MEM_FETCH_PROT, // Fetch from non-executable, but mapped, memory
|
||||
UC_MEM_READ_AFTER, // Memory is read from (successful access)
|
||||
} uc_mem_type;
|
||||
|
||||
// All type of hooks for uc_hook_add() API.
|
||||
typedef enum uc_hook_type {
|
||||
// Hook all interrupt/syscall events
|
||||
UC_HOOK_INTR = 1 << 0,
|
||||
// Hook a particular instruction - only a very small subset of instructions supported here
|
||||
UC_HOOK_INSN = 1 << 1,
|
||||
// Hook a range of code
|
||||
UC_HOOK_CODE = 1 << 2,
|
||||
// Hook basic blocks
|
||||
UC_HOOK_BLOCK = 1 << 3,
|
||||
// Hook for memory read on unmapped memory
|
||||
UC_HOOK_MEM_READ_UNMAPPED = 1 << 4,
|
||||
// Hook for invalid memory write events
|
||||
UC_HOOK_MEM_WRITE_UNMAPPED = 1 << 5,
|
||||
// Hook for invalid memory fetch for execution events
|
||||
UC_HOOK_MEM_FETCH_UNMAPPED = 1 << 6,
|
||||
// Hook for memory read on read-protected memory
|
||||
UC_HOOK_MEM_READ_PROT = 1 << 7,
|
||||
// Hook for memory write on write-protected memory
|
||||
UC_HOOK_MEM_WRITE_PROT = 1 << 8,
|
||||
// Hook for memory fetch on non-executable memory
|
||||
UC_HOOK_MEM_FETCH_PROT = 1 << 9,
|
||||
// Hook memory read events.
|
||||
UC_HOOK_MEM_READ = 1 << 10,
|
||||
// Hook memory write events.
|
||||
UC_HOOK_MEM_WRITE = 1 << 11,
|
||||
// Hook memory fetch for execution events
|
||||
UC_HOOK_MEM_FETCH = 1 << 12,
|
||||
// Hook memory read events, but only successful access.
|
||||
// The callback will be triggered after successful read.
|
||||
UC_HOOK_MEM_READ_AFTER = 1 << 13,
|
||||
} uc_hook_type;
|
||||
|
||||
// Hook type for all events of unmapped memory access
|
||||
#define UC_HOOK_MEM_UNMAPPED (UC_HOOK_MEM_READ_UNMAPPED + UC_HOOK_MEM_WRITE_UNMAPPED + UC_HOOK_MEM_FETCH_UNMAPPED)
|
||||
// Hook type for all events of illegal protected memory access
|
||||
#define UC_HOOK_MEM_PROT (UC_HOOK_MEM_READ_PROT + UC_HOOK_MEM_WRITE_PROT + UC_HOOK_MEM_FETCH_PROT)
|
||||
// Hook type for all events of illegal read memory access
|
||||
#define UC_HOOK_MEM_READ_INVALID (UC_HOOK_MEM_READ_PROT + UC_HOOK_MEM_READ_UNMAPPED)
|
||||
// Hook type for all events of illegal write memory access
|
||||
#define UC_HOOK_MEM_WRITE_INVALID (UC_HOOK_MEM_WRITE_PROT + UC_HOOK_MEM_WRITE_UNMAPPED)
|
||||
// Hook type for all events of illegal fetch memory access
|
||||
#define UC_HOOK_MEM_FETCH_INVALID (UC_HOOK_MEM_FETCH_PROT + UC_HOOK_MEM_FETCH_UNMAPPED)
|
||||
// Hook type for all events of illegal memory access
|
||||
#define UC_HOOK_MEM_INVALID (UC_HOOK_MEM_UNMAPPED + UC_HOOK_MEM_PROT)
|
||||
// Hook type for all events of valid memory access
|
||||
// NOTE: UC_HOOK_MEM_READ is triggered before UC_HOOK_MEM_READ_PROT and UC_HOOK_MEM_READ_UNMAPPED, so
|
||||
// this hook may technically trigger on some invalid reads.
|
||||
#define UC_HOOK_MEM_VALID (UC_HOOK_MEM_READ + UC_HOOK_MEM_WRITE + UC_HOOK_MEM_FETCH)
|
||||
|
||||
/*
|
||||
Callback function for hooking memory (READ, WRITE & FETCH)
|
||||
|
||||
@type: this memory is being READ, or WRITE
|
||||
@address: address where the code is being executed
|
||||
@size: size of data being read or written
|
||||
@value: value of data being written to memory, or irrelevant if type = READ.
|
||||
@user_data: user data passed to tracing APIs
|
||||
*/
|
||||
typedef void (*uc_cb_hookmem_t)(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t address, int size, int64_t value, void *user_data);
|
||||
|
||||
/*
|
||||
Callback function for handling invalid memory access events (UNMAPPED and
|
||||
PROT events)
|
||||
|
||||
@type: this memory is being READ, or WRITE
|
||||
@address: address where the code is being executed
|
||||
@size: size of data being read or written
|
||||
@value: value of data being written to memory, or irrelevant if type = READ.
|
||||
@user_data: user data passed to tracing APIs
|
||||
|
||||
@return: return true to continue, or false to stop program (due to invalid memory).
|
||||
NOTE: returning true to continue execution will only work if if the accessed
|
||||
memory is made accessible with the correct permissions during the hook.
|
||||
|
||||
In the event of a UC_MEM_READ_UNMAPPED or UC_MEM_WRITE_UNMAPPED callback,
|
||||
the memory should be uc_mem_map()-ed with the correct permissions, and the
|
||||
instruction will then read or write to the address as it was supposed to.
|
||||
|
||||
In the event of a UC_MEM_FETCH_UNMAPPED callback, the memory can be mapped
|
||||
in as executable, in which case execution will resume from the fetched address.
|
||||
The instruction pointer may be written to in order to change where execution resumes,
|
||||
but the fetch must succeed if execution is to resume.
|
||||
*/
|
||||
typedef bool (*uc_cb_eventmem_t)(uc_engine *uc, uc_mem_type type,
|
||||
uint64_t address, int size, int64_t value, void *user_data);
|
||||
|
||||
/*
|
||||
Memory region mapped by uc_mem_map() and uc_mem_map_ptr()
|
||||
Retrieve the list of memory regions with uc_mem_regions()
|
||||
*/
|
||||
typedef struct uc_mem_region {
|
||||
uint64_t begin; // begin address of the region (inclusive)
|
||||
uint64_t end; // end address of the region (inclusive)
|
||||
uint32_t perms; // memory permissions of the region
|
||||
} uc_mem_region;
|
||||
|
||||
// All type of queries for uc_query() API.
|
||||
typedef enum uc_query_type {
|
||||
// Dynamically query current hardware mode.
|
||||
UC_QUERY_MODE = 1,
|
||||
UC_QUERY_PAGE_SIZE,
|
||||
UC_QUERY_ARCH,
|
||||
} uc_query_type;
|
||||
|
||||
// Opaque storage for CPU context, used with uc_context_*()
|
||||
struct uc_context;
|
||||
typedef struct uc_context uc_context;
|
||||
|
||||
/*
|
||||
Return combined API version & major and minor version numbers.
|
||||
|
||||
@major: major number of API version
|
||||
@minor: minor number of API version
|
||||
|
||||
@return hexical number as (major << 8 | minor), which encodes both
|
||||
major & minor versions.
|
||||
NOTE: This returned value can be compared with version number made
|
||||
with macro UC_MAKE_VERSION
|
||||
|
||||
For example, second API version would return 1 in @major, and 1 in @minor
|
||||
The return value would be 0x0101
|
||||
|
||||
NOTE: if you only care about returned value, but not major and minor values,
|
||||
set both @major & @minor arguments to NULL.
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
unsigned int uc_version(unsigned int *major, unsigned int *minor);
|
||||
|
||||
|
||||
/*
|
||||
Determine if the given architecture is supported by this library.
|
||||
|
||||
@arch: architecture type (UC_ARCH_*)
|
||||
|
||||
@return True if this library supports the given arch.
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
bool uc_arch_supported(uc_arch arch);
|
||||
|
||||
|
||||
/*
|
||||
Create new instance of unicorn engine.
|
||||
|
||||
@arch: architecture type (UC_ARCH_*)
|
||||
@mode: hardware mode. This is combined of UC_MODE_*
|
||||
@uc: pointer to uc_engine, which will be updated at return time
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **uc);
|
||||
|
||||
/*
|
||||
Close a Unicorn engine instance.
|
||||
NOTE: this must be called only when there is no longer any
|
||||
usage of @uc. This API releases some of @uc's cached memory, thus
|
||||
any use of the Unicorn API with @uc after it has been closed may
|
||||
crash your application. After this, @uc is invalid, and is no
|
||||
longer usable.
|
||||
|
||||
@uc: pointer to a handle returned by uc_open()
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_close(uc_engine *uc);
|
||||
|
||||
/*
|
||||
Query internal status of engine.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@type: query type. See uc_query_type
|
||||
|
||||
@result: save the internal status queried
|
||||
|
||||
@return: error code of uc_err enum type (UC_ERR_*, see above)
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result);
|
||||
|
||||
/*
|
||||
Report the last error number when some API function fail.
|
||||
Like glibc's errno, uc_errno might not retain its old value once accessed.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
|
||||
@return: error code of uc_err enum type (UC_ERR_*, see above)
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_errno(uc_engine *uc);
|
||||
|
||||
/*
|
||||
Return a string describing given error code.
|
||||
|
||||
@code: error code (see UC_ERR_* above)
|
||||
|
||||
@return: returns a pointer to a string that describes the error code
|
||||
passed in the argument @code
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
const char *uc_strerror(uc_err code);
|
||||
|
||||
/*
|
||||
Write to register.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@regid: register ID that is to be modified.
|
||||
@value: pointer to the value that will set to register @regid
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_reg_write(uc_engine *uc, int regid, const void *value);
|
||||
|
||||
/*
|
||||
Read register value.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@regid: register ID that is to be retrieved.
|
||||
@value: pointer to a variable storing the register value.
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_reg_read(uc_engine *uc, int regid, void *value);
|
||||
|
||||
/*
|
||||
Write multiple register values.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@rges: array of register IDs to store
|
||||
@value: pointer to array of register values
|
||||
@count: length of both *regs and *vals
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_reg_write_batch(uc_engine *uc, int *regs, void *const *vals, int count);
|
||||
|
||||
/*
|
||||
Read multiple register values.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@rges: array of register IDs to retrieve
|
||||
@value: pointer to array of values to hold registers
|
||||
@count: length of both *regs and *vals
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_reg_read_batch(uc_engine *uc, int *regs, void **vals, int count);
|
||||
|
||||
/*
|
||||
Write to a range of bytes in memory.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@address: starting memory address of bytes to set.
|
||||
@bytes: pointer to a variable containing data to be written to memory.
|
||||
@size: size of memory to write to.
|
||||
|
||||
NOTE: @bytes must be big enough to contain @size bytes.
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_write(uc_engine *uc, uint64_t address, const void *bytes, size_t size);
|
||||
|
||||
/*
|
||||
Read a range of bytes in memory.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@address: starting memory address of bytes to get.
|
||||
@bytes: pointer to a variable containing data copied from memory.
|
||||
@size: size of memory to read.
|
||||
|
||||
NOTE: @bytes must be big enough to contain @size bytes.
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *bytes, size_t size);
|
||||
|
||||
/*
|
||||
Emulate machine code in a specific duration of time.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@begin: address where emulation starts
|
||||
@until: address where emulation stops (i.e when this address is hit)
|
||||
@timeout: duration to emulate the code (in microseconds). When this value is 0,
|
||||
we will emulate the code in infinite time, until the code is finished.
|
||||
@count: the number of instructions to be emulated. When this value is 0,
|
||||
we will emulate all the code available, until the code is finished.
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until, uint64_t timeout, size_t count);
|
||||
|
||||
/*
|
||||
Stop emulation (which was started by uc_emu_start() API.
|
||||
This is typically called from callback functions registered via tracing APIs.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_emu_stop(uc_engine *uc);
|
||||
|
||||
/*
|
||||
Register callback for a hook event.
|
||||
The callback will be run when the hook event is hit.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@hh: hook handle returned from this registration. To be used in uc_hook_del() API
|
||||
@type: hook type
|
||||
@callback: callback to be run when instruction is hit
|
||||
@user_data: user-defined data. This will be passed to callback function in its
|
||||
last argument @user_data
|
||||
@begin: start address of the area where the callback is effect (inclusive)
|
||||
@end: end address of the area where the callback is effect (inclusive)
|
||||
NOTE 1: the callback is called only if related address is in range [@begin, @end]
|
||||
NOTE 2: if @begin > @end, callback is called whenever this hook type is triggered
|
||||
@...: variable arguments (depending on @type)
|
||||
NOTE: if @type = UC_HOOK_INSN, this is the instruction ID (ex: UC_X86_INS_OUT)
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
|
||||
void *user_data, uint64_t begin, uint64_t end, ...);
|
||||
|
||||
/*
|
||||
Unregister (remove) a hook callback.
|
||||
This API removes the hook callback registered by uc_hook_add().
|
||||
NOTE: this should be called only when you no longer want to trace.
|
||||
After this, @hh is invalid, and nolonger usable.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@hh: handle returned by uc_hook_add()
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_hook_del(uc_engine *uc, uc_hook hh);
|
||||
|
||||
typedef enum uc_prot {
|
||||
UC_PROT_NONE = 0,
|
||||
UC_PROT_READ = 1,
|
||||
UC_PROT_WRITE = 2,
|
||||
UC_PROT_EXEC = 4,
|
||||
UC_PROT_ALL = 7,
|
||||
} uc_prot;
|
||||
|
||||
/*
|
||||
Map memory in for emulation.
|
||||
This API adds a memory region that can be used by emulation.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@address: starting address of the new memory region to be mapped in.
|
||||
This address must be aligned to 4KB, or this will return with UC_ERR_ARG error.
|
||||
@size: size of the new memory region to be mapped in.
|
||||
This size must be multiple of 4KB, or this will return with UC_ERR_ARG error.
|
||||
@perms: Permissions for the newly mapped region.
|
||||
This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC,
|
||||
or this will return with UC_ERR_ARG error.
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms);
|
||||
|
||||
/*
|
||||
Map existing host memory in for emulation.
|
||||
This API adds a memory region that can be used by emulation.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@address: starting address of the new memory region to be mapped in.
|
||||
This address must be aligned to 4KB, or this will return with UC_ERR_ARG error.
|
||||
@size: size of the new memory region to be mapped in.
|
||||
This size must be multiple of 4KB, or this will return with UC_ERR_ARG error.
|
||||
@perms: Permissions for the newly mapped region.
|
||||
This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC,
|
||||
or this will return with UC_ERR_ARG error.
|
||||
@ptr: pointer to host memory backing the newly mapped memory. This host memory is
|
||||
expected to be an equal or larger size than provided, and be mapped with at
|
||||
least PROT_READ | PROT_WRITE. If it is not, the resulting behavior is undefined.
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size, uint32_t perms, void *ptr);
|
||||
|
||||
/*
|
||||
Unmap a region of emulation memory.
|
||||
This API deletes a memory mapping from the emulation memory space.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@address: starting address of the memory region to be unmapped.
|
||||
This address must be aligned to 4KB, or this will return with UC_ERR_ARG error.
|
||||
@size: size of the memory region to be modified.
|
||||
This size must be multiple of 4KB, or this will return with UC_ERR_ARG error.
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_unmap(uc_engine *uc, uint64_t address, size_t size);
|
||||
|
||||
/*
|
||||
Set memory permissions for emulation memory.
|
||||
This API changes permissions on an existing memory region.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@address: starting address of the memory region to be modified.
|
||||
This address must be aligned to 4KB, or this will return with UC_ERR_ARG error.
|
||||
@size: size of the memory region to be modified.
|
||||
This size must be multiple of 4KB, or this will return with UC_ERR_ARG error.
|
||||
@perms: New permissions for the mapped region.
|
||||
This must be some combination of UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC,
|
||||
or this will return with UC_ERR_ARG error.
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_protect(uc_engine *uc, uint64_t address, size_t size, uint32_t perms);
|
||||
|
||||
/*
|
||||
Retrieve all memory regions mapped by uc_mem_map() and uc_mem_map_ptr()
|
||||
This API allocates memory for @regions, and user must free this memory later
|
||||
by free() to avoid leaking memory.
|
||||
NOTE: memory regions may be splitted by uc_mem_unmap()
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@regions: pointer to an array of uc_mem_region struct. This is allocated by
|
||||
Unicorn, and must be freed by user later with uc_free()
|
||||
@count: pointer to number of struct uc_mem_region contained in @regions
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count);
|
||||
|
||||
/*
|
||||
Allocate a region that can be used with uc_context_{save,restore} to perform
|
||||
quick save/rollback of the CPU context, which includes registers and some
|
||||
internal metadata. Contexts may not be shared across engine instances with
|
||||
differing arches or modes.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@context: pointer to a uc_engine*. This will be updated with the pointer to
|
||||
the new context on successful return of this function.
|
||||
Later, this allocated memory must be freed with uc_free().
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_context_alloc(uc_engine *uc, uc_context **context);
|
||||
|
||||
/*
|
||||
Free the memory allocated by uc_context_alloc & uc_mem_regions.
|
||||
|
||||
@mem: memory allocated by uc_context_alloc (returned in *context), or
|
||||
by uc_mem_regions (returned in *regions)
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_free(void *mem);
|
||||
|
||||
/*
|
||||
Save a copy of the internal CPU context.
|
||||
This API should be used to efficiently make or update a saved copy of the
|
||||
internal CPU state.
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@context: handle returned by uc_context_alloc()
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_context_save(uc_engine *uc, uc_context *context);
|
||||
|
||||
/*
|
||||
Restore the current CPU context from a saved copy.
|
||||
This API should be used to roll the CPU context back to a previous
|
||||
state saved by uc_context_save().
|
||||
|
||||
@uc: handle returned by uc_open()
|
||||
@buffer: handle returned by uc_context_alloc that has been used with uc_context_save
|
||||
|
||||
@return UC_ERR_OK on success, or other value on failure (refer to uc_err enum
|
||||
for detailed error).
|
||||
*/
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_context_restore(uc_engine *uc, uc_context *context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
Binary file not shown.
@ -10,11 +10,63 @@ namespace lightSwitch {
|
||||
namespace constant {
|
||||
constexpr uint64_t base_addr = 0x80000000;
|
||||
constexpr uint64_t stack_addr = 0x3000000;
|
||||
constexpr size_t stack_size = 0x1000000;
|
||||
constexpr size_t stack_size = 280; //0x1000000
|
||||
constexpr uint64_t tls_addr = 0x2000000;
|
||||
constexpr size_t tls_size = 0x1000;
|
||||
constexpr uint32_t nro_magic = 0x304F524E; // NRO0 in reverse
|
||||
constexpr uint_t svc_unimpl = 0x177202; // "Unimplemented behaviour"
|
||||
constexpr uint32_t base_handle_index = 0xd001;
|
||||
constexpr uint32_t base_handle_index = 0xD001;
|
||||
constexpr uint16_t svc_last = 0x7F;
|
||||
constexpr uint8_t num_regs = 31;
|
||||
constexpr uint32_t tpidrro_el0 = 0x5E83; // ID of tpidrro_el0 in MRS
|
||||
};
|
||||
|
||||
namespace instr {
|
||||
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/brk-breakpoint-instruction
|
||||
// For some reason if value is set to uint16_t it isn't read correctly ?
|
||||
struct brk {
|
||||
brk(uint16_t val) {
|
||||
start = 0x0; // First 5 bits of an BRK instruction are 0
|
||||
value = val;
|
||||
end = 0x6A1; // Last 11 bits of an BRK instruction stored as uint16_t
|
||||
}
|
||||
|
||||
bool verify() {
|
||||
return (start == 0x0 && end == 0x6A1);
|
||||
}
|
||||
|
||||
uint8_t start:5;
|
||||
uint32_t value:16;
|
||||
uint16_t end:11;
|
||||
};
|
||||
|
||||
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/svc-supervisor-call
|
||||
struct svc {
|
||||
bool verify() {
|
||||
return (start == 0x1 && end == 0x6A0);
|
||||
}
|
||||
|
||||
uint8_t start:5;
|
||||
uint32_t value:16;
|
||||
uint16_t end:11;
|
||||
};
|
||||
|
||||
// https://developer.arm.com/docs/ddi0596/latest/base-instructions-alphabetic-order/mrs-move-system-register
|
||||
struct mrs {
|
||||
bool verify() {
|
||||
return (end == 0xD53);
|
||||
}
|
||||
|
||||
uint8_t Xt:5;
|
||||
uint32_t Sreg:15;
|
||||
uint16_t end:12;
|
||||
};
|
||||
};
|
||||
|
||||
enum xreg {
|
||||
x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17, x18, x19, x20, x21, x22, x23, x24, x25, x26, x27, x28, x29, x30
|
||||
};
|
||||
enum wreg {
|
||||
w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15, w16, w17, w18, w19, w20, w21, w22, w23, w24, w25, w26, w27, w28, w29, w30
|
||||
};
|
||||
}
|
@ -15,7 +15,7 @@ namespace lightSwitch {
|
||||
{"NRO", 1}
|
||||
};
|
||||
public:
|
||||
device(std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings) : cpu(new hw::Cpu()), memory(new hw::Memory(cpu->GetEngine())), state{cpu, memory, settings, logger}, os({cpu, memory, settings, logger}) {};
|
||||
device(std::shared_ptr<Logger> &logger, std::shared_ptr<Settings> &settings) : cpu(new hw::Cpu()), memory(new hw::Memory()), state{cpu, memory, settings, logger}, os({cpu, memory, settings, logger}) {};
|
||||
|
||||
void run(std::string rom_file) {
|
||||
try {
|
||||
@ -27,7 +27,7 @@ namespace lightSwitch {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
cpu->Execute(constant::base_addr);
|
||||
cpu->Execute(hw::Memory::text, memory, os.SvcHandler, &state);
|
||||
} catch (std::out_of_range &e) {
|
||||
throw exception("The ROM extension wasn't recognized.");
|
||||
}
|
||||
|
@ -1,46 +1,125 @@
|
||||
#include "cpu.h"
|
||||
#include "../constant.h"
|
||||
|
||||
|
||||
namespace lightSwitch::hw {
|
||||
Cpu::Cpu() {
|
||||
err = uc_open(UC_ARCH_ARM64, UC_MODE_ARM, &uc);
|
||||
if (err)
|
||||
throw std::runtime_error("An error occurred while running 'uc_open': " + std::string(uc_strerror(err)));
|
||||
}
|
||||
|
||||
Cpu::~Cpu() {
|
||||
uc_close(uc);
|
||||
if (child) kill(child, SIGKILL);
|
||||
}
|
||||
|
||||
void Cpu::Execute(uint64_t address) {
|
||||
// Set Registers
|
||||
SetRegister(UC_ARM64_REG_SP, constant::stack_addr + 0x100000); // Stack Pointer (For some reason programs move the stack pointer backwards so 0x100000 is added)
|
||||
SetRegister(UC_ARM64_REG_TPIDRRO_EL0, constant::tls_addr); // User Read-Only Thread ID Register
|
||||
err = uc_emu_start(uc, address, std::numeric_limits<uint64_t>::max(), 0, 0);
|
||||
if (err)
|
||||
throw std::runtime_error("An error occurred while running 'uc_emu_start': " + std::string(uc_strerror(err)));
|
||||
long *Cpu::ReadMemory(uint64_t address) { // Return a single word (32-bit)
|
||||
status = ptrace(PTRACE_PEEKDATA, child, address, NULL);
|
||||
if (status == -1) throw std::runtime_error("Cannot read memory");
|
||||
return &status;
|
||||
}
|
||||
|
||||
void Cpu::StopExecution() {
|
||||
uc_emu_stop(uc);
|
||||
void Cpu::WriteMemory(uint64_t address) { // Write a single word (32-bit)
|
||||
status = ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov);
|
||||
if (status == -1) throw std::runtime_error("Cannot write memory");
|
||||
}
|
||||
|
||||
uint64_t Cpu::GetRegister(uint32_t reg_id) {
|
||||
uint64_t registerValue;
|
||||
err = uc_reg_read(uc, reg_id, ®isterValue);
|
||||
if (err)
|
||||
throw std::runtime_error("An error occurred while running 'uc_reg_read': " + std::string(uc_strerror(err)));
|
||||
return registerValue;
|
||||
void Cpu::ReadRegisters() { // Read all registers into 'regs'
|
||||
iov = {®s, sizeof(regs)};
|
||||
status = ptrace(PTRACE_GETREGSET, child, NT_PRSTATUS, &iov);
|
||||
if (status == -1) throw std::runtime_error("Cannot read registers");
|
||||
}
|
||||
|
||||
void Cpu::SetRegister(uint32_t regid, uint64_t value) {
|
||||
err = uc_reg_write(uc, regid, &value);
|
||||
if (err)
|
||||
throw std::runtime_error("An error occurred while running 'uc_reg_write': " + std::string(uc_strerror(err)));
|
||||
void Cpu::WriteRegisters() { // Write all registers from 'regs'
|
||||
iov = {®s, sizeof(regs)};
|
||||
status = ptrace(PTRACE_SETREGSET, child, NT_PRSTATUS, &iov);
|
||||
if (status == -1) throw std::runtime_error("Cannot write registers");
|
||||
}
|
||||
|
||||
uc_engine *Cpu::GetEngine() {
|
||||
return uc;
|
||||
void Cpu::ResumeProcess() { // Resumes a process stopped due to a signal
|
||||
status = ptrace(PTRACE_CONT, child, NULL, NULL);
|
||||
if (status == -1) throw std::runtime_error("Cannot resume process");
|
||||
}
|
||||
|
||||
void bin(unsigned n) {
|
||||
unsigned i;
|
||||
std::string s;
|
||||
for (i = 1 << 31; i > 0; i = i / 2)
|
||||
(n & i) ? s += "1" : s += "0";
|
||||
s = s.substr(16);
|
||||
syslog(LOG_WARNING, "%s", s.c_str());
|
||||
}
|
||||
|
||||
void Cpu::WriteBreakpoint(uint64_t &address_, uint64_t &size) {
|
||||
auto address = (uint32_t *) address_;
|
||||
for (uint64_t iter = 0; iter < size; iter++) {
|
||||
auto instr_svc = reinterpret_cast<instr::svc *>(address + iter);
|
||||
auto instr_mrs = reinterpret_cast<instr::mrs *>(address + iter);
|
||||
if (instr_svc->verify()) {
|
||||
// syslog(LOG_WARNING, "Found SVC call: 0x%X, At location 0x%X", instr_svc->value, ((uint64_t)address)+iter);
|
||||
instr::brk brk((uint16_t) instr_svc->value);
|
||||
address[iter] = *(uint32_t *) (&brk);
|
||||
} else if (instr_mrs->verify() && instr_mrs->Sreg == constant::tpidrro_el0) {
|
||||
// syslog(LOG_WARNING, "Found MRS call: 0x%X, At location 0x%X", instr_mrs->Xt, ((uint64_t)address)+iter);
|
||||
instr::brk brk((uint16_t) (constant::svc_last + 1 + instr_mrs->Xt));
|
||||
address[iter] = *(uint32_t *) (&brk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Cpu::Execute(Memory::Region region, std::shared_ptr<Memory> memory, std::function<void(uint16_t, void *)> svc_handler, void *device) {
|
||||
tls = memory->region_map.at(hw::Memory::tls).address;
|
||||
hw::Memory::RegionData rom = memory->region_map.at(hw::Memory::text);
|
||||
WriteBreakpoint(rom.address, rom.size);
|
||||
child = ExecuteChild(rom.address);
|
||||
int stat = 0;
|
||||
while (waitpid(child, &stat, 0)) {
|
||||
if (WIFSTOPPED(stat)) {
|
||||
ReadRegisters();
|
||||
//syslog(LOG_INFO, "PC is at 0x%X", regs.pc);
|
||||
if (!regs.pc || regs.pc == 0xBADC0DE) break;
|
||||
// We store the instruction value as the immediate value. 0x0 to 0x7F are SVC, 0x80 to 0x9E is MRS for TPIDRRO_EL0.
|
||||
auto instr = reinterpret_cast<instr::brk *>(ReadMemory(regs.pc));
|
||||
if (instr->verify()) {
|
||||
if (instr->value <= constant::svc_last) {
|
||||
svc_handler((uint16_t) instr->value, device);
|
||||
syslog(LOG_ERR, "SVC has been called 0x%X", instr->value);
|
||||
if (stop) break;
|
||||
} else if (instr->value > constant::svc_last && instr->value <= constant::svc_last + constant::num_regs) {
|
||||
// Catch MRS that reads the value of TPIDRRO_EL0 (TLS)
|
||||
// https://switchbrew.org/wiki/Thread_Local_Storage
|
||||
SetRegister(xreg(instr->value - (constant::svc_last + 1)), tls);
|
||||
syslog(LOG_ERR, "MRS has been called 0x%X", instr->value - (constant::svc_last + 1));
|
||||
} else syslog(LOG_ERR, "Received unhandled BRK 0x%X", instr->value);
|
||||
}
|
||||
regs.pc += 4; // Increment program counter by a single instruction (32 bits)
|
||||
WriteRegisters();
|
||||
} else if (WIFEXITED(stat))
|
||||
break;
|
||||
ResumeProcess();
|
||||
}
|
||||
kill(child, SIGABRT);
|
||||
child = 0;
|
||||
stop = false;
|
||||
}
|
||||
|
||||
pid_t Cpu::ExecuteChild(uint64_t address) {
|
||||
pid_t pid = fork();
|
||||
if (!pid) {
|
||||
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
|
||||
asm volatile("BR %0"::"r"(address));
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
void Cpu::StopExecution() { stop = true; }
|
||||
|
||||
uint64_t Cpu::GetRegister(xreg reg_id) {
|
||||
return regs.regs[reg_id];
|
||||
}
|
||||
|
||||
void Cpu::SetRegister(xreg reg_id, uint64_t value) {
|
||||
regs.regs[reg_id] = value;
|
||||
}
|
||||
|
||||
uint64_t Cpu::GetRegister(wreg reg_id) {
|
||||
return ((uint32_t *) regs.regs)[wreg_lut[reg_id]];
|
||||
}
|
||||
|
||||
void Cpu::SetRegister(wreg reg_id, uint32_t value) {
|
||||
((uint32_t *) regs.regs)[wreg_lut[reg_id]] = value;
|
||||
}
|
||||
}
|
@ -1,29 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#include <syslog.h>
|
||||
#include <unicorn/unicorn.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/elf.h>
|
||||
#include <switch/constant.h>
|
||||
#include "memory.h"
|
||||
|
||||
namespace lightSwitch::hw {
|
||||
class Cpu {
|
||||
private:
|
||||
uc_engine *uc;
|
||||
uc_err err;
|
||||
bool stop = false;
|
||||
long status = 0;
|
||||
pid_t child;
|
||||
iovec iov;
|
||||
user_pt_regs regs;
|
||||
uint64_t tls;
|
||||
|
||||
static pid_t ExecuteChild(uint64_t address);
|
||||
|
||||
void ReadRegisters();
|
||||
|
||||
void WriteRegisters();
|
||||
|
||||
long *ReadMemory(uint64_t address);
|
||||
|
||||
void WriteMemory(uint64_t address);
|
||||
|
||||
void ResumeProcess();
|
||||
|
||||
void WriteBreakpoint(uint64_t &address, uint64_t &size);
|
||||
|
||||
uint8_t wreg_lut[31] = {1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 43, 45, 47, 49, 51, 53, 55, 57, 59, 61};
|
||||
|
||||
public:
|
||||
Cpu();
|
||||
|
||||
~Cpu();
|
||||
|
||||
void SetHook(void *HookInterrupt);
|
||||
|
||||
void Execute(uint64_t address);
|
||||
void Execute(Memory::Region region, std::shared_ptr<Memory> memory, std::function<void(uint16_t, void *)> svc_handler, void *device);
|
||||
|
||||
void StopExecution();
|
||||
|
||||
uint64_t GetRegister(uint32_t regid);
|
||||
uint64_t GetRegister(xreg reg_id);
|
||||
|
||||
void SetRegister(uint32_t regid, uint64_t value);
|
||||
void SetRegister(xreg reg_id, uint64_t value);
|
||||
|
||||
uc_engine *GetEngine();
|
||||
uint64_t GetRegister(wreg reg_id);
|
||||
|
||||
void SetRegister(wreg reg_id, uint32_t value);
|
||||
};
|
||||
}
|
||||
|
@ -6,21 +6,18 @@
|
||||
|
||||
namespace lightSwitch::hw {
|
||||
// TODO: Boundary checks
|
||||
Memory::Memory(uc_engine *uc_) : uc(uc_) {
|
||||
Memory::Memory() {
|
||||
// Map stack memory
|
||||
Memory::Map(constant::stack_addr, constant::stack_size, stack);
|
||||
// Memory::Map(constant::stack_addr, constant::stack_size, stack);
|
||||
// Map TLS memory
|
||||
Memory::Map(constant::tls_addr, constant::tls_size, tls);
|
||||
}
|
||||
|
||||
void Memory::Map(uint64_t address, size_t size, Region region) {
|
||||
region_map.insert(std::pair<Region, RegionData>(region, {address, size}));
|
||||
void *ptr = mmap((void *) address, size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0, 0);
|
||||
void *ptr = mmap((void *) address, size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON | MAP_FIXED, 0, 0);
|
||||
if (!ptr)
|
||||
throw exception("An occurred while mapping region");
|
||||
uc_err err = uc_mem_map_ptr(uc, address, size, UC_PROT_ALL, (void *) address);
|
||||
if (err)
|
||||
throw exception("Unicorn failed to map region: " + std::string(uc_strerror(err)));
|
||||
}
|
||||
|
||||
void Memory::Write(void *data, uint64_t offset, size_t size) {
|
||||
|
@ -1,13 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unicorn/unicorn.h>
|
||||
#include <map>
|
||||
|
||||
namespace lightSwitch::hw {
|
||||
class Memory {
|
||||
private:
|
||||
uc_engine *uc;
|
||||
public:
|
||||
enum Region {
|
||||
stack, tls, text, rodata, data, bss
|
||||
@ -18,7 +15,7 @@ namespace lightSwitch::hw {
|
||||
};
|
||||
std::map<Region, RegionData> region_map;
|
||||
|
||||
Memory(uc_engine *uc_);
|
||||
Memory();
|
||||
|
||||
void Map(uint64_t address, size_t size, Region region);
|
||||
|
||||
|
@ -22,7 +22,7 @@ namespace lightSwitch::os::ipc {
|
||||
bool handle_desc : 1;
|
||||
} *req_info;
|
||||
|
||||
IpcRequest(uint8_t *tlsPtr, device_state& state);
|
||||
IpcRequest(uint8_t *tlsPtr, device_state &state);
|
||||
|
||||
template<typename T>
|
||||
T GetValue();
|
||||
|
@ -1,49 +1,18 @@
|
||||
|
||||
#include <unicorn/arm64.h>
|
||||
#include "os.h"
|
||||
|
||||
namespace lightSwitch::os {
|
||||
OS::OS(device_state state_) : state(std::move(state_)) {
|
||||
uc_err err = uc_hook_add(state.cpu->GetEngine(), &hook, UC_HOOK_INTR,
|
||||
(void *) HookInterrupt, &state, 1, 0);
|
||||
if (err)
|
||||
throw std::runtime_error("An error occurred while running 'uc_hook_add': " +
|
||||
std::string(uc_strerror(err)));
|
||||
}
|
||||
OS::OS(device_state state_) : state(std::move(state_)) {}
|
||||
|
||||
OS::~OS() {
|
||||
uc_hook_del(state.cpu->GetEngine(), hook);
|
||||
}
|
||||
|
||||
void OS::HookInterrupt(uc_engine *uc, uint32_t int_no, void *user_data) {
|
||||
device_state state = *((device_state *) user_data);
|
||||
try {
|
||||
if (int_no == 2) {
|
||||
uint32_t instr{};
|
||||
uc_err err = uc_mem_read(uc, state.cpu->GetRegister(UC_ARM64_REG_PC) - 4, &instr, 4);
|
||||
if (err)
|
||||
throw exception("An error occurred while running 'uc_mem_read': " + std::string(uc_strerror(err)));
|
||||
uint32_t svcId = instr >> 5U & 0xFF;
|
||||
SvcHandler(svcId, state);
|
||||
} else {
|
||||
state.logger->write(Logger::ERROR, "An unhandled interrupt has occurred: {}", int_no);
|
||||
state.cpu->StopExecution();
|
||||
}
|
||||
} catch (exception &e) {
|
||||
state.logger->write(Logger::WARN, "An exception occurred during an interrupt: {}", e.what());
|
||||
} catch (...) {
|
||||
state.logger->write(Logger::WARN, "An unknown exception has occurred.");
|
||||
}
|
||||
}
|
||||
|
||||
void OS::SvcHandler(uint32_t svc, device_state &state) {
|
||||
void OS::SvcHandler(uint16_t svc, void *vstate) {
|
||||
device_state state = *((device_state *) vstate);
|
||||
if (svc::svcTable[svc])
|
||||
(*svc::svcTable[svc])(state);
|
||||
else
|
||||
state.logger->write(Logger::WARN, "Unimplemented SVC 0x{0:x}", svc);
|
||||
}
|
||||
|
||||
void OS::SvcHandler(uint32_t svc) {
|
||||
SvcHandler(svc, state);
|
||||
void OS::HandleSvc(uint16_t svc) {
|
||||
SvcHandler(svc, &state);
|
||||
}
|
||||
}
|
@ -11,16 +11,11 @@ namespace lightSwitch::os {
|
||||
class OS {
|
||||
private:
|
||||
device_state state;
|
||||
uc_hook hook{};
|
||||
public:
|
||||
OS(device_state state_);
|
||||
|
||||
~OS();
|
||||
static void SvcHandler(uint16_t svc, void *vstate);
|
||||
|
||||
static void HookInterrupt(uc_engine *uc, uint32_t int_no, void *user_data);
|
||||
|
||||
static void SvcHandler(uint32_t svc, device_state &state);
|
||||
|
||||
void SvcHandler(uint32_t svc);
|
||||
void HandleSvc(uint16_t svc);
|
||||
};
|
||||
}
|
@ -2,56 +2,55 @@
|
||||
#include <string>
|
||||
#include <syslog.h>
|
||||
#include <utility>
|
||||
#include <unicorn/arm64.h>
|
||||
#include "svc.h"
|
||||
|
||||
namespace lightSwitch::os::svc {
|
||||
void ConnectToNamedPort(device_state &state) {
|
||||
char port[constant::port_size]{0};
|
||||
state.mem->Read(port, state.cpu->GetRegister(UC_ARM64_REG_X1), constant::port_size);
|
||||
state.mem->Read(port, state.cpu->GetRegister(xreg::x1), constant::port_size);
|
||||
if (std::strcmp(port, "sm:") == 0)
|
||||
state.cpu->SetRegister(UC_ARM64_REG_W1, constant::sm_handle);
|
||||
state.cpu->SetRegister(wreg::w1, constant::sm_handle);
|
||||
else {
|
||||
state.logger->write(Logger::ERROR, "svcConnectToNamedPort tried connecting to invalid port \"{0}\"", port);
|
||||
state.cpu->StopExecution();
|
||||
}
|
||||
state.cpu->SetRegister(UC_ARM64_REG_W0, 0);
|
||||
state.cpu->SetRegister(wreg::w0, 0);
|
||||
}
|
||||
|
||||
void SendSyncRequest(device_state &state) {
|
||||
state.logger->write(Logger::DEBUG, "svcSendSyncRequest called for handle 0x{0:x}.", state.cpu->GetRegister(UC_ARM64_REG_X0));
|
||||
state.logger->write(Logger::DEBUG, "svcSendSyncRequest called for handle 0x{0:x}.", state.cpu->GetRegister(xreg::x0));
|
||||
uint8_t tls[constant::tls_ipc_size];
|
||||
state.mem->Read(&tls, constant::tls_addr, constant::tls_ipc_size);
|
||||
ipc::IpcRequest request(tls, state);
|
||||
state.cpu->SetRegister(UC_ARM64_REG_W0, 0);
|
||||
state.cpu->SetRegister(wreg::w0, 0);
|
||||
}
|
||||
|
||||
void OutputDebugString(device_state &state) {
|
||||
std::string debug(state.cpu->GetRegister(UC_ARM64_REG_X1), '\0');
|
||||
state.mem->Read((void *) debug.data(), state.cpu->GetRegister(UC_ARM64_REG_X0), state.cpu->GetRegister(UC_ARM64_REG_X1));
|
||||
std::string debug(state.cpu->GetRegister(xreg::x1), '\0');
|
||||
state.mem->Read((void *) debug.data(), state.cpu->GetRegister(xreg::x0), state.cpu->GetRegister(xreg::x1));
|
||||
state.logger->write(Logger::INFO, "ROM Output: {0}", debug.c_str());
|
||||
state.cpu->SetRegister(UC_ARM64_REG_W0, 0);
|
||||
state.cpu->SetRegister(wreg::w0, 0);
|
||||
}
|
||||
|
||||
void GetInfo(device_state &state) {
|
||||
switch (state.cpu->GetRegister(UC_ARM64_REG_X1)) {
|
||||
switch (state.cpu->GetRegister(xreg::x1)) {
|
||||
case constant::infoState::AllowedCpuIdBitmask:
|
||||
case constant::infoState::AllowedThreadPriorityMask:
|
||||
case constant::infoState::IsCurrentProcessBeingDebugged:
|
||||
state.cpu->SetRegister(UC_ARM64_REG_X1, 0);
|
||||
state.cpu->SetRegister(xreg::x1, 0);
|
||||
break;
|
||||
case constant::infoState::AddressSpaceBaseAddr:
|
||||
state.cpu->SetRegister(UC_ARM64_REG_X1, constant::base_addr);
|
||||
state.cpu->SetRegister(xreg::x1, constant::base_addr);
|
||||
break;
|
||||
case constant::infoState::TitleId:
|
||||
state.cpu->SetRegister(UC_ARM64_REG_X1, 0); // TODO: Complete this
|
||||
state.cpu->SetRegister(xreg::x1, 0); // TODO: Complete this
|
||||
break;
|
||||
default:
|
||||
state.logger->write(Logger::WARN, "Unimplemented GetInfo call. ID1: {0}, ID2: {1}", state.cpu->GetRegister(UC_ARM64_REG_X1), state.cpu->GetRegister(UC_ARM64_REG_X3));
|
||||
state.cpu->SetRegister(UC_ARM64_REG_X1, constant::svc_unimpl);
|
||||
state.logger->write(Logger::WARN, "Unimplemented GetInfo call. ID1: {0}, ID2: {1}", state.cpu->GetRegister(xreg::x1), state.cpu->GetRegister(xreg::x3));
|
||||
state.cpu->SetRegister(xreg::x1, constant::svc_unimpl);
|
||||
return;
|
||||
}
|
||||
state.cpu->SetRegister(UC_ARM64_REG_W0, 0);
|
||||
state.cpu->SetRegister(wreg::w0, 0);
|
||||
}
|
||||
|
||||
void ExitProcess(device_state &state) {
|
||||
|
Loading…
Reference in New Issue
Block a user