mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-29 08:08:42 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5569cd1b6b | |||
| d6c2d6e301 | |||
| a466dd8f2b |
@@ -1,3 +1,9 @@
|
||||
0.4.1
|
||||
---
|
||||
- **compiler**
|
||||
- fix `objcopy` replacement to include the .data section in the firmware image
|
||||
- use `llvm-ar-7` on Linux to fix the Docker image
|
||||
|
||||
0.4.0
|
||||
---
|
||||
- **compiler**
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// +build !darwin
|
||||
|
||||
package main
|
||||
|
||||
// commands used by the compilation process might have different file names on Linux than those used on macOS.
|
||||
var commands = map[string]string{
|
||||
"ar": "llvm-ar-7",
|
||||
"clang": "clang-7",
|
||||
"ld.lld": "ld.lld-7",
|
||||
"wasm-ld": "wasm-ld-7",
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// +build darwin
|
||||
|
||||
package main
|
||||
|
||||
// commands used by the compilation process might have different file names on macOS than those used on Linux.
|
||||
var commands = map[string]string{
|
||||
"ar": "llvm-ar",
|
||||
"clang": "clang-7",
|
||||
"ld.lld": "ld.lld-7",
|
||||
"wasm-ld": "wasm-ld-7",
|
||||
}
|
||||
@@ -21,13 +21,6 @@ import (
|
||||
"github.com/tinygo-org/tinygo/loader"
|
||||
)
|
||||
|
||||
var commands = map[string]string{
|
||||
"ar": "llvm-ar",
|
||||
"clang": "clang-7",
|
||||
"ld.lld": "ld.lld-7",
|
||||
"wasm-ld": "wasm-ld-7",
|
||||
}
|
||||
|
||||
// commandError is an error type to wrap os/exec.Command errors. This provides
|
||||
// some more information regarding what went wrong while running a command.
|
||||
type commandError struct {
|
||||
|
||||
+62
-13
@@ -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")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,4 +2,4 @@ package main
|
||||
|
||||
// version of this package.
|
||||
// Update this value before release of new version of software.
|
||||
const version = "0.4.0"
|
||||
const version = "0.4.1"
|
||||
|
||||
Reference in New Issue
Block a user