unfinished stuff, also zynq
This commit is contained in:
parent
1b68909633
commit
852c438b10
|
@ -4,3 +4,4 @@
|
||||||
*.elf
|
*.elf
|
||||||
*.map
|
*.map
|
||||||
notes
|
notes
|
||||||
|
*.html
|
||||||
|
|
8
Makefile
8
Makefile
|
@ -8,7 +8,7 @@ CC = $(PREFIX)gcc
|
||||||
GDB ?= $(PREFIX)gdb
|
GDB ?= $(PREFIX)gdb
|
||||||
|
|
||||||
CGENFLAGS = -mcpu=arm926ej-s -mthumb-interwork -fno-pie
|
CGENFLAGS = -mcpu=arm926ej-s -mthumb-interwork -fno-pie
|
||||||
WARN = -Wall -Wextra -Werror
|
WARN = -Wall -Wextra -Werror=return-type -Werror=implicit-function-declaration
|
||||||
OPTIMIZE = -g -Og
|
OPTIMIZE = -g -Og
|
||||||
INCLUDE = -I.
|
INCLUDE = -I.
|
||||||
GENDEP = -MMD -MP
|
GENDEP = -MMD -MP
|
||||||
|
@ -41,7 +41,11 @@ openocd-load: jazelle.elf
|
||||||
|
|
||||||
gdb:
|
gdb:
|
||||||
$(GDB) -ex 'target extended-remote $(OPENOCD_HOST):$(OPENOCD_PORT_GDB)' \
|
$(GDB) -ex 'target extended-remote $(OPENOCD_HOST):$(OPENOCD_PORT_GDB)' \
|
||||||
-ex 'set $$pc=_start' -ex 'b jazelle_exec' -ex c jazelle.elf
|
-ex 'set $$pc=_start' \
|
||||||
|
$(GDB_EXTRA_CMDS) \
|
||||||
|
-ex c jazelle.elf
|
||||||
|
|
||||||
|
# -ex 'b jazelle_exec' \
|
||||||
|
|
||||||
-include $(OBJS:.o=.d)
|
-include $(OBJS:.o=.d)
|
||||||
|
|
||||||
|
|
290
jazelle.c
290
jazelle.c
|
@ -3,21 +3,35 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
// known ID table:
|
// known ID table:
|
||||||
// Chip name | ARM core | Jazelle ID
|
// Chip name | ARM core | ARM CPUID | JTAG IDCODE | Jazelle ID
|
||||||
// ------------+------------+-----------
|
// ------------+------------+------------+-------------+----------- Jazelle DBX:
|
||||||
// ??? | ARM7EJ-S | ???
|
// ??? | ARM7EJ-S | ??? | ??? | ???
|
||||||
// Cypress FX3 | ARM926EJ-S | 0x64100004
|
// Cypress FX3 | ARM926EJ-S | 0x41069265 | 0x07926069 | 0x64100004
|
||||||
// TI Nspire | ARM926EJ-S?| 0x64100004?
|
// TI Nspire | ARM926EJ-S?| 0x41069265?| ??? | 0x64100004?
|
||||||
// RPi v1.?? | ARM11??? | ???
|
// RPi v1.?? | ARM11??? | ??? | ??? | ???
|
||||||
// Ninty ?3DS | ARM11MPCore| 0x74100064?
|
// Ninty ?3DS | ARM11MPCore| 0x410FB025?| ??? | 0x74100064?
|
||||||
|
// Xilinx Zynq7| Cortex-A9MP| 0x413FC090 | 0x4BA00477 | 0xF4100168
|
||||||
|
// ------------+------------+------------+-------------+----------- Jazelle RCT:
|
||||||
|
// Xilinx Zynq7| Cortex-A9MP| 0x413FC090 | 0x4BA00477 | 0xF4100168
|
||||||
|
//
|
||||||
|
// TODO: others (BeagleBoard Cortex-A8? Other A9s?)
|
||||||
|
|
||||||
// TODO: immediate next steps:
|
// TODO: immediate next steps:
|
||||||
// * check if we can override jazelle insn execution with custom handlers
|
// * check if we can override jazelle insn execution with custom handlers
|
||||||
// * check what the control registers actually do
|
// * check what the control registers actually do
|
||||||
// * enumerate what all insns do
|
// * enumerate what all insns do
|
||||||
|
|
||||||
|
__attribute__((__naked__))
|
||||||
|
static uint32_t arm_get_id(void) {
|
||||||
|
asm volatile(
|
||||||
|
"mrc p15, 0, r0, c0, c0, 0\n"
|
||||||
|
"bx lr\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
__attribute__((__naked__))
|
__attribute__((__naked__))
|
||||||
static uint32_t jazelle_get_id(void) {
|
static uint32_t jazelle_get_id(void) {
|
||||||
asm volatile(
|
asm volatile(
|
||||||
|
@ -26,6 +40,13 @@ static uint32_t jazelle_get_id(void) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__attribute__((__aligned__(1024)))
|
||||||
|
static struct {
|
||||||
|
void* handlers[512];
|
||||||
|
uint8_t stack[256];
|
||||||
|
uint8_t locals[256];
|
||||||
|
} jazelle_block;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* c0: Jazelle Identity register (read-only)
|
* c0: Jazelle Identity register (read-only)
|
||||||
Bits 0-11: Subarchitecture-defined bits (reads as 4, meaning unknown)
|
Bits 0-11: Subarchitecture-defined bits (reads as 4, meaning unknown)
|
||||||
|
@ -49,24 +70,60 @@ static uint32_t jazelle_get_id(void) {
|
||||||
Bits 20-21: Disable array instructions if set?
|
Bits 20-21: Disable array instructions if set?
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// https://github.com/SonoSooS/libjz/wiki/Java-instruction-set
|
static uint32_t jazelle_exit_save;
|
||||||
static uint8_t bytecode[] = {
|
|
||||||
0x06, // iconst_3
|
__attribute__((__naked__))
|
||||||
0x07, // iconst_4
|
static int jazelle_exec_native(const void* bytecode, const void* block) {
|
||||||
0x05, // iconst_2
|
// inline asm parameters seems to be borking in GCC sooooo lets do it this way
|
||||||
0x6C, // idiv
|
(void)bytecode; (void)block; (void)&jazelle_exit_save;
|
||||||
0x04, // iconst_1
|
asm volatile(
|
||||||
0x60, // iadd
|
"push {r4-r12,lr}\n"
|
||||||
0x60, // iadd
|
|
||||||
0xAC, // ireturn
|
"mov lr, r0\n"
|
||||||
};
|
|
||||||
|
// init handler table pointer and stack pointer
|
||||||
|
"mov r5, r1\n"
|
||||||
|
"add r6, r5, #0x800\n"
|
||||||
|
"add r7, r5, #0x900\n"
|
||||||
|
// "r8: Pointer to constant pool? (haven't checked this yet)" -Hackspire
|
||||||
|
|
||||||
|
// set configuration valid & jazelle enable bits
|
||||||
|
"mov r0, #2\n"
|
||||||
|
"mcr p14, 7, r0, c1, c0, 0\n"
|
||||||
|
"mov r0, #1\n"
|
||||||
|
"mcr p14, 7, r0, c2, c0, 0\n"
|
||||||
|
|
||||||
|
// apparently there's no good way to find the exit point from jazelle,
|
||||||
|
// so we're going to hack that into the stuff now
|
||||||
|
"ldr r12, =jazelle_exit_save\n"
|
||||||
|
"adr r0, .Ljazend\n"
|
||||||
|
"str r0, [r12]\n"
|
||||||
|
//"bkpt #0\n"
|
||||||
|
|
||||||
|
// switch to jazelle mode
|
||||||
|
"adr r12, .Lno_jazelle\n"
|
||||||
|
"bxj r12\n"
|
||||||
|
|
||||||
|
".Ljazend:\n"
|
||||||
|
"mov r0, #0\n"
|
||||||
|
"b .Lend\n"
|
||||||
|
|
||||||
|
".Lno_jazelle:\n"
|
||||||
|
"mov r0, #1\n"
|
||||||
|
|
||||||
|
".Lend:\n"
|
||||||
|
"mov r5, #0\n"
|
||||||
|
"mcr p14, 7, %r5, c1, c0, 0\n"
|
||||||
|
"mcr p14, 7, %r5, c2, c0, 0\n"
|
||||||
|
|
||||||
|
"pop {r4-r12,lr}\n"
|
||||||
|
"bx lr\n"
|
||||||
|
|
||||||
|
".pool\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
__attribute__((__aligned__(1024)))
|
|
||||||
static struct {
|
|
||||||
void* handlers[512];
|
|
||||||
uint8_t stack[256];
|
|
||||||
uint8_t locals[256];
|
|
||||||
} jazelle_block;
|
|
||||||
|
|
||||||
__attribute__((__naked__))
|
__attribute__((__naked__))
|
||||||
static void handler_idiv(void) {
|
static void handler_idiv(void) {
|
||||||
|
@ -95,6 +152,7 @@ static void handler_idiv(void) {
|
||||||
// reads instead!
|
// reads instead!
|
||||||
|
|
||||||
asm volatile(
|
asm volatile(
|
||||||
|
//"bkpt #3\n"
|
||||||
// FIXME: read out stack contents in a better way
|
// FIXME: read out stack contents in a better way
|
||||||
"add r1, r2\n"
|
"add r1, r2\n"
|
||||||
"str r1, [r6,#-8]\n"
|
"str r1, [r6,#-8]\n"
|
||||||
|
@ -107,16 +165,15 @@ static void handler_idiv(void) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t jazelle_exit_save;
|
|
||||||
|
|
||||||
__attribute__((__naked__))
|
__attribute__((__naked__))
|
||||||
static void handler_ireturn(void) {
|
static void handler_ireturn(void) {
|
||||||
int result;
|
int result;
|
||||||
asm volatile(
|
asm volatile(
|
||||||
|
//"bkpt #4\n"
|
||||||
"ldr %[res], [r6, #-4]!\n"
|
"ldr %[res], [r6, #-4]!\n"
|
||||||
:[res]"=r"(result)
|
:[res]"=r"(result)
|
||||||
);
|
);
|
||||||
printf("result=%d\r\n", result); // FIXME: save & restore r0-r3 if ret implemented properly
|
iprintf("result=%d\r\n", result); // FIXME: save & restore r0-r3 if ret implemented properly
|
||||||
|
|
||||||
// get back to original code
|
// get back to original code
|
||||||
// TODO: later stage: get back to previous bytecode stuff
|
// TODO: later stage: get back to previous bytecode stuff
|
||||||
|
@ -130,56 +187,6 @@ static void handler_ireturn(void) {
|
||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((__naked__))
|
|
||||||
static int jazelle_exec_native(const void* bytecode, const void* block) {
|
|
||||||
// inline asm parameters seems to be borking in GCC sooooo lets do it this way
|
|
||||||
(void)bytecode; (void)block; (void)&jazelle_exit_save;
|
|
||||||
asm volatile(
|
|
||||||
"push {r5-r7,lr}\n"
|
|
||||||
|
|
||||||
"mov lr, r0\n"
|
|
||||||
|
|
||||||
// init handler table pointer and stack pointer
|
|
||||||
"mov r5, r1\n"
|
|
||||||
"add r6, r5, #0x800\n"
|
|
||||||
"add r7, r5, #0x900\n"
|
|
||||||
// "r8: Pointer to constant pool? (haven't checked this yet)" -Hackspire
|
|
||||||
|
|
||||||
// set configuration valid & jazelle enable bits
|
|
||||||
"mov r0, #2\n"
|
|
||||||
"mcr p14, 7, r0, c1, c0, 0\n"
|
|
||||||
"mov r0, #1\n"
|
|
||||||
"mcr p14, 7, r0, c2, c0, 0\n"
|
|
||||||
|
|
||||||
// apparently there's no good way to find the exit point from jazelle,
|
|
||||||
// so we're going to hack that into the stuff now
|
|
||||||
"ldr r12, =jazelle_exit_save\n"
|
|
||||||
"adr r0, .Ljazend\n"
|
|
||||||
"str r0, [r12]\n"
|
|
||||||
|
|
||||||
// switch to jazelle mode
|
|
||||||
"adr r12, .Lno_jazelle\n"
|
|
||||||
"bxj r12\n"
|
|
||||||
|
|
||||||
".Ljazend:\n"
|
|
||||||
"mov r0, #0\n"
|
|
||||||
"b .Lend\n"
|
|
||||||
|
|
||||||
".Lno_jazelle:\n"
|
|
||||||
"mov r0, #1\n"
|
|
||||||
|
|
||||||
".Lend:\n"
|
|
||||||
"mov r5, #0\n"
|
|
||||||
"mcr p14, 7, %r5, c1, c0, 0\n"
|
|
||||||
"mcr p14, 7, %r5, c2, c0, 0\n"
|
|
||||||
|
|
||||||
"pop {r5-r7,lr}\n"
|
|
||||||
"bx lr\n"
|
|
||||||
|
|
||||||
".pool\n"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int jazelle_exec(const uint8_t* bytecode) {
|
static int jazelle_exec(const uint8_t* bytecode) {
|
||||||
jazelle_block.handlers[0x6C] = handler_idiv;
|
jazelle_block.handlers[0x6C] = handler_idiv;
|
||||||
jazelle_block.handlers[0xAC] = handler_ireturn;
|
jazelle_block.handlers[0xAC] = handler_ireturn;
|
||||||
|
@ -199,12 +206,131 @@ static int jazelle_exec(const uint8_t* bytecode) {
|
||||||
return jazelle_exec_native(bytecode, block);
|
return jazelle_exec_native(bytecode, block);
|
||||||
}
|
}
|
||||||
|
|
||||||
void jazelle_main(void) {
|
|
||||||
uint32_t id = jazelle_get_id();
|
|
||||||
printf("hello world! jazelle ID=0x%lx\r\n", id);
|
|
||||||
|
|
||||||
int r = jazelle_exec(bytecode);
|
|
||||||
|
|
||||||
printf("retcode=%d\r\n", r);
|
|
||||||
|
|
||||||
|
|
||||||
|
static int was_exec = -1;
|
||||||
|
|
||||||
|
__attribute__((__naked__))
|
||||||
|
static void handler_wasexec() {
|
||||||
|
asm volatile(
|
||||||
|
//"bkpt #1\n"
|
||||||
|
// set was_exec flag
|
||||||
|
"mov r0, #1\n"
|
||||||
|
"str r0, %[we]\n"
|
||||||
|
// get back to original stuff
|
||||||
|
"ldr r12, %[exsav]\n"
|
||||||
|
"bx r12"
|
||||||
|
:
|
||||||
|
:[exsav]"m"(jazelle_exit_save)
|
||||||
|
,[we]"m"(was_exec)
|
||||||
|
:"r0","r12"
|
||||||
|
);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((__naked__))
|
||||||
|
static void handler_noexec() {
|
||||||
|
asm volatile(
|
||||||
|
//"bkpt #2\n"
|
||||||
|
// clear was_exec flag
|
||||||
|
//"mov r0, #0\n"
|
||||||
|
//"str r0, %[we]\n"
|
||||||
|
// get back to original stuff
|
||||||
|
"ldr r12, %[exsav]\n"
|
||||||
|
"bx r12"
|
||||||
|
:
|
||||||
|
:[exsav]"m"(jazelle_exit_save)
|
||||||
|
//,[we]"m"(was_exec)
|
||||||
|
:"r0","r12"
|
||||||
|
);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t bytecode_testh[] = {
|
||||||
|
0x06,
|
||||||
|
0x07,
|
||||||
|
0x05,
|
||||||
|
0x6C,
|
||||||
|
0x00, 0x00, 0x00, 0x00, // up to 4 nops of argument bytes eg for invokeXYZ
|
||||||
|
0xBA,
|
||||||
|
0x00, 0x00, 0x00, 0x00, // up to 4 nops of argument bytes eg for invokeXYZ
|
||||||
|
|
||||||
|
/*0x06, 0x06, 0x06, 0x06, // pre-fill the stack a bit
|
||||||
|
0x00, // target instruction to test
|
||||||
|
0x00, 0x00, 0x00, 0x00, // up to 4 nops of argument bytes eg for invokeXYZ
|
||||||
|
0xba, // invokedynamic, this one is complex enough that it'll reliably
|
||||||
|
// never be implemented in hardware*/
|
||||||
|
};
|
||||||
|
|
||||||
|
static void jazelle_test_handlers(uint8_t hflags[256/8]) {
|
||||||
|
memset(hflags, 0, 256/8);
|
||||||
|
for (int i = 0x6C; i < 0x100; ++i) {
|
||||||
|
if (i == 0xba) {
|
||||||
|
hflags[i>>3]|=(1<<(i&7));
|
||||||
|
continue; // yeah
|
||||||
|
}
|
||||||
|
|
||||||
|
//iprintf("bc 0x%02x\r\n", i);
|
||||||
|
bytecode_testh[3] = i;
|
||||||
|
//memset(&jazelle_block, 0, sizeof jazelle_block);
|
||||||
|
jazelle_block.handlers[i] = handler_wasexec;
|
||||||
|
jazelle_block.handlers[0xba] = handler_noexec;
|
||||||
|
|
||||||
|
was_exec = 0;
|
||||||
|
jazelle_exec_native(bytecode_testh, &jazelle_block);
|
||||||
|
|
||||||
|
if (was_exec == 1) {
|
||||||
|
hflags[i>>3]|=(1<<(i&7));
|
||||||
|
iprintf("bytecode 0x%02x: uses handler\r\n", i);
|
||||||
|
} else if (was_exec != 0) { // unchanged
|
||||||
|
iprintf("bytecode 0x%02x: wut?\r\n", i);
|
||||||
|
} else {
|
||||||
|
//iprintf("bytecode 0x%02x: hw\r\n", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
jazelle_block.handlers[i] = NULL;
|
||||||
|
//asm volatile("bkpt #7\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/SonoSooS/libjz/wiki/Java-instruction-set
|
||||||
|
static uint8_t bytecode_test1[] = {
|
||||||
|
0x06, // iconst_3
|
||||||
|
0x07, // iconst_4
|
||||||
|
0x05, // iconst_2
|
||||||
|
0x6C, // idiv
|
||||||
|
0x04, // iconst_1
|
||||||
|
0x60, // iadd
|
||||||
|
0x60, // iadd
|
||||||
|
0xAC, // ireturn
|
||||||
|
};
|
||||||
|
|
||||||
|
void jazelle_main(void) {
|
||||||
|
uint32_t aid = arm_get_id();
|
||||||
|
uint32_t jid = jazelle_get_id();
|
||||||
|
iprintf("hello world! ARM coreID=0x%lx jazelle ID=0x%lx\r\n", aid, jid);
|
||||||
|
while (jid == 0) ;
|
||||||
|
|
||||||
|
int r = jazelle_exec(bytecode_test1);
|
||||||
|
iprintf("retcode=%d\r\n", r);
|
||||||
|
|
||||||
|
if (r == 0) {
|
||||||
|
//r = jazelle_exec(bytecode_test1);
|
||||||
|
//iprintf("retcode=%d\r\n", r);
|
||||||
|
|
||||||
|
uint8_t hflags[256/8];
|
||||||
|
jazelle_test_handlers(hflags);
|
||||||
|
|
||||||
|
for (int i = 0; i < 0x100; ++i) {
|
||||||
|
if (hflags[i>>3]&(1<<(i&7))) {
|
||||||
|
iprintf("bytecode 0x%02x: uses handler\r\n", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
*.elf
|
||||||
|
*.o
|
||||||
|
__pycache__/
|
|
@ -0,0 +1,38 @@
|
||||||
|
|
||||||
|
PREFIX ?= arm-none-eabi-
|
||||||
|
CC = $(PREFIX)gcc
|
||||||
|
CFLAGS = -mcpu=cortex-a9
|
||||||
|
LDFLAGS = -T zynq.ld -nostartfiles -nostdlib
|
||||||
|
GDB ?= $(PREFIX)gdb
|
||||||
|
PYTHON3 ?= python3
|
||||||
|
NC ?= nc
|
||||||
|
|
||||||
|
OPENOCD_HOST ?= 172.16.10.2
|
||||||
|
OPENOCD_PORT_GDB ?= 3333
|
||||||
|
OPENOCD_PORT_TELNET ?= 4444
|
||||||
|
|
||||||
|
default: all
|
||||||
|
|
||||||
|
all: zynq.elf
|
||||||
|
|
||||||
|
zynq.o: zynq.c
|
||||||
|
$(CC) $(CFLAGS) -c -o "$@" "$<"
|
||||||
|
|
||||||
|
zynq.elf: zynq.o
|
||||||
|
$(CC) $(CFLAGS) $(LDFLAGS) -o "$@" $^
|
||||||
|
|
||||||
|
openocd-load: zynq.elf
|
||||||
|
{ $(PYTHON3) ./elf2oocd.py "$<"; echo exit; } | $(NC) $(OPENOCD_HOST) $(OPENOCD_PORT_TELNET)
|
||||||
|
# printf 'halt\nload_image zynq.elf\nexit\n' \
|
||||||
|
# | nc $(OPENOCD_HOST) $(OPENOCD_PORT_TELNET)
|
||||||
|
|
||||||
|
gdb: zynq.elf
|
||||||
|
$(GDB) -ex 'target extended-remote $(OPENOCD_HOST):$(OPENOCD_PORT_GDB)' \
|
||||||
|
-ex 'set $$pc=_start' \
|
||||||
|
$(GDB_EXTRA_CMDS) \
|
||||||
|
zynq.elf
|
||||||
|
|
||||||
|
clean:
|
||||||
|
@$(RM) -v zynq.o zynq.elf
|
||||||
|
|
||||||
|
.PHONY: gdb openocd-load all default clean
|
|
@ -0,0 +1,49 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import struct, sys
|
||||||
|
|
||||||
|
import hackyelf
|
||||||
|
|
||||||
|
if len(sys.argv) == 1:
|
||||||
|
printf("Usage: %s <input.elf> > <output.cfg>" % sys.argv[0], file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
elfblob = None
|
||||||
|
with open(sys.argv[1], 'rb') as f:
|
||||||
|
elfblob = f.read()
|
||||||
|
|
||||||
|
elf = hackyelf.parse(elfblob)
|
||||||
|
|
||||||
|
print("""
|
||||||
|
|
||||||
|
# TODO: reset halt maybe?
|
||||||
|
halt
|
||||||
|
|
||||||
|
""")
|
||||||
|
|
||||||
|
endian = '<' if elf.ident[5] == 1 else '>'
|
||||||
|
for phdr in elf.phdrs:
|
||||||
|
lenbytes = min(phdr.filesz, phdr.memsz)
|
||||||
|
zerobytes = phdr.memsz - lenbytes
|
||||||
|
|
||||||
|
baseaddr = phdr.paddr if phdr.paddr else phdr.vaddr
|
||||||
|
# text/data
|
||||||
|
lenm4 = lenbytes - (lenbytes & 3)
|
||||||
|
data = struct.unpack('%c%dI' % (endian, lenm4>>2), elfblob[phdr.off:(phdr.off+lenm4)])
|
||||||
|
for i in range(0, lenbytes, 4):
|
||||||
|
print("mww 0x%08x 0x%08x" % ((baseaddr + i), data[i>>2]))
|
||||||
|
if lenm4 != lenbytes:
|
||||||
|
for i in range(lenm4, lenbytes):
|
||||||
|
print("mwb 0x%08x 0x%02x" % ((baseaddr + i), elfblob[phdr.off + i]))
|
||||||
|
|
||||||
|
# bss
|
||||||
|
if zerobytes:
|
||||||
|
print("mwb 0x%08x 0x00 0x%08x" % ((baseaddr + lenbytes), zerobytes))
|
||||||
|
|
||||||
|
|
||||||
|
print("""
|
||||||
|
|
||||||
|
# set pc to entrypoint
|
||||||
|
reg pc 0x%08x
|
||||||
|
|
||||||
|
""" % elf.entry)
|
|
@ -0,0 +1,431 @@
|
||||||
|
|
||||||
|
# from https://github.com/porocyon/smol , which is WTFPL-2.0
|
||||||
|
|
||||||
|
# stolen from the contrib folder in https://github.com/blackle/LZMA-Vizualizer
|
||||||
|
# (i.e. I'm stealing it from myself)
|
||||||
|
|
||||||
|
# custom elf parser because a standard one wouldn't be trustable because the
|
||||||
|
# ELFs we're parsing will be a bit wonky anyway
|
||||||
|
|
||||||
|
from struct import unpack
|
||||||
|
from typing import *
|
||||||
|
|
||||||
|
|
||||||
|
ELFCLASS32 = 1
|
||||||
|
ELFCLASS64 = 2
|
||||||
|
|
||||||
|
EM_386 = 3
|
||||||
|
EM_X86_64 = 62
|
||||||
|
|
||||||
|
PT_NULL = 0
|
||||||
|
PT_LOAD = 1
|
||||||
|
PT_DYNAMIC = 2
|
||||||
|
PT_INTERP = 3
|
||||||
|
|
||||||
|
DT_NULL = 0
|
||||||
|
DT_NEEDED = 1
|
||||||
|
DT_PLTGOT = 3
|
||||||
|
DT_STRTAB = 5
|
||||||
|
DT_SYMTAB = 6
|
||||||
|
DT_RELA = 7
|
||||||
|
DT_RELASZ = 8
|
||||||
|
DT_RELAENT = 9
|
||||||
|
DT_STRSZ = 10
|
||||||
|
DT_SYMENT = 11
|
||||||
|
DT_SONAME = 14
|
||||||
|
DT_REL = 17
|
||||||
|
DT_RELSZ = 18
|
||||||
|
DT_RELENT = 19
|
||||||
|
DT_PLTREL = 20
|
||||||
|
DT_DEBUG = 21
|
||||||
|
DT_TEXTREL = 22
|
||||||
|
DT_JMPREL = 23
|
||||||
|
DT_BIND_NOW= 24
|
||||||
|
|
||||||
|
SHT_NULL = 0
|
||||||
|
SHT_PROGBITS = 1
|
||||||
|
SHT_SYMTAB = 2
|
||||||
|
SHT_STRTAB = 3
|
||||||
|
SHT_RELA = 4
|
||||||
|
SHT_DYNAMIC = 6
|
||||||
|
SHT_NOBITS = 8
|
||||||
|
SHT_REL = 9
|
||||||
|
SHT_DYNSYM = 11
|
||||||
|
|
||||||
|
SHF_WRITE = 1<<0
|
||||||
|
SHF_ALLOC = 1<<1
|
||||||
|
SHF_EXECINSTR = 1<<2
|
||||||
|
SHF_MERGE = 1<<4
|
||||||
|
SHF_STRINGS = 1<<5
|
||||||
|
SHF_INFO_LINK = 1<<6
|
||||||
|
|
||||||
|
STB_LOCAL = 0
|
||||||
|
STB_GLOBAL = 1
|
||||||
|
STB_WEAK = 2
|
||||||
|
|
||||||
|
STT_NOTYPE = 0
|
||||||
|
STT_OBJECT = 1
|
||||||
|
STT_FUNC = 2
|
||||||
|
STT_SECTION= 3
|
||||||
|
STT_FILE = 4
|
||||||
|
STT_COMMON = 5
|
||||||
|
STT_TLS = 6
|
||||||
|
STT_GNU_IFUNC = 10
|
||||||
|
|
||||||
|
STV_DEFAULT = 0
|
||||||
|
STV_INTERNAL = 1
|
||||||
|
STV_HIDDEN = 2
|
||||||
|
STV_PROTECTED = 3
|
||||||
|
|
||||||
|
class Phdr(NamedTuple):
|
||||||
|
ptype: int
|
||||||
|
off : int
|
||||||
|
vaddr: int
|
||||||
|
paddr: int
|
||||||
|
filesz: int
|
||||||
|
memsz: int
|
||||||
|
flags: int
|
||||||
|
align: int
|
||||||
|
|
||||||
|
class Dyn(NamedTuple):
|
||||||
|
tag: int
|
||||||
|
val: int
|
||||||
|
|
||||||
|
class Shdr(NamedTuple):
|
||||||
|
name: Union[int, str]
|
||||||
|
type: int
|
||||||
|
flags: int
|
||||||
|
addr: int
|
||||||
|
offset: int
|
||||||
|
size: int
|
||||||
|
link: int
|
||||||
|
info: int
|
||||||
|
addralign: int
|
||||||
|
entsize: int
|
||||||
|
|
||||||
|
class Sym(NamedTuple):
|
||||||
|
name: str
|
||||||
|
value: int
|
||||||
|
size: int
|
||||||
|
type: int
|
||||||
|
binding: int
|
||||||
|
visibility: int
|
||||||
|
shndx: int
|
||||||
|
|
||||||
|
class Rel(NamedTuple):
|
||||||
|
offset: int
|
||||||
|
symbol: Sym
|
||||||
|
type: int
|
||||||
|
class Rela(NamedTuple):
|
||||||
|
offset: int
|
||||||
|
symbol: Sym
|
||||||
|
type: int
|
||||||
|
addend: int
|
||||||
|
Reloc = Union[Rel, Rela]
|
||||||
|
|
||||||
|
class ELF(NamedTuple):
|
||||||
|
data : bytes
|
||||||
|
ident : bytes
|
||||||
|
eclass: int
|
||||||
|
mach : int
|
||||||
|
entry : int
|
||||||
|
phdrs : Sequence[Phdr]
|
||||||
|
dyn : Sequence[Dyn]
|
||||||
|
shdrs : Sequence[Shdr]
|
||||||
|
symtab: Sequence[Sym]
|
||||||
|
dynsym: Sequence[Sym]
|
||||||
|
relocs: Sequence[Reloc]
|
||||||
|
is32bit: bool
|
||||||
|
|
||||||
|
def readstr(data: bytes, off: int) -> str:
|
||||||
|
strb = bytearray()
|
||||||
|
while data[off] != 0 and off < len(data):
|
||||||
|
strb.append(data[off])
|
||||||
|
off = off + 1
|
||||||
|
return strb.decode('utf-8')
|
||||||
|
|
||||||
|
# yeah, there's some code duplication here
|
||||||
|
# idgaf
|
||||||
|
|
||||||
|
def parse_phdr32(data: bytes, phoff:int, phentsz:int, phnum:int) -> Sequence[Phdr]:
|
||||||
|
ps = []
|
||||||
|
for off in range(phoff, phoff+phentsz*phnum, phentsz):
|
||||||
|
ptype, off, vaddr, paddr, filesz, memsz, flags, align = \
|
||||||
|
unpack('<IIIIIIII', data[off:off+8*4])
|
||||||
|
p = Phdr(ptype, off, vaddr, paddr, filesz, memsz, flags, align)
|
||||||
|
ps.append(p)
|
||||||
|
|
||||||
|
return ps
|
||||||
|
|
||||||
|
def parse_dyn32(data: bytes, dynp: Phdr) -> Dyn:
|
||||||
|
ds = []
|
||||||
|
|
||||||
|
off = dynp.off
|
||||||
|
while True:
|
||||||
|
tag, val = unpack('<II', data[off:off+2*4])
|
||||||
|
ds.append(Dyn(tag, val))
|
||||||
|
|
||||||
|
if tag == DT_NULL: break
|
||||||
|
off = off + 2*4
|
||||||
|
|
||||||
|
return ds
|
||||||
|
|
||||||
|
def parse_reloc32(data: bytes, reloff: int, nrel: int, entsz: int, syms: Sequence[Sym], rela: bool) -> Reloc:
|
||||||
|
rr=[]
|
||||||
|
|
||||||
|
for off in range(reloff, reloff+entsz*nrel, entsz):
|
||||||
|
off, inf, add = unpack('<IIi', data[off:(off+12)]) if rela \
|
||||||
|
else (*unpack('<Ii', data[off:(off+8)]),0)
|
||||||
|
|
||||||
|
sym = syms[inf >> 8]
|
||||||
|
type = inf & 0xff
|
||||||
|
rr.append(Rela(off, sym, type, add) if rela else Rel(off, sym, type))
|
||||||
|
|
||||||
|
return rr
|
||||||
|
|
||||||
|
def parse_shdr32(data: bytes, shoff: int, shentsz: int, shnum: int,
|
||||||
|
shstrndx: int) -> Sequence[Shdr]:
|
||||||
|
if shnum*shentsz+shoff > len(data) or shentsz==0 or shnum==0 or shoff==0:
|
||||||
|
print("snum*shentsz+shoff",shnum*shentsz+shoff)
|
||||||
|
print("len(data)",len(data))
|
||||||
|
print("shentsz",shentsz)
|
||||||
|
print("shnum",shnum)
|
||||||
|
print("shoff",shoff)
|
||||||
|
return []
|
||||||
|
|
||||||
|
ss = []
|
||||||
|
for off in range(shoff, shoff+shentsz*shnum, shentsz):
|
||||||
|
noff, typ, flags, addr, off, size, link, info, align, entsz = \
|
||||||
|
unpack('<IIIIIIIIII', data[off:off+10*4])
|
||||||
|
s = Shdr(noff, typ, flags, addr, off, size, link, info, align, entsz)
|
||||||
|
ss.append(s)
|
||||||
|
|
||||||
|
if shstrndx < shnum:
|
||||||
|
shstr = ss[shstrndx]
|
||||||
|
for i in range(len(ss)):
|
||||||
|
sname = readstr(data, shstr.offset + ss[i].name) \
|
||||||
|
if ss[i].name < shstr.size else None
|
||||||
|
ss[i] = Shdr(sname, ss[i].type, ss[i].flags, ss[i].addr,
|
||||||
|
ss[i].offset, ss[i].size, ss[i].link, ss[i].info,
|
||||||
|
ss[i].addralign, ss[i].entsize)
|
||||||
|
|
||||||
|
return ss
|
||||||
|
|
||||||
|
def parse_sym32(data: bytes, sym: Shdr, strt: Shdr) -> Sequence[Sym]:
|
||||||
|
ss = []
|
||||||
|
for off in range(sym.offset, sym.offset+sym.size, sym.entsize):
|
||||||
|
noff, val, sz, info, other, shndx = \
|
||||||
|
unpack('<IIIBBH', data[off:off+3*4+2+2])
|
||||||
|
|
||||||
|
sn = readstr(data, strt.offset + noff) \
|
||||||
|
if noff < strt.size else None
|
||||||
|
s = Sym(sn, val, sz, (info & 15), (info >> 4), other, shndx)
|
||||||
|
ss.append(s)
|
||||||
|
return ss#sorted(ss, key=lambda x:x.value)
|
||||||
|
|
||||||
|
def parse_32(data: bytes) -> ELF:
|
||||||
|
ident = data[:16]
|
||||||
|
eclass = data[4]
|
||||||
|
mach = unpack('<H', data[18:18+2])[0]
|
||||||
|
entry = unpack('<I', data[24:24+4])[0]
|
||||||
|
|
||||||
|
phoff = unpack('<I', data[28:28+4])[0]
|
||||||
|
shoff = unpack('<I', data[32:32+4])[0]
|
||||||
|
phentsz = unpack('<H', data[42:42+2])[0]
|
||||||
|
phnum = unpack('<H', data[44:44+2])[0]
|
||||||
|
shentsz = unpack('<H', data[46:46+2])[0]
|
||||||
|
shnum = unpack('<H', data[48:48+2])[0]
|
||||||
|
shstrndx= unpack('<H', data[50:50+2])[0]
|
||||||
|
|
||||||
|
phdrs = [] if phentsz == 0 else parse_phdr32(data, phoff, phentsz, phnum)
|
||||||
|
dyn = None
|
||||||
|
|
||||||
|
for p in phdrs:
|
||||||
|
if p.ptype == PT_DYNAMIC:
|
||||||
|
dyn = parse_dyn32(data, p)
|
||||||
|
break
|
||||||
|
|
||||||
|
shdrs = parse_shdr32(data, shoff, shentsz, shnum, shstrndx)
|
||||||
|
#print("shdrs",shdrs)
|
||||||
|
|
||||||
|
symtabsh = [s for s in shdrs if s.type == SHT_SYMTAB and s.name == ".symtab"]
|
||||||
|
strtabsh = [s for s in shdrs if s.type == SHT_STRTAB and s.name == ".strtab"]
|
||||||
|
dynsymsh = [s for s in shdrs if s.type == SHT_SYMTAB and s.name == ".dynsym"]
|
||||||
|
dynstrsh = [s for s in shdrs if s.type == SHT_STRTAB and s.name == ".dynstr"]
|
||||||
|
relash = [s for s in shdrs if s.type == SHT_RELA]
|
||||||
|
relsh = [s for s in shdrs if s.type == SHT_REL]
|
||||||
|
|
||||||
|
#print("symtab",symtabsh)
|
||||||
|
#print("strtab",strtabsh)
|
||||||
|
|
||||||
|
assert len(symtabsh) < 2
|
||||||
|
assert len(strtabsh) < 2
|
||||||
|
assert len(dynsymsh) < 2
|
||||||
|
assert len(dynstrsh) < 2
|
||||||
|
|
||||||
|
symtab, dynsym = None, None
|
||||||
|
if len(symtabsh) and len(strtabsh):
|
||||||
|
symtab = parse_sym32(data, symtabsh[0], strtabsh[0]) \
|
||||||
|
if len(shdrs) > 0 else []
|
||||||
|
if len(dynsymsh) and len(dynstrsh):
|
||||||
|
dynsym = parse_sym32(data, symtabsh[0], strtabsh[0]) \
|
||||||
|
if len(shdrs) > 0 else []
|
||||||
|
|
||||||
|
relocs = []
|
||||||
|
|
||||||
|
# TODO: use sh.link to use the correct symbol table
|
||||||
|
for sh in relash:
|
||||||
|
relocs += parse_reloc32(data, sh.offset, sh.size//sh.entsize,
|
||||||
|
sh.entsize, symtab, True)
|
||||||
|
for sh in relsh:
|
||||||
|
relocs += parse_reloc32(data, sh.offset, sh.size//sh.entsize,
|
||||||
|
sh.entsize, symtab, False)
|
||||||
|
# TODO: relocs from DT_RELA, DT_REL
|
||||||
|
|
||||||
|
return ELF(data, ident, eclass, mach, entry, phdrs, dyn, shdrs,
|
||||||
|
symtab, dynsym, relocs, True)
|
||||||
|
|
||||||
|
def parse_phdr64(data: bytes, phoff:int, phentsz:int, phnum:int) -> Sequence[Phdr]:
|
||||||
|
ps = []
|
||||||
|
for off in range(phoff, phoff+phentsz*phnum, phentsz):
|
||||||
|
# TODO # what is TODO exactly??
|
||||||
|
ptype, flags, off, vaddr, paddr, filesz, memsz, align = \
|
||||||
|
unpack('<IIQQQQQQ', data[off:off+2*4+6*8])
|
||||||
|
p = Phdr(ptype, off, vaddr, paddr, filesz, memsz, flags, align)
|
||||||
|
ps.append(p)
|
||||||
|
|
||||||
|
return ps
|
||||||
|
|
||||||
|
def parse_dyn64(data: bytes, dynp: Phdr) -> Dyn:
|
||||||
|
ds = []
|
||||||
|
|
||||||
|
off = dynp.off
|
||||||
|
while True:
|
||||||
|
tag, val = unpack('<QQ', data[off:off+2*8])
|
||||||
|
ds.append(Dyn(tag, val))
|
||||||
|
|
||||||
|
if tag == DT_NULL: break
|
||||||
|
off = off + 2*8
|
||||||
|
|
||||||
|
return ds
|
||||||
|
|
||||||
|
def parse_reloc64(data: bytes, reloff: int, nrel: int, entsz: int, syms: Sequence[Sym], rela: bool) -> Reloc:
|
||||||
|
rr=[]
|
||||||
|
|
||||||
|
for off in range(reloff, reloff+entsz*nrel, entsz):
|
||||||
|
off, inf, add = unpack('<QQq', data[off:(off+24)]) if rela \
|
||||||
|
else (*unpack('<Qq', data[off:(off+16)]),0)
|
||||||
|
|
||||||
|
sym = syms[inf >> 32]
|
||||||
|
type = inf & 0xffffffff
|
||||||
|
rr.append(Rela(off, sym, type, add) if rela else Rel(off, sym, type))
|
||||||
|
|
||||||
|
return rr
|
||||||
|
|
||||||
|
def parse_shdr64(data: bytes, shoff: int, shentsz: int, shnum: int,
|
||||||
|
shstrndx: int) -> Sequence[Shdr]:
|
||||||
|
|
||||||
|
if shnum*shentsz+shoff > len(data) or shentsz==0 or shnum==0 or shoff==0:
|
||||||
|
return []
|
||||||
|
|
||||||
|
ss = []
|
||||||
|
for off in range(shoff, shoff+shentsz*shnum, shentsz):
|
||||||
|
noff, typ, flags, addr, off, size, link, info, align, entsz = \
|
||||||
|
unpack('<IIQQQQIIQQ', data[off:off+4*4+6*8])
|
||||||
|
s = Shdr(noff, typ, flags, addr, off, size, link, info, align, entsz)
|
||||||
|
ss.append(s)
|
||||||
|
|
||||||
|
if shstrndx < shnum:
|
||||||
|
shstr = ss[shstrndx]
|
||||||
|
for i in range(len(ss)):
|
||||||
|
sname = readstr(data, shstr.offset + ss[i].name) \
|
||||||
|
if ss[i].name < shstr.size else None
|
||||||
|
ss[i] = Shdr(sname, ss[i].type, ss[i].flags, ss[i].addr,
|
||||||
|
ss[i].offset, ss[i].size, ss[i].link, ss[i].info,
|
||||||
|
ss[i].addralign, ss[i].entsize)
|
||||||
|
|
||||||
|
return ss
|
||||||
|
|
||||||
|
def parse_sym64(data: bytes, sym: Shdr, strt: Shdr) -> Sequence[Sym]:
|
||||||
|
ss = []
|
||||||
|
for off in range(sym.offset, sym.offset+sym.size, sym.entsize):
|
||||||
|
noff, info, other, shndx, value, sz = \
|
||||||
|
unpack('<IBBHQQ', data[off:off+4+2+2+8*2])
|
||||||
|
|
||||||
|
sn = readstr(data, strt.offset + noff) \
|
||||||
|
if noff < strt.size else None
|
||||||
|
s = Sym(sn, value, sz, (info & 15), (info >> 4), other, shndx)
|
||||||
|
ss.append(s)
|
||||||
|
return ss#sorted(ss, key=lambda x:x.value)
|
||||||
|
|
||||||
|
def parse_64(data: bytes) -> ELF:
|
||||||
|
ident = data[:16]
|
||||||
|
eclass = data[4]
|
||||||
|
mach = unpack('<H', data[18:18+2])[0]
|
||||||
|
entry = unpack('<Q', data[24:24+8])[0]
|
||||||
|
|
||||||
|
phoff = unpack('<Q', data[32:32+8])[0]
|
||||||
|
shoff = unpack('<Q', data[40:40+8])[0]
|
||||||
|
phentsz = unpack('<H', data[54:54+2])[0]
|
||||||
|
phnum = unpack('<H', data[56:56+2])[0]
|
||||||
|
shentsz = unpack('<H', data[58:58+2])[0]
|
||||||
|
shnum = unpack('<H', data[60:60+2])[0]
|
||||||
|
shstrndx= unpack('<H', data[62:62+2])[0]
|
||||||
|
|
||||||
|
phdrs = [] if phentsz == 0 else parse_phdr64(data, phoff, phentsz, phnum)
|
||||||
|
dyn = None
|
||||||
|
|
||||||
|
for p in phdrs:
|
||||||
|
if p.ptype == PT_DYNAMIC:
|
||||||
|
dyn = parse_dyn64(data, p)
|
||||||
|
break
|
||||||
|
|
||||||
|
shdrs = [] if shentsz == 0 else parse_shdr64(data, shoff, shentsz, shnum, shstrndx)
|
||||||
|
|
||||||
|
symtabsh = [s for s in shdrs if s.type == SHT_SYMTAB and s.name == ".symtab"]
|
||||||
|
strtabsh = [s for s in shdrs if s.type == SHT_STRTAB and s.name == ".strtab"]
|
||||||
|
dynsymsh = [s for s in shdrs if s.type == SHT_SYMTAB and s.name == ".dynsym"]
|
||||||
|
dynstrsh = [s for s in shdrs if s.type == SHT_STRTAB and s.name == ".dynstr"]
|
||||||
|
relash = [s for s in shdrs if s.type == SHT_RELA]
|
||||||
|
relsh = [s for s in shdrs if s.type == SHT_REL]
|
||||||
|
|
||||||
|
assert len(symtabsh) < 2
|
||||||
|
assert len(strtabsh) < 2
|
||||||
|
assert len(dynsymsh) < 2
|
||||||
|
assert len(dynstrsh) < 2
|
||||||
|
|
||||||
|
symtab, dynsym = None, None
|
||||||
|
if len(symtabsh) and len(strtabsh):
|
||||||
|
symtab = parse_sym64(data, symtabsh[0], strtabsh[0]) \
|
||||||
|
if len(shdrs) > 0 else []
|
||||||
|
if len(dynsymsh) and len(dynstrsh):
|
||||||
|
dynsym = parse_sym64(data, symtabsh[0], strtabsh[0]) \
|
||||||
|
if len(shdrs) > 0 else []
|
||||||
|
|
||||||
|
relocs = []
|
||||||
|
|
||||||
|
# TODO: use sh.link to use the correct symbol table
|
||||||
|
for sh in relash:
|
||||||
|
relocs += parse_reloc32(data, sh.offset, sh.size//sh.entsize,
|
||||||
|
sh.entsize, symtab, True)
|
||||||
|
for sh in relsh:
|
||||||
|
relocs += parse_reloc32(data, sh.offset, sh.size//sh.entsize,
|
||||||
|
sh.entsize, symtab, False)
|
||||||
|
# TODO: relocs from DT_RELA, DT_REL
|
||||||
|
|
||||||
|
return ELF(data, ident, eclass, mach, entry, phdrs, dyn, shdrs,
|
||||||
|
symtab, dynsym, relocs, False)
|
||||||
|
|
||||||
|
def parse(data: bytes) -> ELF:
|
||||||
|
assert data[:4] == b'\x7FELF', "Not a valid ELF file" # good enough
|
||||||
|
|
||||||
|
ecls = data[4]
|
||||||
|
if ecls == ELFCLASS32: return parse_32(data)
|
||||||
|
elif ecls == ELFCLASS64: return parse_64(data)
|
||||||
|
else:
|
||||||
|
emch = unpack('<H', data[18:18+2])[0]
|
||||||
|
if emch == EM_386: return parse_32(data)
|
||||||
|
elif emch == EM_X86_64: return parse_64(data)
|
||||||
|
|
||||||
|
assert False, "bad E_CLASS %d" % ecls
|
||||||
|
|
|
@ -0,0 +1,248 @@
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
// known ID table:
|
||||||
|
// Chip name | ARM core | ARM CPUID | JTAG IDCODE | Jazelle ID
|
||||||
|
// ------------+------------+------------+-------------+----------- Jazelle DBX:
|
||||||
|
// ??? | ARM7EJ-S | ??? | ??? | ???
|
||||||
|
// Cypress FX3 | ARM926EJ-S | 0x41069265 | 0x07926069 | 0x64100004
|
||||||
|
// TI Nspire | ARM926EJ-S?| 0x41069265?| ??? | 0x64100004?
|
||||||
|
// RPi v1.?? | ARM11??? | ??? | ??? | ???
|
||||||
|
// Ninty ?3DS | ARM11MPCore| 0x410FB025?| ??? | 0x74100064?
|
||||||
|
// Xilinx Zynq7| Cortex-A9MP| 0x413FC090 | 0x4BA00477 | 0xF4100168
|
||||||
|
// ------------+------------+------------+-------------+----------- Jazelle RCT:
|
||||||
|
// Xilinx Zynq7| Cortex-A9MP| 0x413FC090 | 0x4BA00477 | 0xF4100168
|
||||||
|
// TODO: others (BeagleBoard Cortex-A8? Other A9s?)
|
||||||
|
|
||||||
|
// TODO: immediate next steps:
|
||||||
|
// * check if we can override jazelle insn execution with custom handlers
|
||||||
|
// * check what the control registers actually do
|
||||||
|
// * enumerate what all insns do
|
||||||
|
|
||||||
|
__attribute__((__naked__))
|
||||||
|
static uint32_t arm_get_id(void) {
|
||||||
|
asm volatile(
|
||||||
|
"mrc p15, 0, r0, c0, c0, 0\n"
|
||||||
|
"bx lr\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((__naked__))
|
||||||
|
static uint32_t jazelle_get_id(void) {
|
||||||
|
asm volatile(
|
||||||
|
"mrc p14, 7, r0, c0, c0, 0\n"
|
||||||
|
"bx lr\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((__aligned__(1024)))
|
||||||
|
static struct {
|
||||||
|
void* handlers[512];
|
||||||
|
uint8_t stack[256];
|
||||||
|
uint8_t locals[256];
|
||||||
|
} jazelle_block;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* c0: Jazelle Identity register (read-only)
|
||||||
|
Bits 0-11: Subarchitecture-defined bits (reads as 4, meaning unknown)
|
||||||
|
Bits 12-19: Subarchitecture (reads as 0, Jazelle V1 according to documentation)
|
||||||
|
Bits 20-27: Implementor (reads as 0x41, ARM Limited according to documentation)
|
||||||
|
Bits 28-31: Architecture (reads as 6, ARMv5TEJ according to documentation)
|
||||||
|
c1: Operating System Control register
|
||||||
|
Bit 0: Configuration Disabled (CD) (documented)
|
||||||
|
Bit 1: Configuration Valid (CV) (documented)
|
||||||
|
c2: Main Configuration register
|
||||||
|
Bit 0: Jazelle Enable (JE) (documented)
|
||||||
|
Bits 26-28: Unknown
|
||||||
|
Bit 29: If set, array object contains its elements directly, otherwise it contains a pointer to its elements
|
||||||
|
Bit 31: Disable array instructions if set?
|
||||||
|
c3: Array object layout register
|
||||||
|
Bits 0-7: Unknown
|
||||||
|
Bits 8-11: Offset (in words) within array object of first element or of pointer to first element
|
||||||
|
Bits 12-15: Offset (in words) within array object of length
|
||||||
|
Bit 16: If set, offset to length is subtracted, otherwise added
|
||||||
|
Bits 17-19: Array length shift value (number of elements = stored length >> this)
|
||||||
|
Bits 20-21: Disable array instructions if set?
|
||||||
|
*/
|
||||||
|
|
||||||
|
static uint32_t jazelle_exit_save;
|
||||||
|
|
||||||
|
__attribute__((__naked__))
|
||||||
|
static int jazelle_exec_native(const void* bytecode, const void* block) {
|
||||||
|
// inline asm parameters seems to be borking in GCC sooooo lets do it this way
|
||||||
|
(void)bytecode; (void)block; (void)&jazelle_exit_save;
|
||||||
|
asm volatile(
|
||||||
|
"push {r4-r12,lr}\n"
|
||||||
|
|
||||||
|
"mov lr, r0\n"
|
||||||
|
|
||||||
|
// init handler table pointer and stack pointer
|
||||||
|
"mov r5, r1\n"
|
||||||
|
"add r6, r5, #0x800\n"
|
||||||
|
"add r7, r5, #0x900\n"
|
||||||
|
// "r8: Pointer to constant pool? (haven't checked this yet)" -Hackspire
|
||||||
|
|
||||||
|
// set configuration valid & jazelle enable bits
|
||||||
|
"mov r0, #2\n"
|
||||||
|
"mcr p14, 7, r0, c1, c0, 0\n"
|
||||||
|
"mov r0, #1\n"
|
||||||
|
"mcr p14, 7, r0, c2, c0, 0\n"
|
||||||
|
|
||||||
|
// apparently there's no good way to find the exit point from jazelle,
|
||||||
|
// so we're going to hack that into the stuff now
|
||||||
|
"ldr r12, =jazelle_exit_save\n"
|
||||||
|
"adr r0, .Ljazend\n"
|
||||||
|
"str r0, [r12]\n"
|
||||||
|
|
||||||
|
// switch to jazelle mode
|
||||||
|
"adr r12, .Lno_jazelle\n"
|
||||||
|
"bxj r12\n"
|
||||||
|
|
||||||
|
".Ljazend:\n"
|
||||||
|
"mov r0, #0\n"
|
||||||
|
"b .Lend\n"
|
||||||
|
|
||||||
|
".Lno_jazelle:\n"
|
||||||
|
"bkpt #1\n"
|
||||||
|
"mov r0, #1\n"
|
||||||
|
|
||||||
|
".Lend:\n"
|
||||||
|
"mov r5, #0\n"
|
||||||
|
"mcr p14, 7, %r5, c1, c0, 0\n"
|
||||||
|
"mcr p14, 7, %r5, c2, c0, 0\n"
|
||||||
|
|
||||||
|
"pop {r4-r12,lr}\n"
|
||||||
|
"bx lr\n"
|
||||||
|
|
||||||
|
".pool\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__((__naked__))
|
||||||
|
static void handler_idiv(void) {
|
||||||
|
// r0 = 3
|
||||||
|
// r1 = 4
|
||||||
|
// r2 = 2
|
||||||
|
// editing the above has no effect
|
||||||
|
// NOTE: these depend on the stack content, i.e. it's not a moving register
|
||||||
|
// window. pushes happen in the following order: r0,r1,r2,r3
|
||||||
|
// TODO: when the 'register stack cache' is full, what happens? does
|
||||||
|
// it loop or does it act in a FIFO way, moving r0<-r1<-r2<-r3?
|
||||||
|
// how can the fillrate be known??? (libjz says r5 & 3 but i
|
||||||
|
// dont see anything like that, maybe its an ARM11 thing?)
|
||||||
|
// "r4: Copy of local variable 0. Only valid when local variable 0 is a
|
||||||
|
// single word (i.e. int, float, or Object; not long or double)"
|
||||||
|
// -Hackspire
|
||||||
|
//
|
||||||
|
// [r6-4] is stack top (2)
|
||||||
|
// [r6-8] is 4
|
||||||
|
// etc
|
||||||
|
// use the above to manipulate the stack, eg. "iadd" is implemented as:
|
||||||
|
// - add r1, r2 // or equivalently, read from stack i guess
|
||||||
|
// - str r1, [r6, #-8] // store to the place where it will be read
|
||||||
|
// - sub r6, #4 // pop off & discard stack top element
|
||||||
|
// NOTE: this input usage (with r1 and r2) is NOT robust at all, use memory
|
||||||
|
// reads instead!
|
||||||
|
|
||||||
|
asm volatile(
|
||||||
|
"bkpt #3\n"
|
||||||
|
// FIXME: read out stack contents in a better way
|
||||||
|
"add r1, r2\n"
|
||||||
|
"str r1, [r6,#-8]\n"
|
||||||
|
"sub r6, #4\n"
|
||||||
|
|
||||||
|
// return to jazelle (yes lr has to be incremented otherwise the
|
||||||
|
// current instruction keeps getting executed in a loop)
|
||||||
|
"add lr, #1\n"
|
||||||
|
"bxj r12\n" // FIXME: r12 can be modified by jazelle so it should be restored to something
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((__naked__))
|
||||||
|
static void handler_ireturn(void) {
|
||||||
|
int result;
|
||||||
|
asm volatile(
|
||||||
|
"ldr %[res], [r6, #-4]!\n"
|
||||||
|
:[res]"=r"(result)
|
||||||
|
);
|
||||||
|
//iprintf("result=%d\r\n", result); // FIXME: save & restore r0-r3 if ret implemented properly
|
||||||
|
|
||||||
|
// get back to original code
|
||||||
|
// TODO: later stage: get back to previous bytecode stuff
|
||||||
|
asm volatile(
|
||||||
|
"ldr r12, %[exsav]\n"
|
||||||
|
"bx r12\n"
|
||||||
|
:
|
||||||
|
:[exsav]"m"(jazelle_exit_save)
|
||||||
|
:"r12"
|
||||||
|
);
|
||||||
|
__builtin_unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int jazelle_exec(const uint8_t* bytecode) {
|
||||||
|
jazelle_block.handlers[0x6C] = handler_idiv;
|
||||||
|
jazelle_block.handlers[0xAC] = handler_ireturn;
|
||||||
|
/*
|
||||||
|
* +000-3FF: Unhandled bytecodes
|
||||||
|
The stack is flushed to memory before calling any of these handlers, so they may modify r0-r3 freely
|
||||||
|
+400: Null pointer exception
|
||||||
|
+404: Array index out of bounds exception
|
||||||
|
+40C: Jazelle mode entered with JE = 0
|
||||||
|
+410: Configuration invalid (Jazelle mode entered with CV = 0)
|
||||||
|
CV is automatically set to 1 on entering this handler
|
||||||
|
+414: Prefetch abort occurred in middle of instruction
|
||||||
|
-Hackspire
|
||||||
|
*/
|
||||||
|
const void* block = &jazelle_block;
|
||||||
|
|
||||||
|
return jazelle_exec_native(bytecode, block);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// https://github.com/SonoSooS/libjz/wiki/Java-instruction-set
|
||||||
|
static uint8_t bytecode_test1[] = {
|
||||||
|
0x06, // iconst_3
|
||||||
|
0x07, // iconst_4
|
||||||
|
0x05, // iconst_2
|
||||||
|
0x6C, // idiv
|
||||||
|
0x04, // iconst_1
|
||||||
|
0x60, // iadd
|
||||||
|
0x60, // iadd
|
||||||
|
0xAC, // ireturn
|
||||||
|
};
|
||||||
|
|
||||||
|
void jazelle_main(void) {
|
||||||
|
uint32_t aid = arm_get_id();
|
||||||
|
uint32_t jid = jazelle_get_id();
|
||||||
|
//iprintf("hello world! ARM coreID=0x%lx jazelle ID=0x%lx\r\n", aid, jid);
|
||||||
|
while (jid == 0) ;
|
||||||
|
|
||||||
|
int r = jazelle_exec(bytecode_test1);
|
||||||
|
//iprintf("retcode=%d\r\n", r);
|
||||||
|
|
||||||
|
while (true) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
jazelle_main();
|
||||||
|
}
|
||||||
|
|
||||||
|
__attribute__((__naked__, __section__(".entry")))
|
||||||
|
void _start(void) {
|
||||||
|
asm volatile(
|
||||||
|
"ldr sp, =0xffffff00\n"
|
||||||
|
"bl main\n"
|
||||||
|
"1: b 1b\n"
|
||||||
|
".pool\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
|
||||||
|
ENTRY(_start)
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
. = 0xffff0000;
|
||||||
|
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
*(.entry*)
|
||||||
|
*(.text*)
|
||||||
|
*(.rodata*)
|
||||||
|
}
|
||||||
|
.data :
|
||||||
|
{
|
||||||
|
*(.data*)
|
||||||
|
}
|
||||||
|
|
||||||
|
.ARM.extab :
|
||||||
|
{
|
||||||
|
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||||
|
}
|
||||||
|
__exidx_start = .;
|
||||||
|
.ARM.exidx :
|
||||||
|
{
|
||||||
|
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
|
||||||
|
}
|
||||||
|
__exidx_end = .;
|
||||||
|
|
||||||
|
.bss :
|
||||||
|
{
|
||||||
|
*(.bss*)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue