parsing some special arm instructions
This commit is contained in:
parent
04979b0afd
commit
d9024990f9
168
macho-go/pkg/ios/macho/arm.c
Normal file
168
macho-go/pkg/ios/macho/arm.c
Normal file
@ -0,0 +1,168 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#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;
|
||||
}
|
7
macho-go/pkg/ios/macho/arm.h
Normal file
7
macho-go/pkg/ios/macho/arm.h
Normal file
@ -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);
|
Loading…
Reference in New Issue
Block a user