aboutsummaryrefslogtreecommitdiffhomepage
path: root/objcopy.go
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2019-02-23 19:41:30 +0100
committerRon Evans <[email protected]>2019-03-04 21:17:56 +0100
commit4f932b6e669eece19b2adcbb690650a668a67fad (patch)
tree55d308ffaefba501006b5737b81bf6846b585cc6 /objcopy.go
parent3538ba943c7abfc6933b72307d1bc24cf581d488 (diff)
downloadtinygo-4f932b6e669eece19b2adcbb690650a668a67fad.tar.gz
tinygo-4f932b6e669eece19b2adcbb690650a668a67fad.zip
all: use internal objcopy implementation
This lessens the dependency on binutils (e.g. arm-none-eabi-objcopy).
Diffstat (limited to 'objcopy.go')
-rw-r--r--objcopy.go78
1 files changed, 78 insertions, 0 deletions
diff --git a/objcopy.go b/objcopy.go
new file mode 100644
index 000000000..d67bcab57
--- /dev/null
+++ b/objcopy.go
@@ -0,0 +1,78 @@
+package main
+
+import (
+ "debug/elf"
+ "os"
+ "path/filepath"
+
+ "github.com/marcinbor85/gohex"
+)
+
+// ObjcopyError is an error returned by functions that act like objcopy.
+type ObjcopyError struct {
+ Op string
+ Err error
+}
+
+func (e ObjcopyError) Error() string {
+ if e.Err == nil {
+ return e.Op
+ }
+ 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) {
+ 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}
+ }
+ data, err := text.Data()
+ if err != nil {
+ return 0, nil, ObjcopyError{"failed to extract .text segment from ELF file", err}
+ }
+ return text.Addr, data, nil
+}
+
+// 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 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 := ExtractTextSegment(infile)
+ if err != nil {
+ return err
+ }
+
+ // Write to the file, in the correct format.
+ switch filepath.Ext(outfile) {
+ case ".bin":
+ // The address is not stored in a .bin file (therefore you
+ // should use .hex files in most cases).
+ _, err := f.Write(data)
+ 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
+ return nil
+ default:
+ panic("unreachable")
+ }
+}