blob: ebf05c871c337f3abe4a6ef26b23b219e82f3651 [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
Dan Willemsen2249dc82018-10-15 00:35:59 -070015package symbol_inject
Colin Cross5498f852018-01-03 23:39:54 -080016
17import (
18 "bytes"
Colin Cross5498f852018-01-03 23:39:54 -080019 "fmt"
20 "io"
21 "math"
Colin Cross5498f852018-01-03 23:39:54 -080022)
23
24var maxUint64 uint64 = math.MaxUint64
25
26type cantParseError struct {
27 error
28}
29
Dan Willemsen2249dc82018-10-15 00:35:59 -070030func OpenFile(r io.ReaderAt) (*File, error) {
Colin Cross8673b5b2018-03-01 11:20:25 -080031 file, err := elfSymbolsFromFile(r)
Colin Cross5498f852018-01-03 23:39:54 -080032 if elfError, ok := err.(cantParseError); ok {
33 // Try as a mach-o file
Colin Cross8673b5b2018-03-01 11:20:25 -080034 file, err = machoSymbolsFromFile(r)
Colin Cross5498f852018-01-03 23:39:54 -080035 if _, ok := err.(cantParseError); ok {
36 // Try as a windows PE file
Colin Cross8673b5b2018-03-01 11:20:25 -080037 file, err = peSymbolsFromFile(r)
Colin Cross5498f852018-01-03 23:39:54 -080038 if _, ok := err.(cantParseError); ok {
39 // Can't parse as elf, macho, or PE, return the elf error
Colin Cross8673b5b2018-03-01 11:20:25 -080040 return nil, elfError
Colin Cross5498f852018-01-03 23:39:54 -080041 }
42 }
43 }
44 if err != nil {
Colin Cross8673b5b2018-03-01 11:20:25 -080045 return nil, err
46 }
47
48 file.r = r
49
50 return file, err
51}
52
Dan Willemsen2249dc82018-10-15 00:35:59 -070053func InjectSymbol(file *File, w io.Writer, symbol, value, from string) error {
Colin Cross8673b5b2018-03-01 11:20:25 -080054 offset, size, err := findSymbol(file, symbol)
55 if err != nil {
Colin Cross5498f852018-01-03 23:39:54 -080056 return err
57 }
58
59 if uint64(len(value))+1 > size {
60 return fmt.Errorf("value length %d overflows symbol size %d", len(value), size)
61 }
62
63 if from != "" {
64 // Read the exsting symbol contents and verify they match the expected value
65 expected := make([]byte, size)
66 existing := make([]byte, size)
67 copy(expected, from)
Colin Cross8673b5b2018-03-01 11:20:25 -080068 _, err := file.r.ReadAt(existing, int64(offset))
Colin Cross5498f852018-01-03 23:39:54 -080069 if err != nil {
70 return err
71 }
72 if bytes.Compare(existing, expected) != 0 {
73 return fmt.Errorf("existing symbol contents %q did not match expected value %q",
74 string(existing), string(expected))
75 }
76 }
77
Colin Cross8673b5b2018-03-01 11:20:25 -080078 return copyAndInject(file.r, w, offset, size, value)
Colin Cross5498f852018-01-03 23:39:54 -080079}
80
Colin Cross8673b5b2018-03-01 11:20:25 -080081func copyAndInject(r io.ReaderAt, w io.Writer, offset, size uint64, value string) (err error) {
Colin Cross5498f852018-01-03 23:39:54 -080082 buf := make([]byte, size)
83 copy(buf, value)
84
Colin Cross5498f852018-01-03 23:39:54 -080085 // Copy the first bytes up to the symbol offset
Colin Cross8673b5b2018-03-01 11:20:25 -080086 _, err = io.Copy(w, io.NewSectionReader(r, 0, int64(offset)))
Colin Cross5498f852018-01-03 23:39:54 -080087
Colin Cross8673b5b2018-03-01 11:20:25 -080088 // Write the injected value in the output file
89 if err == nil {
90 _, err = w.Write(buf)
91 }
92
93 // Write the remainder of the file
94 pos := int64(offset + size)
95 if err == nil {
96 _, err = io.Copy(w, io.NewSectionReader(r, pos, 1<<63-1-pos))
97 }
98
99 if err == io.EOF {
100 err = io.ErrUnexpectedEOF
101 }
102
103 return err
104}
105
106func findSymbol(file *File, symbolName string) (uint64, uint64, error) {
107 for i, symbol := range file.Symbols {
108 if symbol.Name == symbolName {
109 // Find the next symbol (n the same section with a higher address
110 var n int
111 for n = i; n < len(file.Symbols); n++ {
112 if file.Symbols[n].Section != symbol.Section {
113 n = len(file.Symbols)
114 break
115 }
116 if file.Symbols[n].Addr > symbol.Addr {
117 break
118 }
119 }
120
121 size := symbol.Size
122 if size == 0 {
123 var end uint64
124 if n < len(file.Symbols) {
125 end = file.Symbols[n].Addr
126 } else {
127 end = symbol.Section.Size
128 }
129
130 if end <= symbol.Addr || end > symbol.Addr+4096 {
131 return maxUint64, maxUint64, fmt.Errorf("symbol end address does not seem valid, %x:%x", symbol.Addr, end)
132 }
133
134 size = end - symbol.Addr
135 }
136
137 offset := symbol.Section.Offset + symbol.Addr
138
139 return uint64(offset), uint64(size), nil
140 }
141 }
142
143 return maxUint64, maxUint64, fmt.Errorf("symbol not found")
144}
145
146type File struct {
147 r io.ReaderAt
148 Symbols []*Symbol
149 Sections []*Section
150}
151
152type Symbol struct {
153 Name string
154 Addr uint64 // Address of the symbol inside the section.
155 Size uint64 // Size of the symbol, if known.
156 Section *Section
157}
158
159type Section struct {
160 Name string
161 Addr uint64 // Virtual address of the start of the section.
162 Offset uint64 // Offset into the file of the start of the section.
163 Size uint64
164}
165
Dan Willemsen2249dc82018-10-15 00:35:59 -0700166func DumpSymbols(r io.ReaderAt) error {
Colin Cross8673b5b2018-03-01 11:20:25 -0800167 err := dumpElfSymbols(r)
168 if elfError, ok := err.(cantParseError); ok {
169 // Try as a mach-o file
170 err = dumpMachoSymbols(r)
171 if _, ok := err.(cantParseError); ok {
172 // Try as a windows PE file
173 err = dumpPESymbols(r)
174 if _, ok := err.(cantParseError); ok {
175 // Can't parse as elf, macho, or PE, return the elf error
176 return elfError
177 }
178 }
179 }
180 return err
Colin Cross5498f852018-01-03 23:39:54 -0800181}