github.com/westcoastroms/westcoastroms-build@v0.0.0-20190928114312-2350e5a73030/build/soong/cmd/symbol_inject/symbol_inject.go (about) 1 // 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 15 package main 16 17 import ( 18 "bytes" 19 "flag" 20 "fmt" 21 "io" 22 "math" 23 "os" 24 ) 25 26 var ( 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") 32 33 dump = flag.Bool("dump", false, "dump the symbol table for copying into a test") 34 ) 35 36 var maxUint64 uint64 = math.MaxUint64 37 38 type cantParseError struct { 39 error 40 } 41 42 func 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 55 if !*dump { 56 if *output == "" { 57 usageError("-o is required") 58 } 59 60 if *symbol == "" { 61 usageError("-s is required") 62 } 63 64 if *value == "" { 65 usageError("-v is required") 66 } 67 } 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 76 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 85 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 92 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) 99 if err != nil { 100 fmt.Fprintln(os.Stderr, err.Error()) 101 os.Remove(*output) 102 os.Exit(5) 103 } 104 } 105 106 func openFile(r io.ReaderAt) (*File, error) { 107 file, err := elfSymbolsFromFile(r) 108 if elfError, ok := err.(cantParseError); ok { 109 // Try as a mach-o file 110 file, err = machoSymbolsFromFile(r) 111 if _, ok := err.(cantParseError); ok { 112 // Try as a windows PE file 113 file, err = peSymbolsFromFile(r) 114 if _, ok := err.(cantParseError); ok { 115 // Can't parse as elf, macho, or PE, return the elf error 116 return nil, elfError 117 } 118 } 119 } 120 if err != nil { 121 return nil, err 122 } 123 124 file.r = r 125 126 return file, err 127 } 128 129 func injectSymbol(file *File, w io.Writer, symbol, value, from string) error { 130 offset, size, err := findSymbol(file, symbol) 131 if err != nil { 132 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) 144 _, err := file.r.ReadAt(existing, int64(offset)) 145 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 154 return copyAndInject(file.r, w, offset, size, value) 155 } 156 157 func copyAndInject(r io.ReaderAt, w io.Writer, offset, size uint64, value string) (err error) { 158 buf := make([]byte, size) 159 copy(buf, value) 160 161 // Copy the first bytes up to the symbol offset 162 _, err = io.Copy(w, io.NewSectionReader(r, 0, int64(offset))) 163 164 // 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 182 func 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 222 type File struct { 223 r io.ReaderAt 224 Symbols []*Symbol 225 Sections []*Section 226 } 227 228 type 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 235 type 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 242 func 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 257 }