Skip to content

Commit 7117e60

Browse files
committed
Add WIP unicorn emulator
It's kinda buggy, and even more buggy if you don't have certain local patches to unicorn. It's also hard to get to compile. Improvements to come.
1 parent 4e06ced commit 7117e60

7 files changed

Lines changed: 308 additions & 0 deletions

File tree

arch/ish/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ choice
2828
default ISH_EMULATOR_ISHEMU
2929
config ISH_EMULATOR_ISHEMU
3030
bool "Use iSH emulator"
31+
config ISH_EMULATOR_UNICORN
32+
bool "Use unicorn emulator"
3133
endchoice
3234

3335
config ARCH_KALLSYMS

arch/ish/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ export LIB_ISH_EMU = $(ISH_BUILD)/libish_emu.a
99

1010
ifdef CONFIG_ISH_EMULATOR_ISHEMU
1111
EMU_DIR = arch/ish/emu/ishemu
12+
else ifdef CONFIG_ISH_EMULATOR_UNICORN
13+
EMU_DIR = arch/ish/emu/unicorn
1214
endif
1315
export EMU_DIR
1416

arch/ish/emu/unicorn/Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
UNICORN_CFLAGS = -I$(ISH_SRC)/deps/unicorn/include
2+
3+
USER_CFLAGS += $(UNICORN_CFLAGS) -I$(srctree)/$(EMU_DIR)/include
4+
5+
obj-y = unicorn.o unicorn_user.o
6+
7+
include arch/um/scripts/Makefile.rules
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#ifndef __UNICORN_EMU_H
2+
#define __UNICORN_EMU_H
3+
4+
struct uc_struct;
5+
6+
struct emu {
7+
struct uc_struct *uc;
8+
struct pt_regs *regs;
9+
struct emu_mm *mm;
10+
unsigned long tls_ptr;
11+
unsigned long mm_change_count;
12+
};
13+
14+
struct emu_mm {
15+
unsigned long change_count;
16+
};
17+
18+
#endif

arch/ish/emu/unicorn/unicorn.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#include <linux/moduleparam.h>
2+
#include "unicorn.h"
3+
4+
bool unicorn_trace;
5+
module_param_named(trace, unicorn_trace, bool, 0);

arch/ish/emu/unicorn/unicorn.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifndef __UNICORN_UNICORN_H
2+
#define __UNICORN_UNICORN_H
3+
#if __KERNEL__
4+
#include <linux/types.h>
5+
#else
6+
#include <stdbool.h>
7+
#endif
8+
9+
extern bool unicorn_trace;
10+
11+
#endif
Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
#include <unicorn/unicorn.h>
2+
#include <asm/ptrace.h>
3+
#include <asm/page.h>
4+
5+
#include "unicorn.h"
6+
7+
#include <emu/exec.h>
8+
#include <emu/kernel.h>
9+
#include <emu/emu.h>
10+
11+
#define check(err) __check(err, __FUNCTION__, __LINE__)
12+
static void __check(uc_err err, const char *function, int line)
13+
{
14+
if (err != UC_ERR_OK) {
15+
panic("%s:%d: %s", function, line, uc_strerror(err));
16+
}
17+
}
18+
19+
#define uc_reg(op, uc, reg_id, field) \
20+
check(uc_reg_##op##2((uc), (reg_id), (field), (size_t[]){sizeof(*(field))}))
21+
22+
//////////////////////
23+
24+
struct gdt_desc {
25+
unsigned limit1:16;
26+
unsigned base1:16;
27+
unsigned base2:8;
28+
unsigned type:4;
29+
unsigned system:1;
30+
unsigned dpl:2;
31+
unsigned present:1;
32+
unsigned limit2:4;
33+
unsigned avl:1;
34+
unsigned _:1;
35+
unsigned db:1;
36+
unsigned granularity:1;
37+
unsigned base3:8;
38+
} __attribute__((packed));
39+
40+
static const uint32_t gdt_base = 0xfffff000;
41+
static const uint32_t gdt_size = 16 * sizeof(struct gdt_desc);
42+
43+
static void install_gdt_segment(uc_engine *uc, int segment, uint32_t base, int dpl)
44+
{
45+
struct gdt_desc desc = {
46+
.limit1 = 0xffff,
47+
.limit2 = 0xf,
48+
.base1 = (base & 0x0000ffff) >> 0,
49+
.base2 = (base & 0x00ff0000) >> 16,
50+
.base3 = (base & 0xff000000) >> 24,
51+
.type = 3, // read & write
52+
.system = 1, // user
53+
.dpl = dpl,
54+
.present = 1,
55+
.db = 1, // 32 bit code
56+
.granularity = 1,
57+
};
58+
check(uc_mem_write(uc, gdt_base + (segment * sizeof(desc)), &desc, sizeof(desc)));
59+
}
60+
61+
///////////////////
62+
63+
static void load_regs(struct emu *emu)
64+
{
65+
uc_engine *uc = emu->uc;
66+
struct pt_regs *regs = emu->regs;
67+
uc_reg(write, uc, UC_X86_REG_EAX, &regs->ax);
68+
uc_reg(write, uc, UC_X86_REG_EBX, &regs->bx);
69+
uc_reg(write, uc, UC_X86_REG_ECX, &regs->cx);
70+
uc_reg(write, uc, UC_X86_REG_EDX, &regs->dx);
71+
uc_reg(write, uc, UC_X86_REG_ESI, &regs->si);
72+
uc_reg(write, uc, UC_X86_REG_EDI, &regs->di);
73+
uc_reg(write, uc, UC_X86_REG_EBP, &regs->bp);
74+
uc_reg(write, uc, UC_X86_REG_ESP, &regs->sp);
75+
uc_reg(write, uc, UC_X86_REG_EIP, &regs->ip);
76+
uc_reg(write, uc, UC_X86_REG_FLAGS, &regs->flags);
77+
78+
if (emu->tls_ptr != regs->tls) {
79+
emu->tls_ptr = regs->tls;
80+
install_gdt_segment(uc, 0xc, emu->tls_ptr, 3);
81+
}
82+
unsigned long mm_change_count = __atomic_load_n(&emu->mm->change_count, __ATOMIC_SEQ_CST);
83+
if (emu->mm_change_count != mm_change_count) {
84+
check(uc_ctl_flush_tlb(uc));
85+
emu->mm_change_count = mm_change_count;
86+
}
87+
}
88+
89+
static void save_regs(struct emu *emu)
90+
{
91+
uc_engine *uc = emu->uc;
92+
struct pt_regs *regs = emu->regs;
93+
regs->ax = 0;
94+
uc_reg(read, uc, UC_X86_REG_EAX, &regs->ax);
95+
uc_reg(read, uc, UC_X86_REG_EBX, &regs->bx);
96+
uc_reg(read, uc, UC_X86_REG_ECX, &regs->cx);
97+
uc_reg(read, uc, UC_X86_REG_EDX, &regs->dx);
98+
uc_reg(read, uc, UC_X86_REG_ESI, &regs->si);
99+
uc_reg(read, uc, UC_X86_REG_EDI, &regs->di);
100+
uc_reg(read, uc, UC_X86_REG_EBP, &regs->bp);
101+
uc_reg(read, uc, UC_X86_REG_ESP, &regs->sp);
102+
uc_reg(read, uc, UC_X86_REG_EIP, &regs->ip);
103+
uc_reg(read, uc, UC_X86_REG_FLAGS, &regs->flags);
104+
}
105+
106+
static void do_trap(struct emu *emu, int trap_nr)
107+
{
108+
save_regs(emu);
109+
emu->regs->trap_nr = trap_nr;
110+
handle_cpu_trap();
111+
load_regs(emu);
112+
}
113+
114+
////////////////
115+
116+
static bool mem_type_is_write(uc_mem_type type)
117+
{
118+
switch (type) {
119+
case UC_MEM_READ:
120+
case UC_MEM_READ_UNMAPPED:
121+
case UC_MEM_READ_PROT:
122+
case UC_MEM_READ_AFTER:
123+
case UC_MEM_FETCH:
124+
case UC_MEM_FETCH_UNMAPPED:
125+
case UC_MEM_FETCH_PROT:
126+
return false;
127+
case UC_MEM_WRITE:
128+
case UC_MEM_WRITE_UNMAPPED:
129+
case UC_MEM_WRITE_PROT:
130+
return true;
131+
}
132+
}
133+
134+
static bool hook_tlb_fill(uc_engine *uc, uint64_t vaddr, uc_mem_type type, uc_tlb_entry *result, void *user_data)
135+
{
136+
struct emu *emu = user_data;
137+
138+
if (vaddr == gdt_base) {
139+
result->paddr = vaddr;
140+
result->perms = UC_PROT_READ | UC_PROT_WRITE;
141+
return true;
142+
}
143+
144+
bool is_write = mem_type_is_write(type);
145+
bool writable;
146+
void *kernel_addr = user_to_kernel_emu(emu->mm, vaddr, &writable);
147+
148+
if (kernel_addr == NULL || (is_write && !writable)) {
149+
emu->regs->cr2 = vaddr;
150+
emu->regs->error_code = mem_type_is_write(type) ? 2 : 0;
151+
do_trap(emu, 13);
152+
kernel_addr = user_to_kernel_emu(emu->mm, vaddr, &writable);
153+
}
154+
if (kernel_addr == NULL) {
155+
uc_emu_stop(uc);
156+
return false;
157+
}
158+
159+
unsigned long paddr = __pa(kernel_addr);
160+
result->paddr = __pa(kernel_addr);
161+
result->perms = UC_PROT_READ | UC_PROT_EXEC | (writable ? UC_PROT_WRITE : 0);
162+
return true;
163+
}
164+
165+
static void hook_intr(uc_engine *uc, uint32_t intno, void *user_data)
166+
{
167+
struct emu *emu = user_data;
168+
do_trap(emu, intno);
169+
}
170+
171+
static bool hook_trace_code(uc_engine *uc, uint64_t address, size_t size, void *user_data)
172+
{
173+
struct emu *emu = user_data;
174+
void *ptr = user_to_kernel_emu(emu->mm, address, NULL);
175+
uint32_t sp;
176+
uc_reg(read, uc, UC_X86_REG_ESP, &sp);
177+
extern int current_pid();
178+
printk("%d code %#llx+%ld %*ph\n", current_pid(), address, size, (int) size, ptr);
179+
return true;
180+
}
181+
182+
/////////////
183+
184+
static void create_unicorn(struct emu *emu)
185+
{
186+
uc_hook hh;
187+
check(uc_open(UC_ARCH_X86, UC_MODE_32, &emu->uc));
188+
189+
check(uc_mem_map_ptr(emu->uc, 0x0, ish_phys_size, UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, (void *) ish_phys_base));
190+
check(uc_ctl_tlb_mode(emu->uc, UC_TLB_VIRTUAL));
191+
check(uc_hook_add(emu->uc, &hh, UC_HOOK_TLB_FILL, hook_tlb_fill, emu, 1, 0));
192+
193+
check(uc_mem_map(emu->uc, gdt_base, 0x1000, UC_PROT_READ | UC_PROT_WRITE));
194+
struct uc_x86_mmr gdtr = {.base = gdt_base, .limit = gdt_size};
195+
uc_reg(write, emu->uc, UC_X86_REG_GDTR, &gdtr);
196+
// unicorn bug (maybe): if you load any segment register other than ss, sp suddenly becomes 16 bit. can be fixed by loading ss correctly
197+
install_gdt_segment(emu->uc, 1, 0, 0);
198+
int seg = (1 << 3) | 0; // ring 0? why?
199+
uc_reg(write, emu->uc, UC_X86_REG_SS, &seg);
200+
201+
check(uc_hook_add(emu->uc, &hh, UC_HOOK_INTR, hook_intr, emu, 1, 0));
202+
203+
if (unicorn_trace) {
204+
check(uc_hook_add(emu->uc, &hh, UC_HOOK_BLOCK, hook_trace_code, emu, 1, 0));
205+
}
206+
}
207+
208+
void emu_run(struct emu *emu, struct pt_regs *regs)
209+
{
210+
if (!emu->uc) {
211+
create_unicorn(emu);
212+
}
213+
214+
emu->regs = regs;
215+
216+
load_regs(emu);
217+
218+
for (;;) {
219+
check(uc_emu_start(emu->uc, regs->ip, 0, 0, 0));
220+
}
221+
}
222+
223+
void emu_finish_fork(struct emu *emu, struct emu *next)
224+
{
225+
if (emu->uc == NULL) {
226+
return;
227+
}
228+
229+
create_unicorn(next);
230+
231+
uint8_t gdt_buf[gdt_size];
232+
check(uc_mem_read(emu->uc, gdt_base, gdt_buf, gdt_size));
233+
check(uc_mem_write(next->uc, gdt_base, gdt_buf, gdt_size));
234+
235+
uc_context *ctx;
236+
check(uc_context_alloc(emu->uc, &ctx));
237+
check(uc_context_save(emu->uc, ctx));
238+
check(uc_context_restore(next->uc, ctx));
239+
check(uc_context_free(ctx));
240+
}
241+
242+
void emu_destroy(struct emu *emu)
243+
{
244+
check(uc_close(emu->uc));
245+
emu->uc = NULL;
246+
}
247+
248+
void emu_poke_cpu(int cpu) {}
249+
250+
void emu_mmu_init(struct emu_mm *mm)
251+
{
252+
}
253+
void emu_mmu_destroy(struct emu_mm *mm)
254+
{
255+
}
256+
void emu_switch_mm(struct emu *emu, struct emu_mm *mm)
257+
{
258+
emu->mm = mm;
259+
}
260+
void emu_flush_tlb_local(struct emu_mm *mm, unsigned long start, unsigned long end)
261+
{
262+
__atomic_fetch_add(&mm->change_count, 1, __ATOMIC_SEQ_CST);
263+
}

0 commit comments

Comments
 (0)