blob: d0f01c536e154f8c55c2470ccd435eca4095e60b [file] [log] [blame]
Colin Cross5498f852018-01-03 23:39:54 -08001// Copyright 2018 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package main
16
17import (
18 "bytes"
19 "flag"
20 "fmt"
21 "io"
22 "math"
23 "os"
24)
25
26var (
27 input = flag.String("i", "", "input file")
28 output = flag.String("o", "", "output file")
29 symbol = flag.String("s", "", "symbol to inject into")
30 from = flag.String("from", "", "optional existing value of the symbol for verification")
31 value = flag.String("v", "", "value to inject into symbol")
Colin Cross8673b5b2018-03-01 11:20:25 -080032
33 dump = flag.Bool("dump", false, "dump the symbol table for copying into a test")
Colin Cross5498f852018-01-03 23:39:54 -080034)
35
36var maxUint64 uint64 = math.MaxUint64
37
38type cantParseError struct {
39 error
40}
41
42func main() {
43 flag.Parse()
44
45 usageError := func(s string) {
46 fmt.Fprintln(os.Stderr, s)
47 flag.Usage()
48 os.Exit(1)
49 }
50
51 if *input == "" {
52 usageError("-i is required")
53 }
54
Colin Cross8673b5b2018-03-01 11:20:25 -080055 if !*dump {
56 if *output == "" {
57 usageError("-o is required")
58 }
Colin Cross5498f852018-01-03 23:39:54 -080059
Colin Cross8673b5b2018-03-01 11:20:25 -080060 if *symbol == "" {
61 usageError("-s is required")
62 }
Colin Cross5498f852018-01-03 23:39:54 -080063
Colin Cross8673b5b2018-03-01 11:20:25 -080064 if *value == "" {
65 usageError("-v is required")
66 }
Colin Cross5498f852018-01-03 23:39:54 -080067 }
68
69 r, err := os.Open(*input)
70 if err != nil {
71 fmt.Fprintln(os.Stderr, err.Error())
72 os.Exit(2)
73 }
74 defer r.Close()
75
Colin Cross8673b5b2018-03-01 11:20:25 -080076 if *dump {
77 err := dumpSymbols(r)
78 if err != nil {
79 fmt.Fprintln(os.Stderr, err.Error())
80 os.Exit(6)
81 }
82 return
83 }
84
Colin Cross5498f852018-01-03 23:39:54 -080085 w, err := os.OpenFile(*output, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0777)
86 if err != nil {
87 fmt.Fprintln(os.Stderr, err.Error())
88 os.Exit(3)
89 }
90 defer w.Close()
91
Colin Cross8673b5b2018-03-01 11:20:25 -080092 file, err := openFile(r)
93 if err != nil {
94 fmt.Fprintln(os.Stderr, err.Error())
95 os.Exit(4)
96 }
97
98 err = injectSymbol(file, w, *symbol, *value, *from)
Colin Cross5498f852018-01-03 23:39:54 -080099 if err != nil {
100 fmt.Fprintln(os.Stderr, err.Error())
101 os.Remove(*output)
Colin Cross8673b5b2018-03-01 11:20:25 -0800102 os.Exit(5)
Colin Cross5498f852018-01-03 23:39:54 -0800103 }
104}
105
Colin Cross8673b5b2018-03-01 11:20:25 -0800106func openFile(r io.ReaderAt) (*File, error) {
107 file, err := elfSymbolsFromFile(r)
Colin Cross5498f852018-01-03 23:39:54 -0800108 if elfError, ok := err.(cantParseError); ok {
109 // Try as a mach-o file
Colin Cross8673b5b2018-03-01 11:20:25 -0800110 file, err = machoSymbolsFromFile(r)
Colin Cross5498f852018-01-03 23:39:54 -0800111 if _, ok := err.(cantParseError); ok {
112 // Try as a windows PE file
Colin Cross8673b5b2018-03-01 11:20:25 -0800113 file, err = peSymbolsFromFile(r)
Colin Cross5498f852018-01-03 23:39:54 -0800114 if _, ok := err.(cantParseError); ok {
115 // Can't parse as elf, macho, or PE, return the elf error
Colin Cross8673b5b2018-03-01 11:20:25 -0800116 return nil, elfError
Colin Cross5498f852018-01-03 23:39:54 -0800117 }
118 }
119 }
120 if err != nil {
Colin Cross8673b5b2018-03-01 11:20:25 -0800121 return nil, err
122 }
123
124 file.r = r
125
126 return file, err
127}
128
129func injectSymbol(file *File, w io.Writer, symbol, value, from string) error {
130 offset, size, err := findSymbol(file, symbol)
131 if err != nil {
Colin Cross5498f852018-01-03 23:39:54 -0800132 return err
133 }
134
135 if uint64(len(value))+1 > size {
136 return fmt.Errorf("value length %d overflows symbol size %d", len(value), size)
137 }
138
139 if from != "" {
140 // Read the exsting symbol contents and verify they match the expected value
141 expected := make([]byte, size)
142 existing := make([]byte, size)
143 copy(expected, from)
Colin Cross8673b5b2018-03-01 11:20:25 -0800144 _, err := file.r.ReadAt(existing, int64(offset))
Colin Cross5498f852018-01-03 23:39:54 -0800145 if err != nil {
146 return err
147 }
148 if bytes.Compare(existing, expected) != 0 {
149 return fmt.Errorf("existing symbol contents %q did not match expected value %q",
150 string(existing), string(expected))
151 }
152 }
153
Colin Cross8673b5b2018-03-01 11:20:25 -0800154 return copyAndInject(file.r, w, offset, size, value)
Colin Cross5498f852018-01-03 23:39:54 -0800155}
156
Colin Cross8673b5b2018-03-01 11:20:25 -0800157func copyAndInject(r io.ReaderAt, w io.Writer, offset, size uint64, value string) (err error) {
Colin Cross5498f852018-01-03 23:39:54 -0800158 buf := make([]byte, size)
159 copy(buf, value)
160
Colin Cross5498f852018-01-03 23:39:54 -0800161 // Copy the first bytes up to the symbol offset
Colin Cross8673b5b2018-03-01 11:20:25 -0800162 _, err = io.Copy(w, io.NewSectionReader(r, 0, int64(offset)))
Colin Cross5498f852018-01-03 23:39:54 -0800163
Colin Cross8673b5b2018-03-01 11:20:25 -0800164 // Write the injected value in the output file
165 if err == nil {
166 _, err = w.Write(buf)
167 }
168
169 // Write the remainder of the file
170 pos := int64(offset + size)
171 if err == nil {
172 _, err = io.Copy(w, io.NewSectionReader(r, pos, 1<<63-1-pos))
173 }
174
175 if err == io.EOF {
176 err = io.ErrUnexpectedEOF
177 }
178
179 return err
180}
181
182func findSymbol(file *File, symbolName string) (uint64, uint64, error) {
183 for i, symbol := range file.Symbols {
184 if symbol.Name == symbolName {
185 // Find the next symbol (n the same section with a higher address
186 var n int
187 for n = i; n < len(file.Symbols); n++ {
188 if file.Symbols[n].Section != symbol.Section {
189 n = len(file.Symbols)
190 break
191 }
192 if file.Symbols[n].Addr > symbol.Addr {
193 break
194 }
195 }
196
197 size := symbol.Size
198 if size == 0 {
199 var end uint64
200 if n < len(file.Symbols) {
201 end = file.Symbols[n].Addr
202 } else {
203 end = symbol.Section.Size
204 }
205
206 if end <= symbol.Addr || end > symbol.Addr+4096 {
207 return maxUint64, maxUint64, fmt.Errorf("symbol end address does not seem valid, %x:%x", symbol.Addr, end)
208 }
209
210 size = end - symbol.Addr
211 }
212
213 offset := symbol.Section.Offset + symbol.Addr
214
215 return uint64(offset), uint64(size), nil
216 }
217 }
218
219 return maxUint64, maxUint64, fmt.Errorf("symbol not found")
220}
221
222type File struct {
223 r io.ReaderAt
224 Symbols []*Symbol
225 Sections []*Section
226}
227
228type Symbol struct {
229 Name string
230 Addr uint64 // Address of the symbol inside the section.
231 Size uint64 // Size of the symbol, if known.
232 Section *Section
233}
234
235type Section struct {
236 Name string
237 Addr uint64 // Virtual address of the start of the section.
238 Offset uint64 // Offset into the file of the start of the section.
239 Size uint64
240}
241
242func dumpSymbols(r io.ReaderAt) error {
243 err := dumpElfSymbols(r)
244 if elfError, ok := err.(cantParseError); ok {
245 // Try as a mach-o file
246 err = dumpMachoSymbols(r)
247 if _, ok := err.(cantParseError); ok {
248 // Try as a windows PE file
249 err = dumpPESymbols(r)
250 if _, ok := err.(cantParseError); ok {
251 // Can't parse as elf, macho, or PE, return the elf error
252 return elfError
253 }
254 }
255 }
256 return err
Colin Cross5498f852018-01-03 23:39:54 -0800257}