From 852c438b109c59262c7e9dfbc28721d438e560a0 Mon Sep 17 00:00:00 2001 From: sys64738 Date: Mon, 24 Jan 2022 02:12:57 +0100 Subject: [PATCH] unfinished stuff, also zynq --- .gitignore | 1 + Makefile | 8 +- jazelle.c | 290 ++++++++++++++++++++++--------- zynq/.gitignore | 3 + zynq/Makefile | 38 +++++ zynq/elf2oocd.py | 49 ++++++ zynq/hackyelf.py | 431 +++++++++++++++++++++++++++++++++++++++++++++++ zynq/zynq.c | 248 +++++++++++++++++++++++++++ zynq/zynq.ld | 35 ++++ 9 files changed, 1019 insertions(+), 84 deletions(-) create mode 100644 zynq/.gitignore create mode 100644 zynq/Makefile create mode 100755 zynq/elf2oocd.py create mode 100644 zynq/hackyelf.py create mode 100644 zynq/zynq.c create mode 100644 zynq/zynq.ld diff --git a/.gitignore b/.gitignore index c97444d..62b223e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ *.elf *.map notes +*.html diff --git a/Makefile b/Makefile index 8da3761..b650197 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ CC = $(PREFIX)gcc GDB ?= $(PREFIX)gdb 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 INCLUDE = -I. GENDEP = -MMD -MP @@ -41,7 +41,11 @@ openocd-load: jazelle.elf 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) diff --git a/jazelle.c b/jazelle.c index d5623a4..0e1c70c 100644 --- a/jazelle.c +++ b/jazelle.c @@ -3,21 +3,35 @@ #include #include #include +#include // known ID table: -// Chip name | ARM core | Jazelle ID -// ------------+------------+----------- -// ??? | ARM7EJ-S | ??? -// Cypress FX3 | ARM926EJ-S | 0x64100004 -// TI Nspire | ARM926EJ-S?| 0x64100004? -// RPi v1.?? | ARM11??? | ??? -// Ninty ?3DS | ARM11MPCore| 0x74100064? +// 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( @@ -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) 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? */ -// https://github.com/SonoSooS/libjz/wiki/Java-instruction-set -static uint8_t bytecode[] = { - 0x06, // iconst_3 - 0x07, // iconst_4 - 0x05, // iconst_2 - 0x6C, // idiv - 0x04, // iconst_1 - 0x60, // iadd - 0x60, // iadd - 0xAC, // ireturn -}; +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" + //"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__)) static void handler_idiv(void) { @@ -95,6 +152,7 @@ static void handler_idiv(void) { // 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" @@ -107,16 +165,15 @@ static void handler_idiv(void) { ); } -static uint32_t jazelle_exit_save; - __attribute__((__naked__)) static void handler_ireturn(void) { int result; asm volatile( + //"bkpt #4\n" "ldr %[res], [r6, #-4]!\n" :[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 // TODO: later stage: get back to previous bytecode stuff @@ -130,56 +187,6 @@ static void handler_ireturn(void) { __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) { jazelle_block.handlers[0x6C] = handler_idiv; jazelle_block.handlers[0xAC] = handler_ireturn; @@ -199,12 +206,131 @@ static int jazelle_exec(const uint8_t* bytecode) { 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) ; } diff --git a/zynq/.gitignore b/zynq/.gitignore new file mode 100644 index 0000000..4136b11 --- /dev/null +++ b/zynq/.gitignore @@ -0,0 +1,3 @@ +*.elf +*.o +__pycache__/ diff --git a/zynq/Makefile b/zynq/Makefile new file mode 100644 index 0000000..823143b --- /dev/null +++ b/zynq/Makefile @@ -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 diff --git a/zynq/elf2oocd.py b/zynq/elf2oocd.py new file mode 100755 index 0000000..a3b43e6 --- /dev/null +++ b/zynq/elf2oocd.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +import struct, sys + +import hackyelf + +if len(sys.argv) == 1: + printf("Usage: %s > " % 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) diff --git a/zynq/hackyelf.py b/zynq/hackyelf.py new file mode 100644 index 0000000..9145d5b --- /dev/null +++ b/zynq/hackyelf.py @@ -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(' Dyn: + ds = [] + + off = dynp.off + while True: + tag, val = unpack(' Reloc: + rr=[] + + for off in range(reloff, reloff+entsz*nrel, entsz): + off, inf, add = unpack('> 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(' Sequence[Sym]: + ss = [] + for off in range(sym.offset, sym.offset+sym.size, sym.entsize): + noff, val, sz, info, other, shndx = \ + unpack('> 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(' 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(' Dyn: + ds = [] + + off = dynp.off + while True: + tag, val = unpack(' Reloc: + rr=[] + + for off in range(reloff, reloff+entsz*nrel, entsz): + off, inf, add = unpack('> 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(' Sequence[Sym]: + ss = [] + for off in range(sym.offset, sym.offset+sym.size, sym.entsize): + noff, info, other, shndx, value, sz = \ + unpack('> 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(' 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(' +#include +#include +#include +#include + +// 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" + ); +} + diff --git a/zynq/zynq.ld b/zynq/zynq.ld new file mode 100644 index 0000000..e6996d9 --- /dev/null +++ b/zynq/zynq.ld @@ -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*) + } +} +