mirror of
https://github.com/MCKero6423/uv-k5-v3-emulator.git
synced 2026-10-02 03:15:36 +00:00
Build overlay apps without the Arm toolchain, and say how far verification got
pip install ziglang cross-compiles to thumb-freestanding-eabi, which is enough to build a .app with no arm-none-eabi-gcc and no Docker. Measured refusals: --defsym, -Ttext and --section-start come back as unsupported linker args, so the VMA is resolved into a copy of app.ld; -T is forwarded (a missing script errors); --image-base is accepted but page-aligns the segments into 0x200103C8 and 0x20020C94. tools/elf2bin.py extracts allocated sections rather than program headers, because lld maps the ELF header and phdr table as a 180-byte LOAD of its own -- following the headers starts the image at 0x20000000 and APP_ERR_VMA. One division pulled in __aeabi_uidiv, which a -nostdlib blob cannot have: Minesweeper now avoids division entirely. app_main carries the .text.entry attribute upstream's apps use, so the entry is first for the loader's jump to offset 0. Minesweeper builds to 2408 bytes of code against a 4096-byte budget. Installed through the page, the firmware reads the slot header twice and then exactly code_size bytes from slot+0x1000 -- the only read of that size in the boot log -- so the blob shape, header, CRC, VMA and offset are all accepted. Whether control reaches the overlay is unproven: the 100 ms PC probe saw no overlay address, no APP ERROR screen appears, and the app does not draw. test_elf2bin pins the phantom-header-segment lesson; both AGENTS files record the rest.
This commit is contained in:
1 parent
61344e486e
commit
f938d0b3bd
5 files changed
+226
-8
No files matched your search
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
"""The ELF extractor must follow *sections*, not program headers.
|
||||
|
||||
lld maps the ELF header and program-header table as a LOAD of its own -- 52 + 4x32 = 180
|
||||
bytes at the image base, with no section content in it. An extractor that follows program
|
||||
headers therefore puts a phantom 180-byte region below the first real section, and the
|
||||
image is refused with APP_ERR_VMA or, worse, built from the wrong base. These tests build
|
||||
the ELFs by hand, so they need no toolchain.
|
||||
"""
|
||||
import struct
|
||||
import unittest
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
import elf2bin
|
||||
|
||||
|
||||
def build_elf(body=b"\x11" * 64, with_phantom_phdr=False):
|
||||
"""A minimal 32-bit little-endian ELF with one allocated section holding @body.
|
||||
|
||||
With @with_phantom_phdr the first program header is a LOAD covering the ELF header and
|
||||
the program-header table itself -- no section content, exactly what lld emits -- which
|
||||
must not become part of the extracted image.
|
||||
"""
|
||||
ehsize, phentsize, shentsize = 52, 32, 40
|
||||
phnum = 2 if with_phantom_phdr else 1
|
||||
phoff = ehsize
|
||||
data_off = phoff + phnum * phentsize
|
||||
shoff = data_off + len(body)
|
||||
blob = bytearray(shoff + shentsize)
|
||||
blob[0:4] = b"\x7fELF"
|
||||
blob[4] = 1 # 32-bit
|
||||
blob[5] = 1 # little endian
|
||||
struct.pack_into("<I", blob, 0x1C, phoff)
|
||||
struct.pack_into("<H", blob, 0x2A, phentsize)
|
||||
struct.pack_into("<H", blob, 0x2C, phnum)
|
||||
struct.pack_into("<I", blob, 0x20, shoff)
|
||||
struct.pack_into("<H", blob, 0x2E, shentsize)
|
||||
struct.pack_into("<H", blob, 0x30, 1)
|
||||
blob[data_off:data_off + len(body)] = body # the section's file content
|
||||
i = 0
|
||||
if with_phantom_phdr:
|
||||
struct.pack_into("<IIIIIIII", blob, phoff, 1, 0, 0x20000000, 0x20000000,
|
||||
ehsize + phentsize, 0, 4, 4)
|
||||
i = 1
|
||||
struct.pack_into("<IIIIIIII", blob, phoff + i * phentsize, 1, data_off, 0x20000280,
|
||||
0x20000280, len(body), len(body), 5, 4)
|
||||
struct.pack_into("<IIIIIIIIII", blob, shoff, 0, 1, 0x6, 0x20000280,
|
||||
data_off, len(body), 0, 0, 4, 0)
|
||||
return bytes(blob)
|
||||
|
||||
|
||||
class TestExtract(unittest.TestCase):
|
||||
def _extract(self, blob, minimum=0x20000280):
|
||||
path = os.path.join(tempfile.mkdtemp(), "in.elf")
|
||||
out = os.path.join(os.path.dirname(path), "out.bin")
|
||||
with open(path, "wb") as fh:
|
||||
fh.write(blob)
|
||||
elf2bin.main([path, out, "--min", hex(minimum), "--quiet"])
|
||||
with open(out, "rb") as fh:
|
||||
return fh.read()
|
||||
|
||||
def test_a_plain_single_section_elf(self):
|
||||
blob = build_elf()
|
||||
self.assertEqual(self._extract(blob), b"\x11" * 64)
|
||||
|
||||
def test_the_phantom_header_segment_is_not_part_of_the_image(self):
|
||||
"""This is the case that broke the first version: 0x20000000 is not the base."""
|
||||
blob = build_elf(with_phantom_phdr=True)
|
||||
self.assertEqual(self._extract(bytes(blob)), b"\x11" * 64)
|
||||
|
||||
def test_a_wrong_base_is_refused_rather_than_silently_shifted(self):
|
||||
blob = build_elf()
|
||||
with self.assertRaises(SystemExit):
|
||||
self._extract(bytes(blob), minimum=0x20000000)
|
||||
|
||||
def test_something_that_is_not_an_elf_is_refused(self):
|
||||
with self.assertRaises(SystemExit):
|
||||
self._extract(b"not an elf at all" + b"\x00" * 64)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user