Commit 63e2d3ba authored by Léo Grange's avatar Léo Grange
Browse files

more interesting example, linker script and symbol address extraction

parent bca7599a
#!/usr/bin/env python3
#
# Simple attempt to use Unicorn framework to simulate a basic ARM CPU and do some benchmarking
# On arch: pacman -S python-unicorn
......@@ -10,17 +11,52 @@ from unicorn import arm_const
# Base address of user code (?)
ADDRESS = 0x200000
MEMORY_SIZE = 4 * 1024 * 2014
# file containing the raw binary to execute (must be assemble and linked)
CODE_FILE = "test_arm/test.bin"
def get_machine_code():
with open(CODE_FILE, 'rb') as f:
return f.read()
def count_stuff(*args, **kwargs):
print(f"[I] hook called, args: {*args}, {**kwargs}")
def main():
print("Emulate ARM board")
# Initialize emulator in ARM mode
mu = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_ARM)
try:
# Initialize emulator in ARM mode
mu = unicorn.Uc(unicorn.UC_ARCH_ARM, unicorn.UC_MODE_ARM)
# Give some memory, mapped at our start address
mu.mem_map(ADDRESS, MEMORY_SIZE)
# write the binary machine code
mu.mem_write(ADDRESS, get_machine_code())
# initialize with at least PC
# TODO init also SP and a few other registers?
mu.reg_write(arm_const.UC_ARM_REG_R13, ADDRESS + MEMORY_SIZE - 4)
# XXX just a test, not sure to understand what/why
#mu.hook_add(unicorn.UC_HOOK_INSN, arg1=arm_const.UC_)
# from ADDRESS to ADDRESS+400 (arbitrary)
# With maximum 10 seconds or 100 instructions (?)
# NOTE: very badly documented...
mu.emu_start(ADDRESS, ADDRESS+400, timeout=10, count=100)
# Give some memory
# should be done, check the registers
result = mu.reg_read(arm_const.UC_ARM_REG_R3)
print("Emulation done (?)")
print(f"[I] Resulting value in r3: {result}")
pass
except UcError as e:
print(f"[E] Exception: {e}")
if __name__ == '__main__':
......
......@@ -2,15 +2,25 @@ PREFIX ?= arm-none-eabi
AS = $(PREFIX)-as
LD = $(PREFIX)-ld
OBJCOPY = $(PREFIX)-objcopy
NM = $(PREFIX)-nm
LINKER_SCRIPT=emulation.ld
TARGET = test.bin
AUX_SOURCES = crt0.s
.phony: all clean
all: $(TARGET)
# we *DON'T* want any default rules....
.SUFFIXES:
.SECONDARY: $(TARGET:.bin=.elf) $(TARGET:.bin=.o) $(TARGET:.s=.o)
all: $(TARGET) $(TARGET:.bin=)-symbols.json
$(TARGET:.bin=.elf): $(AUX_SOURCES:.s=.o)
%.bin: %.elf
$(OBJCOPY) -O binary $^ $@
......@@ -21,6 +31,9 @@ all: $(TARGET)
%.elf: %.o
$(LD) -o $@ -T $(LINKER_SCRIPT) $^
%-symbols.json: %.elf
./find_symbols.sh $(NM) "$^" "_exit" "_start" > $@
clean:
rm -f $(TARGET) $(TARGET:.bin=.elf) $(TARGET:.bin=.o)
rm -f $(TARGET) $(TARGET:.bin=.elf) $(TARGET:.bin=.o) $(TARGET:.bin=-symbols.json)
@ Simale initial assembly for starting a program in a virtual machine
@ It consists only on a __reset__ symbol that will call an external _start symbol
.section .text.crt0
.extern _start
__reset__:
adr r0, _start_sym
ldr r15, [r0]
nop
_start_sym: .int _start
SECTIONS
{
. = 0x200000;
.text : { *(.text) }
.text : { *(.text.crt0) *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
#!/bin/sh
# A simple overlay on nm to write a JSON file with the address of a given list of symbols
# Quite dirty but... let's say "usable"?
usage() {
echo "Usage: $0 <nm binary> <ELF file> symbol1 [symbol2 [...]]" >&2
exit 1
}
if [ $# -lt 3 ]
then
usage $0
fi
nm_binary=$1
elf_file=$2
shift 2
all_symbols=$(${nm_binary} "${elf_file}")
echo "{"
# wonderful JSON format......
first_line=true
# not a very efficient way to do that...
for sym in "$@"
do
# some sed magic
sym_address=$(echo "$all_symbols" | sed -e 's/^\([0-9a-f]\+\) .*\<'"${sym}"'\>$/\1/;t;d')
if [ "$sym_address" ]
then
#echo "[D] Find address 0x${sym_address} for symbol '${sym}'" >&2
if ${first_line}
then
first_line=false
else
echo -en ",\n"
fi
echo -en "\t\"${sym}\": \"0x${sym_address}\""
else
echo "[W] No symbol '${sym}' found!" >&2
fi
done
echo -e "\n}"
.global _start
.global _exit
_start:
mov r1, #10
mov r2, #40
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment