OLD | NEW |
| (Empty) |
1 // Copyright 2014 The Crashpad Authors. 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 #include "util/mac/mach_o_image_symbol_table_reader.h" | |
16 | |
17 #include <mach-o/loader.h> | |
18 #include <mach-o/nlist.h> | |
19 | |
20 #include "base/memory/scoped_ptr.h" | |
21 #include "base/strings/stringprintf.h" | |
22 #include "util/mac/checked_mach_address_range.h" | |
23 #include "util/mach/task_memory.h" | |
24 | |
25 namespace crashpad { | |
26 | |
27 namespace internal { | |
28 | |
29 //! \brief The internal implementation for MachOImageSymbolTableReader. | |
30 //! | |
31 //! Initialization is broken into more than one function that needs to share | |
32 //! data, so member variables are used. However, much of this data is irrelevant | |
33 //! after initialization is completed, so rather than doing it in | |
34 //! MachOImageSymbolTableReader, it’s handled by this class, which is a “friend” | |
35 //! of MachOImageSymbolTableReader. | |
36 class MachOImageSymbolTableReaderInitializer { | |
37 public: | |
38 MachOImageSymbolTableReaderInitializer( | |
39 ProcessReader* process_reader, | |
40 const MachOImageSegmentReader* linkedit_segment, | |
41 const std::string& module_info) | |
42 : module_info_(module_info), | |
43 linkedit_range_(), | |
44 process_reader_(process_reader), | |
45 linkedit_segment_(linkedit_segment) { | |
46 linkedit_range_.SetRange( | |
47 process_reader_, linkedit_segment->Address(), linkedit_segment->Size()); | |
48 DCHECK(linkedit_range_.IsValid()); | |
49 } | |
50 | |
51 ~MachOImageSymbolTableReaderInitializer() {} | |
52 | |
53 //! \brief Reads the symbol table from another process. | |
54 //! | |
55 //! \sa MachOImageSymbolTableReader::Initialize() | |
56 bool Initialize(const process_types::symtab_command* symtab_command, | |
57 const process_types::dysymtab_command* dysymtab_command, | |
58 MachOImageSymbolTableReader::SymbolInformationMap* | |
59 external_defined_symbols) { | |
60 mach_vm_address_t symtab_address = | |
61 AddressForLinkEditComponent(symtab_command->symoff); | |
62 uint32_t symbol_count = symtab_command->nsyms; | |
63 size_t nlist_size = process_types::nlist::ExpectedSize(process_reader_); | |
64 mach_vm_size_t symtab_size = symbol_count * nlist_size; | |
65 if (!IsInLinkEditSegment(symtab_address, symtab_size, "symtab")) { | |
66 return false; | |
67 } | |
68 | |
69 // If a dysymtab is present, use it to filter the symtab for just the | |
70 // portion used for extdefsym. If no dysymtab is present, the entire symtab | |
71 // will need to be consulted. | |
72 uint32_t skip_count = 0; | |
73 if (dysymtab_command) { | |
74 if (dysymtab_command->iextdefsym >= symtab_command->nsyms || | |
75 dysymtab_command->iextdefsym + dysymtab_command->nextdefsym > | |
76 symtab_command->nsyms) { | |
77 LOG(WARNING) << base::StringPrintf( | |
78 "dysymtab extdefsym %u + %u > symtab nsyms %u", | |
79 dysymtab_command->iextdefsym, | |
80 dysymtab_command->nextdefsym, | |
81 symtab_command->nsyms) << module_info_; | |
82 return false; | |
83 } | |
84 | |
85 skip_count = dysymtab_command->iextdefsym; | |
86 mach_vm_size_t skip_size = skip_count * nlist_size; | |
87 symtab_address += skip_size; | |
88 symtab_size -= skip_size; | |
89 symbol_count = dysymtab_command->nextdefsym; | |
90 } | |
91 | |
92 mach_vm_address_t strtab_address = | |
93 AddressForLinkEditComponent(symtab_command->stroff); | |
94 mach_vm_size_t strtab_size = symtab_command->strsize; | |
95 if (!IsInLinkEditSegment(strtab_address, strtab_size, "strtab")) { | |
96 return false; | |
97 } | |
98 | |
99 scoped_ptr<process_types::nlist[]> symbols( | |
100 new process_types::nlist[symtab_command->nsyms]); | |
101 if (!process_types::nlist::ReadArrayInto( | |
102 process_reader_, symtab_address, symbol_count, &symbols[0])) { | |
103 LOG(WARNING) << "could not read symbol table" << module_info_; | |
104 return false; | |
105 } | |
106 | |
107 scoped_ptr<TaskMemory::MappedMemory> string_table; | |
108 for (size_t symbol_index = 0; symbol_index < symbol_count; ++symbol_index) { | |
109 const process_types::nlist& symbol = symbols[symbol_index]; | |
110 std::string symbol_info = base::StringPrintf(", symbol index %zu%s", | |
111 skip_count + symbol_index, | |
112 module_info_.c_str()); | |
113 uint8_t symbol_type = symbol.n_type & N_TYPE; | |
114 if ((symbol.n_type & N_STAB) == 0 && (symbol.n_type & N_PEXT) == 0 && | |
115 (symbol_type == N_ABS || symbol_type == N_SECT) && | |
116 (symbol.n_type & N_EXT)) { | |
117 if (symbol.n_strx >= strtab_size) { | |
118 LOG(WARNING) << base::StringPrintf( | |
119 "string at 0x%x out of bounds (0x%llx)", | |
120 symbol.n_strx, | |
121 strtab_size) << symbol_info; | |
122 return false; | |
123 } | |
124 | |
125 if (!string_table) { | |
126 string_table = process_reader_->Memory()->ReadMapped( | |
127 strtab_address, strtab_size); | |
128 if (!string_table) { | |
129 LOG(WARNING) << "could not read string table" << module_info_; | |
130 return false; | |
131 } | |
132 } | |
133 | |
134 std::string name; | |
135 if (!string_table->ReadCString(symbol.n_strx, &name)) { | |
136 LOG(WARNING) << "could not read string" << symbol_info; | |
137 return false; | |
138 } | |
139 | |
140 if (symbol_type == N_ABS && symbol.n_sect != NO_SECT) { | |
141 LOG(WARNING) << base::StringPrintf("N_ABS symbol %s in section %u", | |
142 name.c_str(), | |
143 symbol.n_sect) << symbol_info; | |
144 return false; | |
145 } | |
146 | |
147 if (symbol_type == N_SECT && symbol.n_sect == NO_SECT) { | |
148 LOG(WARNING) << base::StringPrintf( | |
149 "N_SECT symbol %s in section NO_SECT", | |
150 name.c_str()) << symbol_info; | |
151 return false; | |
152 } | |
153 | |
154 if (external_defined_symbols->count(name)) { | |
155 LOG(WARNING) << "duplicate symbol " << name << symbol_info; | |
156 return false; | |
157 } | |
158 | |
159 MachOImageSymbolTableReader::SymbolInformation symbol_info; | |
160 symbol_info.value = symbol.n_value; | |
161 symbol_info.section = symbol.n_sect; | |
162 (*external_defined_symbols)[name] = symbol_info; | |
163 } else if (dysymtab_command) { | |
164 LOG(WARNING) << "non-external symbol in extdefsym" << symbol_info; | |
165 return false; | |
166 } | |
167 } | |
168 | |
169 return true; | |
170 } | |
171 | |
172 private: | |
173 //! \brief Computes the address for data in the `__LINKEDIT` segment | |
174 //! identified by its file offset in a Mach-O image. | |
175 //! | |
176 //! \param[in] fileoff The file offset relative to the beginning of an image’s | |
177 //! `mach_header` or `mach_header_64` of the data in the `__LINKEDIT` | |
178 //! segment. | |
179 //! | |
180 //! \return The address, in the remote process’ address space, of the | |
181 //! requested data. | |
182 mach_vm_address_t AddressForLinkEditComponent(uint32_t fileoff) const { | |
183 return linkedit_range_.Base() + fileoff - linkedit_segment_->fileoff(); | |
184 } | |
185 | |
186 //! \brief Determines whether an address range is located within the | |
187 //! `__LINKEDIT` segment. | |
188 //! | |
189 //! \param[in] address The base address of the range to check. | |
190 //! \param[in] size The size of the range to check. | |
191 //! \param[in] tag A string that identifies the range being checked. This is | |
192 //! used only for logging. | |
193 //! | |
194 //! \return `true` if the range identified by \a address + \a size lies | |
195 //! entirely within the `__LINKEDIT` segment. `false` if that range is | |
196 //! invalid, or if that range is not contained by the `__LINKEDIT` | |
197 //! segment, with an appropriate message logged. | |
198 bool IsInLinkEditSegment(mach_vm_address_t address, | |
199 mach_vm_size_t size, | |
200 const char* tag) const { | |
201 CheckedMachAddressRange subrange(process_reader_, address, size); | |
202 if (!subrange.IsValid()) { | |
203 LOG(WARNING) << base::StringPrintf("invalid %s range (0x%llx + 0x%llx)", | |
204 tag, | |
205 address, | |
206 size) << module_info_; | |
207 return false; | |
208 } | |
209 | |
210 if (!linkedit_range_.ContainsRange(subrange)) { | |
211 LOG(WARNING) << base::StringPrintf( | |
212 "%s at 0x%llx + 0x%llx outside of " SEG_LINKEDIT | |
213 " segment at 0x%llx + 0x%llx", | |
214 tag, | |
215 address, | |
216 size, | |
217 linkedit_range_.Base(), | |
218 linkedit_range_.Size()) << module_info_; | |
219 return false; | |
220 } | |
221 | |
222 return true; | |
223 } | |
224 | |
225 std::string module_info_; | |
226 CheckedMachAddressRange linkedit_range_; | |
227 ProcessReader* process_reader_; // weak | |
228 const MachOImageSegmentReader* linkedit_segment_; // weak | |
229 | |
230 DISALLOW_COPY_AND_ASSIGN(MachOImageSymbolTableReaderInitializer); | |
231 }; | |
232 | |
233 } // namespace internal | |
234 | |
235 MachOImageSymbolTableReader::MachOImageSymbolTableReader() | |
236 : external_defined_symbols_(), initialized_() { | |
237 } | |
238 | |
239 MachOImageSymbolTableReader::~MachOImageSymbolTableReader() { | |
240 } | |
241 | |
242 bool MachOImageSymbolTableReader::Initialize( | |
243 ProcessReader* process_reader, | |
244 const process_types::symtab_command* symtab_command, | |
245 const process_types::dysymtab_command* dysymtab_command, | |
246 const MachOImageSegmentReader* linkedit_segment, | |
247 const std::string& module_info) { | |
248 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); | |
249 | |
250 internal::MachOImageSymbolTableReaderInitializer initializer(process_reader, | |
251 linkedit_segment, | |
252 module_info); | |
253 if (!initializer.Initialize( | |
254 symtab_command, dysymtab_command, &external_defined_symbols_)) { | |
255 return false; | |
256 } | |
257 | |
258 INITIALIZATION_STATE_SET_VALID(initialized_); | |
259 return true; | |
260 } | |
261 | |
262 const MachOImageSymbolTableReader::SymbolInformation* | |
263 MachOImageSymbolTableReader::LookUpExternalDefinedSymbol( | |
264 const std::string& name) const { | |
265 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
266 | |
267 const auto& iterator = external_defined_symbols_.find(name); | |
268 if (iterator == external_defined_symbols_.end()) { | |
269 return nullptr; | |
270 } | |
271 return &iterator->second; | |
272 } | |
273 | |
274 } // namespace crashpad | |
OLD | NEW |