unfinished stuff, also zynq

This commit is contained in:
Triss 2022-01-24 02:12:57 +01:00
parent 1b68909633
commit 852c438b10
9 changed files with 1019 additions and 84 deletions

1
.gitignore vendored
View File

@ -4,3 +4,4 @@
*.elf
*.map
notes
*.html

View File

@ -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)

290
jazelle.c
View File

@ -3,21 +3,35 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
// 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) ;
}

3
zynq/.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.elf
*.o
__pycache__/

38
zynq/Makefile Normal file
View File

@ -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

49
zynq/elf2oocd.py Executable file
View File

@ -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)

431
zynq/hackyelf.py Normal file
View File

@ -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

248
zynq/zynq.c Normal file
View File

@ -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"
);
}

35
zynq/zynq.ld Normal file
View File

@ -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*)
}
}