Skip to content

Commit

Permalink
add dump command to picobin
Browse files Browse the repository at this point in the history
  • Loading branch information
soypat committed Sep 15, 2024
1 parent 399ba0e commit 671fd32
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 92 deletions.
67 changes: 0 additions & 67 deletions blocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,73 +20,6 @@ var (
endMarker = binary.LittleEndian.AppendUint32(nil, BlockMarkerEnd)
)

type Item struct {
Head uint8 // [0:1]:size_flag(0 means 1 byte size, 1 means 2 byte size) [1:7]: item type.
// Contains size and special data.
// [0:8]: s0%256
// [8:16]: s0/256 if size_flag==1, or is type specific data for blocks that are never >256 words.
SizeAndSpecial uint16
TypeData uint8 // Type specific data.
Data []byte // Item data.
}

// HeaderBytes returns the first 4 bytes of the item header as a static array.
func (it Item) HeaderBytes() [4]byte {
return [4]byte{it.Head, byte(it.SizeAndSpecial), byte(it.SizeAndSpecial >> 8), it.TypeData}
}

type ItemType uint8

// ItemType definitions.
const (
ItemTypeVectorTable ItemType = 0x03 // vector table
ItemTypeRollingWindowDelta ItemType = 0x05 // rolling window delta
ItemTypeRollingLoadMap ItemType = 0x06 // load map
ItemTypeSignature ItemType = 0x09 // signature
ItemTypePartitionTable ItemType = 0x0A // partition table
ItemTypeSalt ItemType = 0x0C // salt

ItemTypeNextBlockOffset ItemType = 0x41 // next block offset
ItemTypeImageDef ItemType = 0x42 // image def
ItemTypeEntryPoint ItemType = 0x44 // entry point
ItemTypeHashDef ItemType = 0x47 // hash def
ItemTypeVersion ItemType = 0x48 // version
ItemTypeHashValue ItemType = 0x4b // hash value
)

// ItemType definitions for 2 byte items.
const (
ItemTypeIgnored ItemType = 0x7e // ignored
ItemTypeLast ItemType = 0x7f // last
)

func (it Item) sizeflag() bool { return it.Head&1 != 0 }

// Size returns the size in bytes of the item.
func (it Item) Size() int {
return it.SizeWords() * 4
}

// SizeWords returns the size in words (4 bytes each) of the item.
func (it Item) SizeWords() int {
if it.sizeflag() {
return int(it.SizeAndSpecial)
}
return int(it.SizeAndSpecial & 0xff)
}

func (it Item) ItemType() ItemType { return ItemType(it.Head & 0x7f) }

func (it Item) String() string {
switch it.ItemType() {
case ItemTypeImageDef:
return ImageDef{Item: it}.String()
case ItemTypeRollingLoadMap:
return LoadMap{Item: it}.String()
}
return fmt.Sprintf("Item(%s, size=%d)", it.ItemType(), it.Size())
}

type Block struct {
// Items is a list of items not including the ItemTypeLast item.
Items []Item
Expand Down
138 changes: 116 additions & 22 deletions cmd/picobin/picobin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ package main

import (
"debug/elf"
"encoding/hex"
"errors"
"flag"
"fmt"
"log"
"os"

"github.com/github.com/soypat/picobin"
"golang.org/x/exp/constraints"
)

const (
Expand All @@ -16,6 +19,10 @@ const (
MB
)

type Flags struct {
block int
}

func main() {
err := run()
if err != nil {
Expand All @@ -24,52 +31,126 @@ func main() {
}

func run() error {
var flags Flags
flag.Usage = func() {
output := flag.CommandLine.Output()
fmt.Fprintf(output, "Usage of %s:\n", os.Args[0])
fmt.Fprintf(output, "\tavailable commands: [info, dump]\n")
fmt.Fprintf(output, "Example:\n\tpicobin info <ELF-filename>\n")
flag.PrintDefaults()
}
flag.IntVar(&flags.block, "block", -1, "Specify a single block to analyze")
flag.Parse()
filename := flag.Arg(0)
if filename == "" {
command := flag.Arg(0)
source := flag.Arg(1)
// First check command argument.
if command == "" {
flag.Usage()
return errors.New("missing command argument")
}
var cmd func(*elf.File, Flags) error
switch command {
case "info":
cmd = info
case "dump":
cmd = dump
default:
flag.Usage()
return errors.New("uknown command: " + command)
}

// Next check filename.
if source == "" {
flag.Usage()
return errors.New("missing filename argument")
}
file, err := elf.Open(filename)
file, err := elf.Open(source)
if err != nil {
return err
}
defer file.Close()

// Finally run command.
return cmd(file, flags)
}

func info(f *elf.File, flags Flags) error {
blocks, start, err := getBlocks(f)
if err != nil {
return err
}
addr := start
for i, block := range blocks {
if flags.block >= 0 && i != flags.block {
addr += int64(block.Link)
continue
}
fmt.Printf("BLOCK%d @ Addr=%#x Size=%d Items=%d\n", i, addr, block.Size(), len(block.Items))
for _, item := range block.Items {
fmt.Printf("\t%s\n", item.String())
}
addr += int64(block.Link)
}
return nil
}

func dump(f *elf.File, flags Flags) error {
blocks, start, err := getBlocks(f)
if err != nil {
return err
}
addr := start
for i, block := range blocks {
if flags.block >= 0 && i != flags.block || len(block.Items) >= 1 && block.Items[0].ItemType() == picobin.ItemTypeIgnored {
addr += int64(block.Link)
continue
}
blockSize := block.Size()
dataSize := block.Link - blockSize
if dataSize < 0 {
break // Last block.
}
data := make([]byte, dataSize)
n, err := readAddr(f, addr+int64(blockSize), data)
if err != nil {
return err
} else if n == 0 {
return errors.New("unable to extract data after block")
}
fmt.Printf("BLOCK%d @ Addr=%#x dump:\n%s", i, addr, hex.Dump(data))
addr += int64(block.Link)
}
return nil
}

func getBlocks(f *elf.File) ([]picobin.Block, int64, error) {
var flash [2 * MB]byte
const flashAddr = 0x10000000
flashEnd, err := readAddr(file, flashAddr, flash[:])
flashEnd, err := readAddr(f, flashAddr, flash[:])
if err != nil {
return err
return nil, 0, err
}
start0, _, err := picobin.NextBlockIdx(flash[:flashEnd])
if err != nil {
return err
return nil, 0, err
}
var blocks []picobin.Block
var blkaddr []int
start := start0
startAbs := int64(start) + flashAddr
for {
absAddr := start + flashAddr
block, _, err := picobin.DecodeBlock(flash[start:flashEnd])
if err != nil {
return fmt.Errorf("decoding block at Addr=%#x: %w", absAddr, err)
return blocks, startAbs, fmt.Errorf("decoding block at Addr=%#x: %w", absAddr, err)
}
blocks = append(blocks, block)
blkaddr = append(blkaddr, absAddr)
nextStart := start + block.Link
if nextStart == start0 {
break // Found last block.
}
start = nextStart
}
for i, block := range blocks {
addr := blkaddr[i]
fmt.Printf("BLOCK%d @ Addr=%#x Size=%d Items=%d\n", i, addr, block.Size(), len(block.Items))
for _, item := range block.Items {
fmt.Printf("\t%s\n", item.String())
}
}
return nil
return blocks, startAbs, nil
}

func readAddr(f *elf.File, addr int64, b []byte) (int, error) {
Expand All @@ -78,29 +159,42 @@ func readAddr(f *elf.File, addr int64, b []byte) (int, error) {
maxReadIdx := 0
for _, section := range f.Sections {
saddr := int64(section.Addr)
if saddr >= addr && saddr < end {
send := saddr + int64(section.Size)
if aliases(addr, end, saddr, send) {
data, err := section.Data()
if err != nil {
return 0, err
}
off := int(saddr - addr)
n := copy(b[off:], data)
maxReadIdx = max(maxReadIdx, n+off)
sectOff := max(0, int(addr-saddr))
bOff := max(0, int(saddr-addr))
n := copy(b[bOff:], data[sectOff:])
maxReadIdx = max(maxReadIdx, n+bOff)
}
}
return maxReadIdx, nil
}

func aliases(start0, end0, start1, end1 int64) bool {
return start0 < end1 && end0 > start1
}

func clear[E any](a []E) {
var z E
for i := range a {
a[i] = z
}
}

func max(a, b int) int {
func max[T constraints.Integer](a, b T) T {
if a > b {
return a
}
return b
}

func min[T constraints.Integer](a, b T) T {
if a < b {
return a
}
return b
}
14 changes: 14 additions & 0 deletions cmd/picobin/picobin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package main

import (
"debug/elf"
"testing"
)

func TestDump(t *testing.T) {
fp, err := elf.Open("../../blink.elf")
if err != nil {
t.Fatal(err)
}
dump(fp, Flags{block: 0})
}
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
module github.com/github.com/soypat/picobin

go 1.18
go 1.22.0

toolchain go1.23.0

require golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk=
golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY=
Loading

0 comments on commit 671fd32

Please sign in to comment.