feat: support generic bin and intel hex firmware

This commit is contained in:
Li Jie
2025-09-07 14:56:39 +08:00
parent df07513a37
commit 9eeb14ae31
4 changed files with 53 additions and 39 deletions

View File

@@ -2,7 +2,6 @@ package firmware
import (
"fmt"
"io"
"os"
"strings"
)
@@ -35,9 +34,14 @@ func ExtractFileFormatFromCommand(cmd string) string {
// ConvertFormats processes format conversions for embedded targets only
func ConvertFormats(binFmt, fmtDetail string, envMap map[string]string) error {
var err error
// Convert to bin format first (needed for img)
if envMap["bin"] != "" {
err := makeFirmwareImage(envMap["out"], envMap["bin"], binFmt, fmtDetail)
if strings.HasPrefix(binFmt, "esp") {
err = makeFirmwareImage(envMap["out"], envMap["bin"], binFmt, fmtDetail)
} else {
err = objcopy(envMap["out"], envMap["bin"], "bin")
}
if err != nil {
return fmt.Errorf("failed to convert to bin format: %w", err)
}
@@ -45,7 +49,7 @@ func ConvertFormats(binFmt, fmtDetail string, envMap map[string]string) error {
// Convert to hex format
if envMap["hex"] != "" {
err := makeFirmwareImage(envMap["out"], envMap["hex"], binFmt, fmtDetail)
err := objcopy(envMap["out"], envMap["hex"], "hex")
if err != nil {
return fmt.Errorf("failed to convert to hex format: %w", err)
}
@@ -53,7 +57,7 @@ func ConvertFormats(binFmt, fmtDetail string, envMap map[string]string) error {
// Convert to img format
if envMap["img"] != "" {
err := makeFirmwareImage(envMap["out"], envMap["img"], binFmt+"-img", fmtDetail)
err = makeFirmwareImage(envMap["out"], envMap["img"], binFmt+"-img", fmtDetail)
if err != nil {
return fmt.Errorf("failed to convert to img format: %w", err)
}
@@ -77,38 +81,3 @@ func ConvertFormats(binFmt, fmtDetail string, envMap map[string]string) error {
return nil
}
// convertToHex converts binary file to hex format (each byte as two hex characters)
func convertToHex(infile, outfile string) error {
srcFile, err := os.Open(infile)
if err != nil {
return err
}
defer srcFile.Close()
dstFile, err := os.Create(outfile)
if err != nil {
return err
}
defer dstFile.Close()
// Read input file and convert each byte to two hex characters
buf := make([]byte, 4096) // Read in chunks
for {
n, err := srcFile.Read(buf)
if n > 0 {
for i := 0; i < n; i++ {
if _, writeErr := fmt.Fprintf(dstFile, "%02x", buf[i]); writeErr != nil {
return writeErr
}
}
}
if err == io.EOF {
break
}
if err != nil {
return err
}
}
return nil
}

View File

@@ -4,8 +4,12 @@ package firmware
import (
"debug/elf"
"fmt"
"io"
"os"
"sort"
"github.com/marcinbor85/gohex"
)
// maxPadBytes is the maximum allowed bytes to be padded in a rom extraction
@@ -103,3 +107,41 @@ func extractROM(path string) (uint64, []byte, error) {
return progs[0].Paddr, rom, nil
}
}
// From tinygo/builder/builder/objcopy.go objcopy
// objcopy converts an ELF file to a different (simpler) output file format:
// .bin or .hex. It extracts only the .text section.
func objcopy(infile, outfile, binaryFormat string) error {
f, err := os.OpenFile(outfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666)
if err != nil {
return err
}
defer f.Close()
// Read the .text segment.
addr, data, err := extractROM(infile)
if err != nil {
return err
}
// Write to the file, in the correct format.
switch binaryFormat {
case "hex":
fmt.Fprintf(os.Stderr, "Converting firmware format: %s -> %s (intel hex format: %s)\n", infile, outfile, binaryFormat)
// Intel hex file, includes the firmware start address.
mem := gohex.NewMemory()
err := mem.AddBinary(uint32(addr), data)
if err != nil {
return objcopyError{"failed to create .hex file", err}
}
return mem.DumpIntelHex(f, 16)
case "bin":
fmt.Fprintf(os.Stderr, "Converting firmware format: %s -> %s (format: %s)\n", infile, outfile, binaryFormat)
// The start address is not stored in raw firmware files (therefore you
// should use .hex files in most cases).
_, err := f.Write(data)
return err
default:
panic("unreachable")
}
}