Declare global arch arrays with contents (next branch) (#1186)
* Declare global arch arrays with contents (#1171)
This eliminates the need for archs_enable() and eliminates the racey
initialization.
This makes the architecture-specific init and option functions
non-static so that they may be called from a different file.
Cherry-picked 853a2870
* Add cs_arch_disallowed_mode_mask global
Cherry-pick 94bce437:
mips: CS_MODE_MIPS32R6 implies CS_MODE_32
Cherry-pick 8998a3a1:
ppc: fix endian check (#1029)
Fixes bug where endianness could not be set for ppc.
Remove `big_endian` field of `cs_struct`.
Added a helper macro `MODE_IS_BIG_ENDIAN()` to check if
`CS_MODE_BIG_ENDIAN` is set.
Refactored `cs_open()` check for valid mode out of arch-specific code
into arch-independent code. Also added a valid mode check to
`cs_option()`. The checks use a new global array
`cs_arch_disallowed_mode_mask[]`.
* Make global arrays static
Make all_arch uint32_t to guarantee a certain number of bits (with
adequate room for growth).
This commit is contained in:
parent
ba25ab0fe1
commit
65da43d0b1
|
@ -238,7 +238,7 @@ static DecodeStatus _getInstruction(cs_struct *ud, MCInst *MI,
|
|||
MI->flat_insn->detail->arm64.operands[i].vector_index = -1;
|
||||
}
|
||||
|
||||
if (ud->big_endian)
|
||||
if (MODE_IS_BIG_ENDIAN(ud->mode))
|
||||
insn = (code[3] << 0) | (code[2] << 8) |
|
||||
(code[1] << 16) | ((uint32_t) code[0] << 24);
|
||||
else
|
||||
|
|
|
@ -8,15 +8,11 @@
|
|||
#include "AArch64Disassembler.h"
|
||||
#include "AArch64InstPrinter.h"
|
||||
#include "AArch64Mapping.h"
|
||||
#include "AArch64Module.h"
|
||||
|
||||
static cs_err init(cs_struct *ud)
|
||||
cs_err AArch64_global_init(cs_struct *ud)
|
||||
{
|
||||
MCRegisterInfo *mri;
|
||||
|
||||
// verify if requested mode is valid
|
||||
if (ud->mode & ~(CS_MODE_LITTLE_ENDIAN | CS_MODE_ARM | CS_MODE_BIG_ENDIAN))
|
||||
return CS_ERR_MODE;
|
||||
|
||||
mri = cs_mem_malloc(sizeof(*mri));
|
||||
|
||||
AArch64_init(mri);
|
||||
|
@ -36,22 +32,13 @@ static cs_err init(cs_struct *ud)
|
|||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
static cs_err option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
cs_err AArch64_option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
{
|
||||
if (type == CS_OPT_MODE) {
|
||||
handle->big_endian = (((cs_mode)value & CS_MODE_BIG_ENDIAN) != 0);
|
||||
handle->mode = (cs_mode)value;
|
||||
}
|
||||
|
||||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
void AArch64_enable(void)
|
||||
{
|
||||
cs_arch_init[CS_ARCH_ARM64] = init;
|
||||
cs_arch_option[CS_ARCH_ARM64] = option;
|
||||
|
||||
// support this arch
|
||||
all_arch |= (1 << CS_ARCH_ARM64);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* Capstone Disassembly Engine */
|
||||
/* By Travis Finkenauer <tmfinken@gmail.com>, 2018 */
|
||||
|
||||
#ifndef CS_AARCH64_MODULE_H
|
||||
#define CS_AARCH64_MODULE_H
|
||||
|
||||
#include "../../utils.h"
|
||||
|
||||
cs_err AArch64_global_init(cs_struct *ud);
|
||||
cs_err AArch64_option(cs_struct *handle, cs_opt_type type, size_t value);
|
||||
|
||||
#endif
|
|
@ -482,7 +482,7 @@ static DecodeStatus _ARM_getInstruction(cs_struct *ud, MCInst *MI, const uint8_t
|
|||
}
|
||||
}
|
||||
|
||||
if (ud->big_endian)
|
||||
if (MODE_IS_BIG_ENDIAN(ud->mode))
|
||||
insn = (code[3] << 0) |
|
||||
(code[2] << 8) |
|
||||
(code[1] << 16) |
|
||||
|
@ -725,7 +725,7 @@ static DecodeStatus _Thumb_getInstruction(cs_struct *ud, MCInst *MI, const uint8
|
|||
}
|
||||
}
|
||||
|
||||
if (ud->big_endian)
|
||||
if (MODE_IS_BIG_ENDIAN(ud->mode))
|
||||
insn16 = (code[0] << 8) | code[1];
|
||||
else
|
||||
insn16 = (code[1] << 8) | code[0];
|
||||
|
@ -776,7 +776,7 @@ static DecodeStatus _Thumb_getInstruction(cs_struct *ud, MCInst *MI, const uint8
|
|||
// not enough data
|
||||
return MCDisassembler_Fail;
|
||||
|
||||
if (ud->big_endian)
|
||||
if (MODE_IS_BIG_ENDIAN(ud->mode))
|
||||
insn32 = (code[3] << 0) |
|
||||
(code[2] << 8) |
|
||||
(code[1] << 16) |
|
||||
|
|
|
@ -8,16 +8,11 @@
|
|||
#include "ARMDisassembler.h"
|
||||
#include "ARMInstPrinter.h"
|
||||
#include "ARMMapping.h"
|
||||
#include "ARMModule.h"
|
||||
|
||||
static cs_err init(cs_struct *ud)
|
||||
cs_err ARM_global_init(cs_struct *ud)
|
||||
{
|
||||
MCRegisterInfo *mri;
|
||||
|
||||
// verify if requested mode is valid
|
||||
if (ud->mode & ~(CS_MODE_LITTLE_ENDIAN | CS_MODE_ARM | CS_MODE_V8 |
|
||||
CS_MODE_MCLASS | CS_MODE_THUMB | CS_MODE_BIG_ENDIAN))
|
||||
return CS_ERR_MODE;
|
||||
|
||||
mri = cs_mem_malloc(sizeof(*mri));
|
||||
|
||||
ARM_init(mri);
|
||||
|
@ -42,7 +37,7 @@ static cs_err init(cs_struct *ud)
|
|||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
static cs_err option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
cs_err ARM_option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
{
|
||||
switch(type) {
|
||||
case CS_OPT_MODE:
|
||||
|
@ -52,7 +47,6 @@ static cs_err option(cs_struct *handle, cs_opt_type type, size_t value)
|
|||
handle->disasm = ARM_getInstruction;
|
||||
|
||||
handle->mode = (cs_mode)value;
|
||||
handle->big_endian = ((handle->mode & CS_MODE_BIG_ENDIAN) != 0);
|
||||
|
||||
break;
|
||||
case CS_OPT_SYNTAX:
|
||||
|
@ -66,13 +60,4 @@ static cs_err option(cs_struct *handle, cs_opt_type type, size_t value)
|
|||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
void ARM_enable(void)
|
||||
{
|
||||
cs_arch_init[CS_ARCH_ARM] = init;
|
||||
cs_arch_option[CS_ARCH_ARM] = option;
|
||||
|
||||
// support this arch
|
||||
all_arch |= (1 << CS_ARCH_ARM);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* Capstone Disassembly Engine */
|
||||
/* By Travis Finkenauer <tmfinken@gmail.com>, 2018 */
|
||||
|
||||
#ifndef CS_ARM_MODULE_H
|
||||
#define CS_ARM_MODULE_H
|
||||
|
||||
#include "../../utils.h"
|
||||
|
||||
cs_err ARM_global_init(cs_struct *ud);
|
||||
cs_err ARM_option(cs_struct *handle, cs_opt_type type, size_t value);
|
||||
|
||||
#endif
|
|
@ -7,8 +7,9 @@
|
|||
#include "EVMDisassembler.h"
|
||||
#include "EVMInstPrinter.h"
|
||||
#include "EVMMapping.h"
|
||||
#include "EVMModule.h"
|
||||
|
||||
static cs_err init(cs_struct *ud)
|
||||
cs_err EVM_global_init(cs_struct *ud)
|
||||
{
|
||||
// verify if requested mode is valid
|
||||
if (ud->mode)
|
||||
|
@ -24,18 +25,9 @@ static cs_err init(cs_struct *ud)
|
|||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
static cs_err option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
cs_err EVM_option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
{
|
||||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
void EVM_enable(void)
|
||||
{
|
||||
cs_arch_init[CS_ARCH_EVM] = init;
|
||||
cs_arch_option[CS_ARCH_EVM] = option;
|
||||
|
||||
// support this arch
|
||||
all_arch |= (1 << CS_ARCH_EVM);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* Capstone Disassembly Engine */
|
||||
/* By Travis Finkenauer <tmfinken@gmail.com>, 2018 */
|
||||
|
||||
#ifndef CS_EVM_MODULE_H
|
||||
#define CS_EVM_MODULE_H
|
||||
|
||||
#include "../../utils.h"
|
||||
|
||||
cs_err EVM_global_init(cs_struct *ud);
|
||||
cs_err EVM_option(cs_struct *handle, cs_opt_type type, size_t value);
|
||||
|
||||
#endif
|
|
@ -8,8 +8,9 @@
|
|||
#include "M680XDisassembler.h"
|
||||
#include "M680XDisassemblerInternals.h"
|
||||
#include "M680XInstPrinter.h"
|
||||
#include "M680XModule.h"
|
||||
|
||||
static cs_err init(cs_struct *ud)
|
||||
cs_err M680X_global_init(cs_struct *ud)
|
||||
{
|
||||
m680x_info *info;
|
||||
cs_err errcode;
|
||||
|
@ -66,20 +67,11 @@ static cs_err init(cs_struct *ud)
|
|||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
static cs_err option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
cs_err M680X_option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
{
|
||||
//TODO
|
||||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
void M680X_enable(void)
|
||||
{
|
||||
cs_arch_init[CS_ARCH_M680X] = init;
|
||||
cs_arch_option[CS_ARCH_M680X] = option;
|
||||
|
||||
// support this arch
|
||||
all_arch |= (1 << CS_ARCH_M680X);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* Capstone Disassembly Engine */
|
||||
/* By Travis Finkenauer <tmfinken@gmail.com>, 2018 */
|
||||
|
||||
#ifndef CS_M680X_MODULE_H
|
||||
#define CS_M680X_MODULE_H
|
||||
|
||||
#include "../../utils.h"
|
||||
|
||||
cs_err M680X_global_init(cs_struct *ud);
|
||||
cs_err M680X_option(cs_struct *handle, cs_opt_type type, size_t value);
|
||||
|
||||
#endif
|
|
@ -7,8 +7,9 @@
|
|||
#include "../../MCRegisterInfo.h"
|
||||
#include "M68KDisassembler.h"
|
||||
#include "M68KInstPrinter.h"
|
||||
#include "M68KModule.h"
|
||||
|
||||
static cs_err init(cs_struct *ud)
|
||||
cs_err M68K_global_init(cs_struct *ud)
|
||||
{
|
||||
m68k_info *info;
|
||||
|
||||
|
@ -32,19 +33,10 @@ static cs_err init(cs_struct *ud)
|
|||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
static cs_err option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
cs_err M68K_option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
{
|
||||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
void M68K_enable(void)
|
||||
{
|
||||
cs_arch_init[CS_ARCH_M68K] = init;
|
||||
cs_arch_option[CS_ARCH_M68K] = option;
|
||||
|
||||
// support this arch
|
||||
all_arch |= (1 << CS_ARCH_M68K);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* Capstone Disassembly Engine */
|
||||
/* By Travis Finkenauer <tmfinken@gmail.com>, 2018 */
|
||||
|
||||
#ifndef CS_M68K_MODULE_H
|
||||
#define CS_M68K_MODULE_H
|
||||
|
||||
#include "../../utils.h"
|
||||
|
||||
cs_err M68K_global_init(cs_struct *ud);
|
||||
cs_err M68K_option(cs_struct *handle, cs_opt_type type, size_t value);
|
||||
|
||||
#endif
|
|
@ -509,7 +509,7 @@ bool Mips_getInstruction(csh ud, const uint8_t *code, size_t code_len, MCInst *i
|
|||
DecodeStatus status = MipsDisassembler_getInstruction(handle->mode, instr,
|
||||
code, code_len,
|
||||
size,
|
||||
address, handle->big_endian, (MCRegisterInfo *)info);
|
||||
address, MODE_IS_BIG_ENDIAN(handle->mode), (MCRegisterInfo *)info);
|
||||
|
||||
return status == MCDisassembler_Success;
|
||||
}
|
||||
|
|
|
@ -8,18 +8,22 @@
|
|||
#include "MipsDisassembler.h"
|
||||
#include "MipsInstPrinter.h"
|
||||
#include "MipsMapping.h"
|
||||
#include "MipsModule.h"
|
||||
|
||||
// Returns mode value with implied bits set
|
||||
static inline cs_mode updated_mode(cs_mode mode)
|
||||
{
|
||||
if (mode & CS_MODE_MIPS32R6) {
|
||||
mode |= CS_MODE_32;
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
|
||||
static cs_err init(cs_struct *ud)
|
||||
cs_err Mips_global_init(cs_struct *ud)
|
||||
{
|
||||
MCRegisterInfo *mri;
|
||||
|
||||
// verify if requested mode is valid
|
||||
if (ud->mode & ~(CS_MODE_LITTLE_ENDIAN | CS_MODE_32 | CS_MODE_64 |
|
||||
CS_MODE_MICRO | CS_MODE_MIPS32R6 | CS_MODE_BIG_ENDIAN |
|
||||
CS_MODE_MIPS2 | CS_MODE_MIPS3))
|
||||
return CS_ERR_MODE;
|
||||
|
||||
mri = cs_mem_malloc(sizeof(*mri));
|
||||
|
||||
Mips_init(mri);
|
||||
|
@ -36,24 +40,14 @@ static cs_err init(cs_struct *ud)
|
|||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
static cs_err option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
cs_err Mips_option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
{
|
||||
if (type == CS_OPT_MODE) {
|
||||
handle->mode = (cs_mode)value;
|
||||
handle->big_endian = ((handle->mode & CS_MODE_BIG_ENDIAN) != 0);
|
||||
handle->mode = updated_mode(value);
|
||||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
return CS_ERR_OPTION;
|
||||
}
|
||||
|
||||
void Mips_enable(void)
|
||||
{
|
||||
cs_arch_init[CS_ARCH_MIPS] = init;
|
||||
cs_arch_option[CS_ARCH_MIPS] = option;
|
||||
|
||||
// support this arch
|
||||
all_arch |= (1 << CS_ARCH_MIPS);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* Capstone Disassembly Engine */
|
||||
/* By Travis Finkenauer <tmfinken@gmail.com>, 2018 */
|
||||
|
||||
#ifndef CS_MIPS_MODULE_H
|
||||
#define CS_MIPS_MODULE_H
|
||||
|
||||
#include "../../utils.h"
|
||||
|
||||
cs_err Mips_global_init(cs_struct *ud);
|
||||
cs_err Mips_option(cs_struct *handle, cs_opt_type type, size_t value);
|
||||
|
||||
#endif
|
|
@ -363,7 +363,7 @@ static DecodeStatus getInstruction(MCInst *MI,
|
|||
}
|
||||
|
||||
// The instruction is big-endian encoded.
|
||||
if (MI->csh->mode & CS_MODE_BIG_ENDIAN)
|
||||
if (MODE_IS_BIG_ENDIAN(MI->csh->mode))
|
||||
insn = ((uint32_t) code[0] << 24) | (code[1] << 16) |
|
||||
(code[2] << 8) | (code[3] << 0);
|
||||
else
|
||||
|
|
|
@ -8,16 +8,11 @@
|
|||
#include "PPCDisassembler.h"
|
||||
#include "PPCInstPrinter.h"
|
||||
#include "PPCMapping.h"
|
||||
#include "PPCModule.h"
|
||||
|
||||
static cs_err init(cs_struct *ud)
|
||||
cs_err PPC_global_init(cs_struct *ud)
|
||||
{
|
||||
MCRegisterInfo *mri;
|
||||
|
||||
// verify if requested mode is valid
|
||||
if (ud->mode & ~(CS_MODE_LITTLE_ENDIAN | CS_MODE_32 | CS_MODE_64 |
|
||||
CS_MODE_BIG_ENDIAN | CS_MODE_QPX))
|
||||
return CS_ERR_MODE;
|
||||
|
||||
mri = (MCRegisterInfo *) cs_mem_malloc(sizeof(*mri));
|
||||
|
||||
PPC_init(mri);
|
||||
|
@ -35,25 +30,16 @@ static cs_err init(cs_struct *ud)
|
|||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
static cs_err option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
cs_err PPC_option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
{
|
||||
if (type == CS_OPT_SYNTAX)
|
||||
handle->syntax = (int) value;
|
||||
|
||||
if (type == CS_OPT_MODE) {
|
||||
handle->big_endian = (((cs_mode)value & CS_MODE_BIG_ENDIAN) != 0);
|
||||
handle->mode = (cs_mode)value;
|
||||
}
|
||||
|
||||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
void PPC_enable(void)
|
||||
{
|
||||
cs_arch_init[CS_ARCH_PPC] = init;
|
||||
cs_arch_option[CS_ARCH_PPC] = option;
|
||||
|
||||
// support this arch
|
||||
all_arch |= (1 << CS_ARCH_PPC);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* Capstone Disassembly Engine */
|
||||
/* By Travis Finkenauer <tmfinken@gmail.com>, 2018 */
|
||||
|
||||
#ifndef CS_POWERPC_MODULE_H
|
||||
#define CS_POWERPC_MODULE_H
|
||||
|
||||
#include "../../utils.h"
|
||||
|
||||
cs_err PPC_global_init(cs_struct *ud);
|
||||
cs_err PPC_option(cs_struct *handle, cs_opt_type type, size_t value);
|
||||
|
||||
#endif
|
|
@ -8,15 +8,11 @@
|
|||
#include "SparcDisassembler.h"
|
||||
#include "SparcInstPrinter.h"
|
||||
#include "SparcMapping.h"
|
||||
#include "SparcModule.h"
|
||||
|
||||
static cs_err init(cs_struct *ud)
|
||||
cs_err Sparc_global_init(cs_struct *ud)
|
||||
{
|
||||
MCRegisterInfo *mri;
|
||||
|
||||
// verify if requested mode is valid
|
||||
if (ud->mode & ~(CS_MODE_BIG_ENDIAN | CS_MODE_V9))
|
||||
return CS_ERR_MODE;
|
||||
|
||||
mri = cs_mem_malloc(sizeof(*mri));
|
||||
|
||||
Sparc_init(mri);
|
||||
|
@ -34,25 +30,16 @@ static cs_err init(cs_struct *ud)
|
|||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
static cs_err option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
cs_err Sparc_option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
{
|
||||
if (type == CS_OPT_SYNTAX)
|
||||
handle->syntax = (int) value;
|
||||
|
||||
if (type == CS_OPT_MODE) {
|
||||
handle->big_endian = (((cs_mode)value & CS_MODE_BIG_ENDIAN) != 0);
|
||||
handle->mode = (cs_mode)value;
|
||||
}
|
||||
|
||||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
void Sparc_enable(void)
|
||||
{
|
||||
cs_arch_init[CS_ARCH_SPARC] = init;
|
||||
cs_arch_option[CS_ARCH_SPARC] = option;
|
||||
|
||||
// support this arch
|
||||
all_arch |= (1 << CS_ARCH_SPARC);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* Capstone Disassembly Engine */
|
||||
/* By Travis Finkenauer <tmfinken@gmail.com>, 2018 */
|
||||
|
||||
#ifndef CS_SPARC_MODULE_H
|
||||
#define CS_SPARC_MODULE_H
|
||||
|
||||
#include "../../utils.h"
|
||||
|
||||
cs_err Sparc_global_init(cs_struct *ud);
|
||||
cs_err Sparc_option(cs_struct *handle, cs_opt_type type, size_t value);
|
||||
|
||||
#endif
|
|
@ -8,11 +8,11 @@
|
|||
#include "SystemZDisassembler.h"
|
||||
#include "SystemZInstPrinter.h"
|
||||
#include "SystemZMapping.h"
|
||||
#include "SystemZModule.h"
|
||||
|
||||
static cs_err init(cs_struct *ud)
|
||||
cs_err SystemZ_global_init(cs_struct *ud)
|
||||
{
|
||||
MCRegisterInfo *mri;
|
||||
|
||||
mri = cs_mem_malloc(sizeof(*mri));
|
||||
|
||||
SystemZ_init(mri);
|
||||
|
@ -30,21 +30,15 @@ static cs_err init(cs_struct *ud)
|
|||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
static cs_err option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
cs_err SystemZ_option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
{
|
||||
if (type == CS_OPT_SYNTAX)
|
||||
handle->syntax = (int) value;
|
||||
|
||||
// Do not set mode because only CS_MODE_BIG_ENDIAN is valid; we cannot
|
||||
// test for CS_MODE_LITTLE_ENDIAN because it is 0
|
||||
|
||||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
void SystemZ_enable(void)
|
||||
{
|
||||
cs_arch_init[CS_ARCH_SYSZ] = init;
|
||||
cs_arch_option[CS_ARCH_SYSZ] = option;
|
||||
|
||||
// support this arch
|
||||
all_arch |= (1 << CS_ARCH_SYSZ);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* Capstone Disassembly Engine */
|
||||
/* By Travis Finkenauer <tmfinken@gmail.com>, 2018 */
|
||||
|
||||
#ifndef CS_SYSTEMZ_MODULE_H
|
||||
#define CS_SYSTEMZ_MODULE_H
|
||||
|
||||
#include "../../utils.h"
|
||||
|
||||
cs_err SystemZ_global_init(cs_struct *ud);
|
||||
cs_err SystemZ_option(cs_struct *handle, cs_opt_type type, size_t value);
|
||||
|
||||
#endif
|
|
@ -8,8 +8,9 @@
|
|||
#include "TMS320C64xDisassembler.h"
|
||||
#include "TMS320C64xInstPrinter.h"
|
||||
#include "TMS320C64xMapping.h"
|
||||
#include "TMS320C64xModule.h"
|
||||
|
||||
static cs_err init(cs_struct *ud)
|
||||
cs_err TMS320C64x_global_init(cs_struct *ud)
|
||||
{
|
||||
MCRegisterInfo *mri;
|
||||
|
||||
|
@ -30,17 +31,9 @@ static cs_err init(cs_struct *ud)
|
|||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
static cs_err option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
cs_err TMS320C64x_option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
{
|
||||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
void TMS320C64x_enable(void)
|
||||
{
|
||||
cs_arch_init[CS_ARCH_TMS320C64X] = init;
|
||||
cs_arch_option[CS_ARCH_TMS320C64X] = option;
|
||||
|
||||
all_arch |= (1 << CS_ARCH_TMS320C64X);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* Capstone Disassembly Engine */
|
||||
/* By Travis Finkenauer <tmfinken@gmail.com>, 2018 */
|
||||
|
||||
#ifndef CS_TMS320C64X_MODULE_H
|
||||
#define CS_TMS320C64X_MODULE_H
|
||||
|
||||
#include "../../utils.h"
|
||||
|
||||
cs_err TMS320C64x_global_init(cs_struct *ud);
|
||||
cs_err TMS320C64x_option(cs_struct *handle, cs_opt_type type, size_t value);
|
||||
|
||||
#endif
|
|
@ -8,15 +8,11 @@
|
|||
#include "X86Disassembler.h"
|
||||
#include "X86InstPrinter.h"
|
||||
#include "X86Mapping.h"
|
||||
#include "X86Module.h"
|
||||
|
||||
static cs_err init(cs_struct *ud)
|
||||
cs_err X86_global_init(cs_struct *ud)
|
||||
{
|
||||
MCRegisterInfo *mri;
|
||||
|
||||
// verify if requested mode is valid
|
||||
if (ud->mode & ~(CS_MODE_LITTLE_ENDIAN | CS_MODE_32 | CS_MODE_64 | CS_MODE_16))
|
||||
return CS_ERR_MODE;
|
||||
|
||||
mri = cs_mem_malloc(sizeof(*mri));
|
||||
|
||||
X86_init(mri);
|
||||
|
@ -43,7 +39,7 @@ static cs_err init(cs_struct *ud)
|
|||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
static cs_err option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
cs_err X86_option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
{
|
||||
switch(type) {
|
||||
default:
|
||||
|
@ -95,13 +91,4 @@ static cs_err option(cs_struct *handle, cs_opt_type type, size_t value)
|
|||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
void X86_enable(void)
|
||||
{
|
||||
cs_arch_init[CS_ARCH_X86] = init;
|
||||
cs_arch_option[CS_ARCH_X86] = option;
|
||||
|
||||
// support this arch
|
||||
all_arch |= (1 << CS_ARCH_X86);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* Capstone Disassembly Engine */
|
||||
/* By Travis Finkenauer <tmfinken@gmail.com>, 2018 */
|
||||
|
||||
#ifndef CS_X86_MODULE_H
|
||||
#define CS_X86_MODULE_H
|
||||
|
||||
#include "../../utils.h"
|
||||
|
||||
cs_err X86_global_init(cs_struct *ud);
|
||||
cs_err X86_option(cs_struct *handle, cs_opt_type type, size_t value);
|
||||
|
||||
#endif
|
|
@ -8,11 +8,11 @@
|
|||
#include "XCoreDisassembler.h"
|
||||
#include "XCoreInstPrinter.h"
|
||||
#include "XCoreMapping.h"
|
||||
#include "XCoreModule.h"
|
||||
|
||||
static cs_err init(cs_struct *ud)
|
||||
cs_err XCore_global_init(cs_struct *ud)
|
||||
{
|
||||
MCRegisterInfo *mri;
|
||||
|
||||
mri = cs_mem_malloc(sizeof(*mri));
|
||||
|
||||
XCore_init(mri);
|
||||
|
@ -30,18 +30,12 @@ static cs_err init(cs_struct *ud)
|
|||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
static cs_err option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
cs_err XCore_option(cs_struct *handle, cs_opt_type type, size_t value)
|
||||
{
|
||||
// Do not set mode because only CS_MODE_BIG_ENDIAN is valid; we cannot
|
||||
// test for CS_MODE_LITTLE_ENDIAN because it is 0
|
||||
|
||||
return CS_ERR_OK;
|
||||
}
|
||||
|
||||
void XCore_enable(void)
|
||||
{
|
||||
cs_arch_init[CS_ARCH_XCORE] = init;
|
||||
cs_arch_option[CS_ARCH_XCORE] = option;
|
||||
|
||||
// support this arch
|
||||
all_arch |= (1 << CS_ARCH_XCORE);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
/* Capstone Disassembly Engine */
|
||||
/* By Travis Finkenauer <tmfinken@gmail.com>, 2018 */
|
||||
|
||||
#ifndef CS_XCORE_MODULE_H
|
||||
#define CS_XCORE_MODULE_H
|
||||
|
||||
#include "../../utils.h"
|
||||
|
||||
cs_err XCore_global_init(cs_struct *ud);
|
||||
cs_err XCore_option(cs_struct *handle, cs_opt_type type, size_t value);
|
||||
|
||||
#endif
|
305
cs.c
305
cs.c
|
@ -52,72 +52,259 @@
|
|||
#define SKIPDATA_MNEM NULL
|
||||
#endif
|
||||
|
||||
cs_err (*cs_arch_init[MAX_ARCH])(cs_struct *) = { NULL };
|
||||
cs_err (*cs_arch_option[MAX_ARCH]) (cs_struct *, cs_opt_type, size_t value) = { NULL };
|
||||
void (*cs_arch_destroy[MAX_ARCH]) (cs_struct *) = { NULL };
|
||||
|
||||
extern void ARM_enable(void);
|
||||
extern void AArch64_enable(void);
|
||||
extern void M680X_enable(void);
|
||||
extern void M68K_enable(void);
|
||||
extern void Mips_enable(void);
|
||||
extern void X86_enable(void);
|
||||
extern void PPC_enable(void);
|
||||
extern void Sparc_enable(void);
|
||||
extern void SystemZ_enable(void);
|
||||
extern void XCore_enable(void);
|
||||
extern void TMS320C64x_enable(void);
|
||||
extern void EVM_enable(void);
|
||||
|
||||
static void archs_enable(void)
|
||||
{
|
||||
static bool initialized = false;
|
||||
|
||||
if (initialized)
|
||||
return;
|
||||
#include "arch/AArch64/AArch64Module.h"
|
||||
#include "arch/ARM/ARMModule.h"
|
||||
#include "arch/EVM/EVMModule.h"
|
||||
#include "arch/M680X/M680XModule.h"
|
||||
#include "arch/M68K/M68KModule.h"
|
||||
#include "arch/Mips/MipsModule.h"
|
||||
#include "arch/PowerPC/PPCModule.h"
|
||||
#include "arch/Sparc/SparcModule.h"
|
||||
#include "arch/SystemZ/SystemZModule.h"
|
||||
#include "arch/TMS320C64x/TMS320C64xModule.h"
|
||||
#include "arch/X86/X86Module.h"
|
||||
#include "arch/XCore/XCoreModule.h"
|
||||
|
||||
// constructor initialization for all archs
|
||||
static cs_err (*cs_arch_init[MAX_ARCH])(cs_struct *) = {
|
||||
#ifdef CAPSTONE_HAS_ARM
|
||||
ARM_enable();
|
||||
ARM_global_init,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_ARM64
|
||||
AArch64_enable();
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_M680X
|
||||
M680X_enable();
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_M68K
|
||||
M68K_enable();
|
||||
AArch64_global_init,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_MIPS
|
||||
Mips_enable();
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_POWERPC
|
||||
PPC_enable();
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_SPARC
|
||||
Sparc_enable();
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_SYSZ
|
||||
SystemZ_enable();
|
||||
Mips_global_init,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_X86
|
||||
X86_enable();
|
||||
X86_global_init,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_POWERPC
|
||||
PPC_global_init,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_SPARC
|
||||
Sparc_global_init,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_SYSZ
|
||||
SystemZ_global_init,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_XCORE
|
||||
XCore_enable();
|
||||
XCore_global_init,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_M68K
|
||||
M68K_global_init,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_TMS320C64X
|
||||
TMS320C64x_enable();
|
||||
TMS320C64x_global_init,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_M680X
|
||||
M680X_global_init,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_EVM
|
||||
EVM_enable();
|
||||
EVM_global_init,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
// support cs_option() for all archs
|
||||
static cs_err (*cs_arch_option[MAX_ARCH]) (cs_struct *, cs_opt_type, size_t value) = {
|
||||
#ifdef CAPSTONE_HAS_ARM
|
||||
ARM_option,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_ARM64
|
||||
AArch64_option,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_MIPS
|
||||
Mips_option,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_X86
|
||||
X86_option,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_POWERPC
|
||||
PPC_option,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_SPARC
|
||||
Sparc_option,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_SYSZ
|
||||
SystemZ_option,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_XCORE
|
||||
XCore_option,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_M68K
|
||||
M68K_option,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_TMS320C64X
|
||||
TMS320C64x_option,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_M680X
|
||||
M680X_option,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_EVM
|
||||
EVM_option,
|
||||
#else
|
||||
NULL,
|
||||
#endif
|
||||
};
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
// bitmask for finding disallowed modes for an arch:
|
||||
// to be called in cs_open()/cs_option()
|
||||
static cs_mode cs_arch_disallowed_mode_mask[MAX_ARCH] = {
|
||||
#ifdef CAPSTONE_HAS_ARM
|
||||
~(CS_MODE_LITTLE_ENDIAN | CS_MODE_ARM | CS_MODE_V8 | CS_MODE_MCLASS
|
||||
| CS_MODE_THUMB | CS_MODE_BIG_ENDIAN),
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_ARM64
|
||||
~(CS_MODE_LITTLE_ENDIAN | CS_MODE_ARM | CS_MODE_BIG_ENDIAN),
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_MIPS
|
||||
~(CS_MODE_LITTLE_ENDIAN | CS_MODE_32 | CS_MODE_64 | CS_MODE_MICRO
|
||||
| CS_MODE_MIPS32R6 | CS_MODE_BIG_ENDIAN | CS_MODE_MIPS2 | CS_MODE_MIPS3),
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_X86
|
||||
~(CS_MODE_LITTLE_ENDIAN | CS_MODE_32 | CS_MODE_64 | CS_MODE_16),
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_POWERPC
|
||||
~(CS_MODE_LITTLE_ENDIAN | CS_MODE_32 | CS_MODE_64 | CS_MODE_BIG_ENDIAN
|
||||
| CS_MODE_QPX),
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_SPARC
|
||||
~(CS_MODE_BIG_ENDIAN | CS_MODE_V9),
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_SYSZ
|
||||
~(CS_MODE_BIG_ENDIAN),
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_XCORE
|
||||
~(CS_MODE_BIG_ENDIAN),
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_M68K
|
||||
~(CS_MODE_BIG_ENDIAN | CS_MODE_M68K_000 | CS_MODE_M68K_010 | CS_MODE_M68K_020
|
||||
| CS_MODE_M68K_030 | CS_MODE_M68K_040 | CS_MODE_M68K_060),
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_TMS320C64X
|
||||
~(CS_MODE_BIG_ENDIAN),
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_M680X
|
||||
~(CS_MODE_M680X_6301 | CS_MODE_M680X_6309 | CS_MODE_M680X_6800
|
||||
| CS_MODE_M680X_6801 | CS_MODE_M680X_6805 | CS_MODE_M680X_6808
|
||||
| CS_MODE_M680X_6809 | CS_MODE_M680X_6811 | CS_MODE_M680X_CPU12
|
||||
| CS_MODE_M680X_HCS08),
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_EVM
|
||||
0,
|
||||
#else
|
||||
0,
|
||||
#endif
|
||||
};
|
||||
|
||||
// bitmask of enabled architectures
|
||||
static uint32_t all_arch = 0
|
||||
#ifdef CAPSTONE_HAS_ARM
|
||||
| (1 << CS_ARCH_ARM)
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_ARM64
|
||||
| (1 << CS_ARCH_ARM64)
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_MIPS
|
||||
| (1 << CS_ARCH_MIPS)
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_X86
|
||||
| (1 << CS_ARCH_X86)
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_POWERPC
|
||||
| (1 << CS_ARCH_PPC)
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_SPARC
|
||||
| (1 << CS_ARCH_SPARC)
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_SYSZ
|
||||
| (1 << CS_ARCH_SYSZ)
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_XCORE
|
||||
| (1 << CS_ARCH_XCORE)
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_M68K
|
||||
| (1 << CS_ARCH_M68K)
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_TMS320C64X
|
||||
| (1 << CS_ARCH_TMS320C64X)
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_M680X
|
||||
| (1 << CS_ARCH_M680X)
|
||||
#endif
|
||||
#ifdef CAPSTONE_HAS_EVM
|
||||
| (1 << CS_ARCH_EVM)
|
||||
#endif
|
||||
;
|
||||
|
||||
unsigned int all_arch = 0;
|
||||
|
||||
#if defined(CAPSTONE_USE_SYS_DYN_MEM)
|
||||
#if !defined(CAPSTONE_HAS_OSXKERNEL) && !defined(_KERNEL_MODE)
|
||||
|
@ -171,8 +358,6 @@ cs_vsnprintf_t cs_vsnprintf = NULL;
|
|||
CAPSTONE_EXPORT
|
||||
unsigned int CAPSTONE_API cs_version(int *major, int *minor)
|
||||
{
|
||||
archs_enable();
|
||||
|
||||
if (major != NULL && minor != NULL) {
|
||||
*major = CS_API_MAJOR;
|
||||
*minor = CS_API_MINOR;
|
||||
|
@ -184,8 +369,6 @@ unsigned int CAPSTONE_API cs_version(int *major, int *minor)
|
|||
CAPSTONE_EXPORT
|
||||
bool CAPSTONE_API cs_support(int query)
|
||||
{
|
||||
archs_enable();
|
||||
|
||||
if (query == CS_ARCH_ALL)
|
||||
return all_arch == ((1 << CS_ARCH_ARM) | (1 << CS_ARCH_ARM64) |
|
||||
(1 << CS_ARCH_MIPS) | (1 << CS_ARCH_X86) |
|
||||
|
@ -278,9 +461,13 @@ cs_err CAPSTONE_API cs_open(cs_arch arch, cs_mode mode, csh *handle)
|
|||
// with cs_option(CS_OPT_MEM)
|
||||
return CS_ERR_MEMSETUP;
|
||||
|
||||
archs_enable();
|
||||
|
||||
if (arch < CS_ARCH_MAX && cs_arch_init[arch]) {
|
||||
// verify if requested mode is valid
|
||||
if (mode & cs_arch_disallowed_mode_mask[arch]) {
|
||||
*handle = 0;
|
||||
return CS_ERR_MODE;
|
||||
}
|
||||
|
||||
ud = cs_mem_calloc(1, sizeof(*ud));
|
||||
if (!ud) {
|
||||
// memory insufficient
|
||||
|
@ -290,7 +477,6 @@ cs_err CAPSTONE_API cs_open(cs_arch arch, cs_mode mode, csh *handle)
|
|||
ud->errnum = CS_ERR_OK;
|
||||
ud->arch = arch;
|
||||
ud->mode = mode;
|
||||
ud->big_endian = (mode & CS_MODE_BIG_ENDIAN) != 0;
|
||||
// by default, do not break instruction into details
|
||||
ud->detail = CS_OPT_OFF;
|
||||
|
||||
|
@ -464,8 +650,6 @@ cs_err CAPSTONE_API cs_option(csh ud, cs_opt_type type, size_t value)
|
|||
struct cs_struct *handle;
|
||||
cs_opt_mnem *opt;
|
||||
|
||||
archs_enable();
|
||||
|
||||
// cs_option() can be called with NULL handle just for CS_OPT_MEM
|
||||
// This is supposed to be executed before all other APIs (even cs_open())
|
||||
if (type == CS_OPT_MEM) {
|
||||
|
@ -566,6 +750,13 @@ cs_err CAPSTONE_API cs_option(csh ud, cs_opt_type type, size_t value)
|
|||
}
|
||||
}
|
||||
return CS_ERR_OK;
|
||||
|
||||
case CS_OPT_MODE:
|
||||
// verify if requested mode is valid
|
||||
if (value & cs_arch_disallowed_mode_mask[handle->arch]) {
|
||||
return CS_ERR_OPTION;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return cs_arch_option[handle->arch](handle, type, value);
|
||||
|
|
10
cs_priv.h
10
cs_priv.h
|
@ -55,7 +55,6 @@ struct cs_struct {
|
|||
void *printer_info; // aux info for printer
|
||||
Disasm_t disasm; // disassembler
|
||||
void *getinsn_info; // auxiliary info for printer
|
||||
bool big_endian;
|
||||
GetName_t reg_name;
|
||||
GetName_t insn_name;
|
||||
GetName_t group_name;
|
||||
|
@ -78,13 +77,8 @@ struct cs_struct {
|
|||
|
||||
#define MAX_ARCH CS_ARCH_MAX
|
||||
|
||||
// constructor initialization for all archs
|
||||
extern cs_err (*cs_arch_init[MAX_ARCH]) (cs_struct *);
|
||||
|
||||
// support cs_option() for all archs
|
||||
extern cs_err (*cs_arch_option[MAX_ARCH]) (cs_struct*, cs_opt_type, size_t value);
|
||||
|
||||
extern unsigned int all_arch;
|
||||
// Returns a bool (0 or 1) whether big endian is enabled for a mode
|
||||
#define MODE_IS_BIG_ENDIAN(mode) (((mode) & CS_MODE_BIG_ENDIAN) != 0)
|
||||
|
||||
extern cs_malloc_t cs_mem_malloc;
|
||||
extern cs_calloc_t cs_mem_calloc;
|
||||
|
|
Loading…
Reference in New Issue