diff --git a/macho-go/pkg/ios/macho/arm.c b/macho-go/pkg/ios/macho/arm.c new file mode 100644 index 0000000..bd59c0e --- /dev/null +++ b/macho-go/pkg/ios/macho/arm.c @@ -0,0 +1,168 @@ +#include +#include + +#define true 1 +#define false 0 + + +// this file encodes and decodes arm instructions +// only support for ARMv8 or (aarch64) because +// it is the default ISA in Apple devices +// https://yurichev.com/mirrors/ARMv8-A_Architecture_Reference_Manual_(Issue_A.a).pdf + +// using bit field in little endian is crazy +// but clang does not support switching endianess (fuck u llvm) +// gcc does, but default on Apple compiler is clang +// TODO: default to using big endian for bit field parsing + +// this file assumes everything is LITTLE ENDIAN +// the struct is assigned as bit field, and parsed using get_bits +// to encode the instruction, can use the bit field and load each bits +// through shifting, by default it has 0 padded so should work + +// mask to delete last 12 bits +uint32_t ZEROS_12_LOWER = ~0xFFF; + +uint32_t get_bits(uint32_t value, uint32_t from, uint32_t to) { + // should assert compiler error + if (to < from) return false; + if (from == to) { + // bit at position is set + return (value & (1 << from)) != 0; + } + + int32_t mask = ~(1U << (to - from + 1)); + return (value & mask) >> from; +} + +int is_bit_set(uint32_t value, uint32_t at) { + return (value & (1 << at)) != 0; +} + +struct add { + uint32_t sf : 1; + uint32_t op : 1; + uint32_t s : 1; + uint32_t sig : 5; + uint32_t imm : 12; + uint32_t rn : 5; + uint32_t rd : 5; +}; + +struct add to_add(uint32_t inst) { + struct add parsed; + parsed.sf = is_bit_set(inst, 31); + parsed.op = is_bit_set(inst, 30); + parsed.s = is_bit_set(inst, 29); + parsed.sig = get_bits(inst, 24, 28); + parsed.imm = get_bits(inst, 10, 21); + parsed.rn = get_bits(inst, 5, 9); + parsed.rd = get_bits(inst, 0, 4); + return parsed; +} + +void from_add(struct add parsed, uint32_t *inst) { + *inst = 0; + *inst |= parsed.sf << 31; + *inst |= parsed.op << 30; + *inst |= parsed.s << 29; + *inst |= parsed.sig << 24; + *inst |= parsed.imm << 10; + *inst |= parsed.rn << 5; + *inst |= parsed.rd; +} + +int add_imm_set(uint32_t *inst, uint32_t offset) { + struct add parsed = to_add(*inst); + if (parsed.op != 0 || parsed.sig != 0b10001) { + return false; + } + parsed.imm = offset; // auto truncate? + from_add(parsed, inst); + return true; +} + +uint32_t add_imm_get(uint32_t inst) { + struct add parsed = to_add(inst); + return parsed.imm; +} + +int is_add(uint32_t inst) { + struct add parsed = to_add(inst); + if (parsed.op != 0 || parsed.sig != 0b10001) { + return false; + } + return true; +} + +struct adrp { + uint32_t op : 1; + uint32_t immlo : 2; + uint32_t sig : 5; + uint32_t immhi : 19; + uint32_t rd : 5; +}; + +struct adrp to_adrp(uint32_t inst) { + struct adrp parsed; + parsed.op = is_bit_set(inst, 31); + parsed.immlo = get_bits(inst, 29, 30); + parsed.sig = get_bits(inst, 24, 28); + parsed.immhi = get_bits(inst, 5, 23); + parsed.rd = get_bits(inst, 0, 4); + return parsed; +} + +void from_adrp(struct adrp parsed, uint32_t *inst) { + *inst = 0; + *inst |= parsed.op << 31; + *inst |= parsed.immlo << 29; + *inst |= parsed.sig << 24; + *inst |= parsed.immhi << 5; + *inst |= parsed.rd; +} + +int is_adrp(uint32_t inst) { + struct adrp parsed = to_adrp(inst); + if (parsed.op != 1 || parsed.sig != 0b10000) { + return false; + } + return true; +} + +// change adrp imm to something else +int adrp_imm_set(uint32_t *inst, uint32_t offset) { + struct adrp parsed = to_adrp(*inst); + if (parsed.op != 1 || parsed.sig != 0b10000) { + return false; + } + + // uint32_t imm = 0; + + // imm = parsed.immhi << 14; + // imm |= parsed.immlo << 12; + // printf("old adrp is %x\n", imm); + // printf(" immlo %x\n", parsed.immlo); + // printf(" immhi %x\n", parsed.immhi); + + // adrp: register = (base masked lower 12-bit) + imm + parsed.immlo = get_bits(offset >> 12, 0, 1); + parsed.immhi = offset >> 14; + + // imm = parsed.immhi << 14; + // imm |= parsed.immlo << 12; + // printf("new adrp is %x\n", imm); + // printf(" immlo %x\n", parsed.immlo); + // printf(" immhi %x\n", parsed.immhi); + + from_adrp(parsed, inst); + return true; +} + +uint32_t adrp_imm_get(uint32_t inst) { + struct adrp parsed = to_adrp(inst); + uint32_t imm = 0; + imm = parsed.immhi << 14; + imm |= parsed.immlo << 12; + return imm; +} diff --git a/macho-go/pkg/ios/macho/arm.h b/macho-go/pkg/ios/macho/arm.h new file mode 100644 index 0000000..e66ef11 --- /dev/null +++ b/macho-go/pkg/ios/macho/arm.h @@ -0,0 +1,7 @@ +int is_add(uint32_t inst); +int add_imm_set(uint32_t *inst, uint32_t offset); +uint32_t add_imm_get(uint32_t inst); + +int is_adrp(uint32_t inst); +int adrp_imm_set(uint32_t *inst, uint32_t offset); +uint32_t adrp_imm_get(uint32_t inst);