symbol_inject.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  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. package symbol_inject
  15. import (
  16. "bytes"
  17. "encoding/binary"
  18. "fmt"
  19. "io"
  20. "math"
  21. )
  22. var maxUint64 uint64 = math.MaxUint64
  23. type cantParseError struct {
  24. error
  25. }
  26. func OpenFile(r io.ReaderAt) (*File, error) {
  27. file, err := elfSymbolsFromFile(r)
  28. if elfError, ok := err.(cantParseError); ok {
  29. // Try as a mach-o file
  30. file, err = machoSymbolsFromFile(r)
  31. if _, ok := err.(cantParseError); ok {
  32. // Try as a windows PE file
  33. file, err = peSymbolsFromFile(r)
  34. if _, ok := err.(cantParseError); ok {
  35. // Can't parse as elf, macho, or PE, return the elf error
  36. return nil, elfError
  37. }
  38. }
  39. }
  40. if err != nil {
  41. return nil, err
  42. }
  43. file.r = r
  44. return file, err
  45. }
  46. func InjectStringSymbol(file *File, w io.Writer, symbol, value, from string) error {
  47. offset, size, err := findSymbol(file, symbol)
  48. if err != nil {
  49. return err
  50. }
  51. if uint64(len(value))+1 > size {
  52. return fmt.Errorf("value length %d overflows symbol size %d", len(value), size)
  53. }
  54. if from != "" {
  55. // Read the exsting symbol contents and verify they match the expected value
  56. expected := make([]byte, size)
  57. existing := make([]byte, size)
  58. copy(expected, from)
  59. _, err := file.r.ReadAt(existing, int64(offset))
  60. if err != nil {
  61. return err
  62. }
  63. if bytes.Compare(existing, expected) != 0 {
  64. return fmt.Errorf("existing symbol contents %q did not match expected value %q",
  65. string(existing), string(expected))
  66. }
  67. }
  68. buf := make([]byte, size)
  69. copy(buf, value)
  70. return copyAndInject(file.r, w, offset, buf)
  71. }
  72. func InjectUint64Symbol(file *File, w io.Writer, symbol string, value uint64) error {
  73. offset, size, err := findSymbol(file, symbol)
  74. if err != nil {
  75. return err
  76. }
  77. if size != 8 {
  78. return fmt.Errorf("symbol %q is not a uint64, it is %d bytes long", symbol, size)
  79. }
  80. buf := make([]byte, 8)
  81. binary.LittleEndian.PutUint64(buf, value)
  82. return copyAndInject(file.r, w, offset, buf)
  83. }
  84. func copyAndInject(r io.ReaderAt, w io.Writer, offset uint64, buf []byte) (err error) {
  85. // Copy the first bytes up to the symbol offset
  86. _, err = io.Copy(w, io.NewSectionReader(r, 0, int64(offset)))
  87. // Write the injected value in the output file
  88. if err == nil {
  89. _, err = w.Write(buf)
  90. }
  91. // Write the remainder of the file
  92. pos := int64(offset) + int64(len(buf))
  93. if err == nil {
  94. _, err = io.Copy(w, io.NewSectionReader(r, pos, 1<<63-1-pos))
  95. }
  96. if err == io.EOF {
  97. err = io.ErrUnexpectedEOF
  98. }
  99. return err
  100. }
  101. func findSymbol(file *File, symbolName string) (uint64, uint64, error) {
  102. for i, symbol := range file.Symbols {
  103. if symbol.Name == symbolName {
  104. // Find the next symbol (n the same section with a higher address
  105. var n int
  106. for n = i; n < len(file.Symbols); n++ {
  107. if file.Symbols[n].Section != symbol.Section {
  108. n = len(file.Symbols)
  109. break
  110. }
  111. if file.Symbols[n].Addr > symbol.Addr {
  112. break
  113. }
  114. }
  115. size := symbol.Size
  116. if size == 0 {
  117. var end uint64
  118. if n < len(file.Symbols) {
  119. end = file.Symbols[n].Addr
  120. } else {
  121. end = symbol.Section.Size
  122. }
  123. if end <= symbol.Addr || end > symbol.Addr+4096 {
  124. return maxUint64, maxUint64, fmt.Errorf("symbol end address does not seem valid, %x:%x", symbol.Addr, end)
  125. }
  126. size = end - symbol.Addr
  127. }
  128. offset := symbol.Section.Offset + symbol.Addr
  129. return uint64(offset), uint64(size), nil
  130. }
  131. }
  132. return maxUint64, maxUint64, fmt.Errorf("symbol not found")
  133. }
  134. type File struct {
  135. r io.ReaderAt
  136. Symbols []*Symbol
  137. Sections []*Section
  138. IsMachoFile bool
  139. }
  140. type Symbol struct {
  141. Name string
  142. Addr uint64 // Address of the symbol inside the section.
  143. Size uint64 // Size of the symbol, if known.
  144. Section *Section
  145. }
  146. type Section struct {
  147. Name string
  148. Addr uint64 // Virtual address of the start of the section.
  149. Offset uint64 // Offset into the file of the start of the section.
  150. Size uint64
  151. }
  152. func DumpSymbols(r io.ReaderAt) error {
  153. err := dumpElfSymbols(r)
  154. if elfError, ok := err.(cantParseError); ok {
  155. // Try as a mach-o file
  156. err = dumpMachoSymbols(r)
  157. if _, ok := err.(cantParseError); ok {
  158. // Try as a windows PE file
  159. err = dumpPESymbols(r)
  160. if _, ok := err.(cantParseError); ok {
  161. // Can't parse as elf, macho, or PE, return the elf error
  162. return elfError
  163. }
  164. }
  165. }
  166. return err
  167. }