Add a tiny elfutils like library
.. it can find symbols and determine how to load a binary This might be useful for CircuitPython "coproc" so that an elf file can be used (instead of a bin file); symbol offsets within the shared memory area can be determined, and the right portion of the elf file can be loaded into the coprocessor memory.
This commit is contained in:
parent
01d8291a61
commit
6549bbcb72
4 changed files with 167 additions and 3 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -1,2 +1,4 @@
|
|||
__pycache__
|
||||
/link.ld
|
||||
/a.out
|
||||
/a.out-stripped
|
||||
|
|
|
|||
15
Makefile
15
Makefile
|
|
@ -1,6 +1,12 @@
|
|||
ifeq ($(origin IDF_PATH),undefined)
|
||||
$(error You must "source esp-idf/export.sh" before building)
|
||||
endif
|
||||
|
||||
COPROC_RESERVE_MEM ?= 8176
|
||||
SOC := esp32s3
|
||||
CC := riscv32-esp-elf-gcc
|
||||
CROSS := riscv32-esp-elf-
|
||||
CC := $(CROSS)gcc
|
||||
STRIP := $(CROSS)strip
|
||||
CFLAGS := -Os -march=rv32imc -mdiv -fdata-sections -ffunction-sections
|
||||
CFLAGS += -isystem $(IDF_PATH)/components/ulp/ulp_riscv/include/
|
||||
CFLAGS += -isystem $(IDF_PATH)/components/soc/$(SOC)/include
|
||||
|
|
@ -20,13 +26,16 @@ LDFLAGS += link.ld
|
|||
|
||||
|
||||
.PHONY: default
|
||||
default: a.out
|
||||
default: a.out-stripped
|
||||
a.out-stripped: a.out
|
||||
$(STRIP) -g -o $@ $<
|
||||
|
||||
a.out: $(SRCS) link.ld
|
||||
$(CC) -flto $(CFLAGS) $^ -o $@ $(LDFLAGS)
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f a.out link.ld
|
||||
rm -f a.out* link.ld
|
||||
|
||||
link.ld: ulp.riscv.ld
|
||||
$(CC) -E -P -xc $(CFLAGS) -o $@ $<
|
||||
|
|
|
|||
12
py/minidump.py
Normal file
12
py/minidump.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
from minielf import ELFFile, PT_LOAD
|
||||
|
||||
a_out = open("a.out-stripped", "rb")
|
||||
e = ELFFile(a_out)
|
||||
s = e.get_section(1)
|
||||
s = e.get_section_by_name('.symtab')
|
||||
sy = s.get_symbol_by_name('shared_mem')[0]
|
||||
en = sy.entry
|
||||
for h in e.iter_headers():
|
||||
if h.p_type == PT_LOAD:
|
||||
print(f"@{h.p_vaddr:04x}: Load {h.p_filesz} bytes starting at {h.p_offset}")
|
||||
print(f"shared_mem @ 0x{en.st_value:04x} 0x{en.st_size:04x} bytes")
|
||||
141
py/minielf.py
Normal file
141
py/minielf.py
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
import struct
|
||||
from collections import namedtuple
|
||||
|
||||
|
||||
class StructMixin:
|
||||
@classmethod
|
||||
def calcsize(cls):
|
||||
return struct.calcsize(cls._fmt)
|
||||
|
||||
@classmethod
|
||||
def frombuffer(cls, buf):
|
||||
return cls(*struct.unpack(cls._fmt, buf))
|
||||
|
||||
_ElfHeader32 = namedtuple('_ElfHeader32', """
|
||||
e_ident e_type e_machine e_version e_entry e_phoff e_shoff e_flags e_ehsize e_phentsize e_phnum e_shentsize e_shnum e_shstrndx""".split())
|
||||
class ElfHeader32(_ElfHeader32, StructMixin):
|
||||
_fmt='<16s2h5l6h'
|
||||
|
||||
_SectionHeader32 = namedtuple('_SectionHeader32', """
|
||||
sh_name sh_type sh_flags sh_addr sh_offset sh_size sh_link sh_info
|
||||
sh_addralign sh_entsize
|
||||
""".split())
|
||||
class SectionHeader32(_SectionHeader32, StructMixin):
|
||||
_fmt = '<10l'
|
||||
|
||||
class Section:
|
||||
def __init__(self, ef, sh):
|
||||
self._elffile = ef
|
||||
self._header = sh
|
||||
|
||||
def readat(self, offset, sz):
|
||||
return self._elffile._readat(offset + self._header.sh_offset, sz)
|
||||
def constructat(self, offset, cls):
|
||||
return self._elffile._constructat(offset + self._header.sh_offset, cls)
|
||||
|
||||
class StringTable(Section):
|
||||
def symbolat(self, offset):
|
||||
result = b''
|
||||
stream = self._elffile.stream
|
||||
stream.seek(self._header.sh_offset + offset)
|
||||
while (c := stream.read(1)) != b'\0' and c != b'':
|
||||
result += c
|
||||
return result
|
||||
|
||||
_SymbolTableEntry = namedtuple('_SymbolTableEntry',
|
||||
['st_name', 'st_value', 'st_size', 'set_info', 'st_other', 'st_shndx'])
|
||||
|
||||
class SymbolTableEntry(_SymbolTableEntry, StructMixin):
|
||||
_fmt = '<3l2bh'
|
||||
|
||||
class Symbol:
|
||||
def __init__(self, name, entry):
|
||||
self.name = name
|
||||
self.entry = entry
|
||||
|
||||
class SymbolTable(Section):
|
||||
def iter_symbols(self):
|
||||
for i in range(0, self._header.sh_size, SymbolTableEntry.calcsize()):
|
||||
yield self.constructat(i, SymbolTableEntry)
|
||||
|
||||
def get_symbol_by_name(self, name):
|
||||
if not isinstance(name, bytes): name = name.encode()
|
||||
strs = self._elffile.get_section_by_name('.strtab')
|
||||
for sy in self.iter_symbols():
|
||||
name2 = strs.symbolat(sy.st_name)
|
||||
if name == name2:
|
||||
return [Symbol(name, sy)]
|
||||
|
||||
|
||||
section_constructors = {
|
||||
2: SymbolTable,
|
||||
3: StringTable,
|
||||
}
|
||||
|
||||
_HeaderTableEntry = namedtuple('_HeaderTableEntry',
|
||||
['p_type', 'p_offset', 'p_vaddr', 'p_paddr', 'p_filesz', 'p_memsz', 'p_flags', 'p_align'])
|
||||
|
||||
PT_LOAD = 1
|
||||
|
||||
class HeaderTableEntry(_HeaderTableEntry, StructMixin):
|
||||
_fmt = '<8l'
|
||||
|
||||
class ELFFile:
|
||||
def __init__(self, stream):
|
||||
self.stream = stream
|
||||
self._buffer = ()
|
||||
if self._readat(0, 4) != b'\177ELF':
|
||||
raise ValueError("Not an ELF file")
|
||||
if self._readat(4, 3) != b'\1\1\1':
|
||||
raise ValueError("Incompatible ELF file")
|
||||
self._header = self._constructat(0, ElfHeader32)
|
||||
|
||||
def _readat(self, offset, sz):
|
||||
if len(self._buffer) < sz:
|
||||
self._buffer = bytearray(sz)
|
||||
self._view = memoryview(self._buffer)
|
||||
mv = self._view[:sz]
|
||||
self.stream.seek(offset)
|
||||
self.stream.readinto(mv)
|
||||
return mv
|
||||
|
||||
def _decodeat(self, offset, fmt):
|
||||
sz = struct.calcsize(fmt)
|
||||
mb = self._readat(offset, sz)
|
||||
return struct.unpack(fmt, mv)
|
||||
|
||||
def _constructat(self, offset, cls):
|
||||
sz = cls.calcsize()
|
||||
mb = self._readat(offset, sz)
|
||||
return cls.frombuffer(mb)
|
||||
|
||||
def get_section(self, index):
|
||||
if not (0 <= index < self._header.e_shnum):
|
||||
raise IndexError("Invalid section number")
|
||||
offset = self._header.e_shoff + index * self._header.e_shentsize
|
||||
sh = self._constructat(offset, SectionHeader32)
|
||||
constructor = section_constructors.get(sh.sh_type, Section)
|
||||
return constructor(self, sh)
|
||||
|
||||
def iter_sections(self):
|
||||
for i in range(self._header.e_shnum):
|
||||
yield self.get_section(i)
|
||||
|
||||
def get_section_by_name(self, name):
|
||||
if not isinstance(name, bytes): name = name.encode()
|
||||
idx = self.get_section(self._header.e_shstrndx)
|
||||
for sec in self.iter_sections():
|
||||
off = sec._header.sh_name
|
||||
name2 = idx.symbolat(off)
|
||||
if name == name2:
|
||||
return sec
|
||||
|
||||
def get_header(self, index):
|
||||
if not (0 <= index < self._header.e_phnum):
|
||||
raise IndexError("Invalid header number")
|
||||
offset = self._header.e_phoff + index * self._header.e_phentsize
|
||||
return self._constructat(offset, HeaderTableEntry)
|
||||
|
||||
def iter_headers(self):
|
||||
for i in range(self._header.e_phnum):
|
||||
yield self.get_header(i)
|
||||
Loading…
Reference in a new issue