@@ -2,8 +2,10 @@ package main
2
2
3
3
import (
4
4
"debug/elf"
5
+ "io/ioutil"
5
6
"os"
6
7
"path/filepath"
8
+ "sort"
7
9
8
10
"github.com/marcinbor85/gohex"
9
11
)
@@ -21,24 +23,72 @@ func (e ObjcopyError) Error() string {
21
23
return e .Op + ": " + e .Err .Error ()
22
24
}
23
25
24
- // ExtractTextSegment returns the .text segment and the first address from the
25
- // ELF file in the given path.
26
- func ExtractTextSegment (path string ) (uint64 , []byte , error ) {
26
+ type ProgSlice []* elf.Prog
27
+
28
+ func (s ProgSlice ) Len () int { return len (s ) }
29
+ func (s ProgSlice ) Less (i , j int ) bool { return s [i ].Paddr < s [j ].Paddr }
30
+ func (s ProgSlice ) Swap (i , j int ) { s [i ], s [j ] = s [j ], s [i ] }
31
+
32
+ // ExtractROM extracts a firmware image and the first load address from the
33
+ // given ELF file. It tries to emulate the behavior of objcopy.
34
+ func ExtractROM (path string ) (uint64 , []byte , error ) {
27
35
f , err := elf .Open (path )
28
36
if err != nil {
29
37
return 0 , nil , ObjcopyError {"failed to open ELF file to extract text segment" , err }
30
38
}
31
39
defer f .Close ()
32
40
33
- text := f .Section (".text" )
34
- if text == nil {
35
- return 0 , nil , ObjcopyError {"file does not contain .text segment: " + path , nil }
41
+ // The GNU objcopy command does the following for firmware extraction (from
42
+ // the man page):
43
+ // > When objcopy generates a raw binary file, it will essentially produce a
44
+ // > memory dump of the contents of the input object file. All symbols and
45
+ // > relocation information will be discarded. The memory dump will start at
46
+ // > the load address of the lowest section copied into the output file.
47
+
48
+ // Find the lowest section address.
49
+ startAddr := ^ uint64 (0 )
50
+ for _ , section := range f .Sections {
51
+ if section .Type != elf .SHT_PROGBITS || section .Flags & elf .SHF_ALLOC == 0 {
52
+ continue
53
+ }
54
+ if section .Addr < startAddr {
55
+ startAddr = section .Addr
56
+ }
36
57
}
37
- data , err := text .Data ()
38
- if err != nil {
39
- return 0 , nil , ObjcopyError {"failed to extract .text segment from ELF file" , err }
58
+
59
+ progs := make (ProgSlice , 0 , 2 )
60
+ for _ , prog := range f .Progs {
61
+ if prog .Type != elf .PT_LOAD || prog .Filesz == 0 {
62
+ continue
63
+ }
64
+ progs = append (progs , prog )
65
+ }
66
+ if len (progs ) == 0 {
67
+ return 0 , nil , ObjcopyError {"file does not contain ROM segments: " + path , nil }
68
+ }
69
+ sort .Sort (progs )
70
+
71
+ var rom []byte
72
+ for _ , prog := range progs {
73
+ if prog .Paddr != progs [0 ].Paddr + uint64 (len (rom )) {
74
+ return 0 , nil , ObjcopyError {"ROM segments are non-contiguous: " + path , nil }
75
+ }
76
+ data , err := ioutil .ReadAll (prog .Open ())
77
+ if err != nil {
78
+ return 0 , nil , ObjcopyError {"failed to extract segment from ELF file: " + path , err }
79
+ }
80
+ rom = append (rom , data ... )
81
+ }
82
+ if progs [0 ].Paddr < startAddr {
83
+ // The lowest memory address is before the first section. This means
84
+ // that there is some extra data loaded at the start of the image that
85
+ // should be discarded.
86
+ // Example: ELF files where .text doesn't start at address 0 because
87
+ // there is a bootloader at the start.
88
+ return startAddr , rom [startAddr - progs [0 ].Paddr :], nil
89
+ } else {
90
+ return progs [0 ].Paddr , rom , nil
40
91
}
41
- return text .Addr , data , nil
42
92
}
43
93
44
94
// Objcopy converts an ELF file to a different (simpler) output file format:
@@ -51,7 +101,7 @@ func Objcopy(infile, outfile string) error {
51
101
defer f .Close ()
52
102
53
103
// Read the .text segment.
54
- addr , data , err := ExtractTextSegment (infile )
104
+ addr , data , err := ExtractROM (infile )
55
105
if err != nil {
56
106
return err
57
107
}
@@ -65,12 +115,11 @@ func Objcopy(infile, outfile string) error {
65
115
return err
66
116
case ".hex" :
67
117
mem := gohex .NewMemory ()
68
- mem .SetStartAddress (uint32 (addr )) // ignored in most cases (Intel-specific)
69
118
err := mem .AddBinary (uint32 (addr ), data )
70
119
if err != nil {
71
120
return ObjcopyError {"failed to create .hex file" , err }
72
121
}
73
- mem .DumpIntelHex (f , 32 ) // TODO: handle error
122
+ mem .DumpIntelHex (f , 16 ) // TODO: handle error
74
123
return nil
75
124
default :
76
125
panic ("unreachable" )
0 commit comments