Skip to content
Permalink
Browse files

main: include .data section in .hex file

The function extracting the firmware image for .hex and .bin files
wasn't working correctly: it only extracted the .text segment and not
the .data segment.
This commit fixes this issue, so that it behaves (hopefully) just like
objcopy -O{ihex|binary}.

Another small change is that the formatting of the .hex file was made
more like the output of objcopy: no entry addres (old Intel CPU
holdover) and 16 bytes of data on each line.
  • Loading branch information...
aykevl authored and deadprogram committed Mar 11, 2019
1 parent b1744db commit a466dd8f2bae702f443cc49aee3f4c3f26878d4b
Showing with 63 additions and 14 deletions.
  1. +62 −13 objcopy.go
  2. +1 −1 uf2.go
@@ -2,8 +2,10 @@ package main

import (
"debug/elf"
"io/ioutil"
"os"
"path/filepath"
"sort"

"github.com/marcinbor85/gohex"
)
@@ -21,24 +23,72 @@ func (e ObjcopyError) Error() string {
return e.Op + ": " + e.Err.Error()
}

// ExtractTextSegment returns the .text segment and the first address from the
// ELF file in the given path.
func ExtractTextSegment(path string) (uint64, []byte, error) {
type ProgSlice []*elf.Prog

func (s ProgSlice) Len() int { return len(s) }
func (s ProgSlice) Less(i, j int) bool { return s[i].Paddr < s[j].Paddr }
func (s ProgSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

// ExtractROM extracts a firmware image and the first load address from the
// given ELF file. It tries to emulate the behavior of objcopy.
func ExtractROM(path string) (uint64, []byte, error) {
f, err := elf.Open(path)
if err != nil {
return 0, nil, ObjcopyError{"failed to open ELF file to extract text segment", err}
}
defer f.Close()

text := f.Section(".text")
if text == nil {
return 0, nil, ObjcopyError{"file does not contain .text segment: " + path, nil}
// The GNU objcopy command does the following for firmware extraction (from
// the man page):
// > When objcopy generates a raw binary file, it will essentially produce a
// > memory dump of the contents of the input object file. All symbols and
// > relocation information will be discarded. The memory dump will start at
// > the load address of the lowest section copied into the output file.

// Find the lowest section address.
startAddr := ^uint64(0)
for _, section := range f.Sections {
if section.Type != elf.SHT_PROGBITS || section.Flags&elf.SHF_ALLOC == 0 {
continue
}
if section.Addr < startAddr {
startAddr = section.Addr
}
}
data, err := text.Data()
if err != nil {
return 0, nil, ObjcopyError{"failed to extract .text segment from ELF file", err}

progs := make(ProgSlice, 0, 2)
for _, prog := range f.Progs {
if prog.Type != elf.PT_LOAD || prog.Filesz == 0 {
continue
}
progs = append(progs, prog)
}
if len(progs) == 0 {
return 0, nil, ObjcopyError{"file does not contain ROM segments: " + path, nil}
}
sort.Sort(progs)

var rom []byte
for _, prog := range progs {
if prog.Paddr != progs[0].Paddr+uint64(len(rom)) {
return 0, nil, ObjcopyError{"ROM segments are non-contiguous: " + path, nil}
}
data, err := ioutil.ReadAll(prog.Open())
if err != nil {
return 0, nil, ObjcopyError{"failed to extract segment from ELF file: " + path, err}
}
rom = append(rom, data...)
}
if progs[0].Paddr < startAddr {
// The lowest memory address is before the first section. This means
// that there is some extra data loaded at the start of the image that
// should be discarded.
// Example: ELF files where .text doesn't start at address 0 because
// there is a bootloader at the start.
return startAddr, rom[startAddr-progs[0].Paddr:], nil
} else {
return progs[0].Paddr, rom, nil
}
return text.Addr, data, nil
}

// Objcopy converts an ELF file to a different (simpler) output file format:
@@ -51,7 +101,7 @@ func Objcopy(infile, outfile string) error {
defer f.Close()

// Read the .text segment.
addr, data, err := ExtractTextSegment(infile)
addr, data, err := ExtractROM(infile)
if err != nil {
return err
}
@@ -65,12 +115,11 @@ func Objcopy(infile, outfile string) error {
return err
case ".hex":
mem := gohex.NewMemory()
mem.SetStartAddress(uint32(addr)) // ignored in most cases (Intel-specific)
err := mem.AddBinary(uint32(addr), data)
if err != nil {
return ObjcopyError{"failed to create .hex file", err}
}
mem.DumpIntelHex(f, 32) // TODO: handle error
mem.DumpIntelHex(f, 16) // TODO: handle error
return nil
default:
panic("unreachable")
2 uf2.go
@@ -15,7 +15,7 @@ import (
// ConvertELFFileToUF2File converts an ELF file to a UF2 file.
func ConvertELFFileToUF2File(infile, outfile string) error {
// Read the .text segment.
_, data, err := ExtractTextSegment(infile)
_, data, err := ExtractROM(infile)
if err != nil {
return err
}

0 comments on commit a466dd8

Please sign in to comment.
You can’t perform that action at this time.