Chromium Code Reviews| 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 mach_vm_address_t linkedit_address, | |
| 42 mach_vm_size_t linkedit_size, | |
| 43 const std::string& module_info) | |
| 44 : module_info_(module_info), | |
| 45 linkedit_range_(), | |
| 46 process_reader_(process_reader), | |
| 47 linkedit_segment_(linkedit_segment) { | |
| 48 linkedit_range_.SetRange(process_reader_, linkedit_address, linkedit_size); | |
| 49 DCHECK(linkedit_range_.IsValid()); | |
| 50 } | |
| 51 | |
| 52 ~MachOImageSymbolTableReaderInitializer() {} | |
| 53 | |
| 54 //! \brief Reads the symbol table from another process. | |
| 55 //! | |
| 56 //! \sa MachOImageSymbolTableReader::Initialize() | |
| 57 bool Initialize(const process_types::symtab_command* symtab_command, | |
| 58 const process_types::dysymtab_command* dysymtab_command, | |
| 59 MachOImageSymbolTableReader::SymbolInformationMap* | |
| 60 external_defined_symbols) { | |
| 61 mach_vm_address_t symtab_address = | |
| 62 AddressForLinkEditComponent(symtab_command->symoff); | |
| 63 uint32_t symbol_count = symtab_command->nsyms; | |
| 64 size_t nlist_size = process_types::nlist::ExpectedSize(process_reader_); | |
| 65 mach_vm_size_t symtab_size = symbol_count * nlist_size; | |
| 66 if (!IsInLinkEditSegment(symtab_address, symtab_size, "symtab")) { | |
| 67 return false; | |
| 68 } | |
| 69 | |
| 70 // If a dysymtab is present, use it to filter the symtab for just the | |
| 71 // portion used for extdefsym. If no dysymtab is present, the entire symtab | |
| 72 // will need to be consulted. | |
| 73 uint32_t skip_count = 0; | |
| 74 if (dysymtab_command) { | |
| 75 if (dysymtab_command->iextdefsym >= symtab_command->nsyms || | |
| 76 dysymtab_command->iextdefsym + dysymtab_command->nextdefsym > | |
| 77 symtab_command->nsyms) { | |
| 78 LOG(WARNING) << base::StringPrintf( | |
| 79 "dysymtab extdefsym %u + %u > symtab nsyms %u", | |
| 80 dysymtab_command->iextdefsym, | |
| 81 dysymtab_command->nextdefsym, | |
| 82 symtab_command->nsyms) << module_info_; | |
| 83 return false; | |
| 84 } | |
| 85 | |
| 86 skip_count = dysymtab_command->iextdefsym; | |
| 87 mach_vm_size_t skip_size = skip_count * nlist_size; | |
| 88 symtab_address += skip_size; | |
| 89 symtab_size -= skip_size; | |
| 90 symbol_count = dysymtab_command->nextdefsym; | |
| 91 } | |
| 92 | |
| 93 mach_vm_address_t strtab_address = | |
| 94 AddressForLinkEditComponent(symtab_command->stroff); | |
| 95 mach_vm_size_t strtab_size = symtab_command->strsize; | |
| 96 if (!IsInLinkEditSegment(strtab_address, strtab_size, "strtab")) { | |
| 97 return false; | |
| 98 } | |
| 99 | |
| 100 scoped_ptr<process_types::nlist[]> symbols( | |
| 101 new process_types::nlist[symtab_command->nsyms]); | |
| 102 if (!process_types::nlist::ReadArrayInto( | |
| 103 process_reader_, symtab_address, symbol_count, &symbols[0])) { | |
| 104 LOG(WARNING) << "could not read symbol table" << module_info_; | |
| 105 return false; | |
| 106 } | |
| 107 | |
| 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 std::string name; | |
| 126 if (!process_reader_->Memory()->ReadCStringSizeLimited( | |
| 127 strtab_address + symbol.n_strx, | |
| 128 strtab_size - symbol.n_strx, | |
| 129 &name)) { | |
| 130 LOG(WARNING) << "could not read string" << symbol_info; | |
| 131 return false; | |
| 132 } | |
| 133 | |
| 134 if (symbol_type == N_ABS && symbol.n_sect != NO_SECT) { | |
| 135 LOG(WARNING) << base::StringPrintf("N_ABS symbol %s in section %u", | |
| 136 name.c_str(), | |
| 137 symbol.n_sect) << symbol_info; | |
| 138 return false; | |
| 139 } | |
| 140 | |
| 141 if (symbol_type == N_SECT && symbol.n_sect == NO_SECT) { | |
| 142 LOG(WARNING) << base::StringPrintf( | |
| 143 "N_SECT symbol %s in section NO_SECT", | |
| 144 name.c_str()) << symbol_info; | |
| 145 return false; | |
| 146 } | |
| 147 | |
| 148 if (external_defined_symbols->count(name)) { | |
| 149 LOG(WARNING) << "duplicate symbol " << name << symbol_info; | |
| 150 return false; | |
| 151 } | |
| 152 | |
| 153 MachOImageSymbolTableReader::SymbolInformation symbol_info; | |
| 154 symbol_info.value = symbol.n_value; | |
| 155 symbol_info.section = symbol.n_sect; | |
| 156 (*external_defined_symbols)[name] = symbol_info; | |
| 157 } else if (dysymtab_command) { | |
| 158 LOG(WARNING) << "non-external symbol in extdefsym" << symbol_info; | |
| 159 return false; | |
| 160 } | |
| 161 } | |
| 162 | |
| 163 return true; | |
| 164 } | |
| 165 | |
| 166 private: | |
| 167 //! \brief Computes the address for data in the `__LINKEDIT` segment | |
| 168 //! identified by its file offset in a Mach-O image. | |
| 169 //! | |
| 170 //! \param[in] fileoff The file offset relative to the beginning of an image’s | |
| 171 //! `mach_header` or `mach_header_64` of the data in the `__LINKEDIT` | |
| 172 //! segment. | |
| 173 //! | |
| 174 //! \return The address, in the remote process’ address space, of the | |
| 175 //! requested data. | |
| 176 mach_vm_address_t AddressForLinkEditComponent(uint32_t fileoff) const { | |
| 177 return linkedit_range_.Base() + fileoff - linkedit_segment_->fileoff(); | |
| 178 } | |
| 179 | |
| 180 //! \brief Determines whether an address range is located within the | |
| 181 //! `__LINKEDIT` segment. | |
| 182 //! | |
| 183 //! \param[in] address The base address of the range to check. | |
| 184 //! \param[in] size The size of the range to check. | |
| 185 //! \param[in] tag A string that identifies the range being checked. This is | |
| 186 //! used only for logging. | |
| 187 //! | |
| 188 //! \return `true` if the range identified by \a address + \a size lies | |
| 189 //! entirely within the `__LINKEDIT` segment. `false` if that range is | |
| 190 //! invalid, or if that range is not contained by the `__LINKEDIT` | |
| 191 //! segment, with an appropriate message logged. | |
| 192 bool IsInLinkEditSegment(mach_vm_address_t address, | |
| 193 mach_vm_size_t size, | |
| 194 const char* tag) const { | |
| 195 CheckedMachAddressRange subrange(process_reader_, address, size); | |
| 196 if (!subrange.IsValid()) { | |
| 197 LOG(WARNING) << base::StringPrintf("invalid %s range (0x%llx + 0x%llx)", | |
| 198 tag, | |
| 199 address, | |
| 200 size) << module_info_; | |
| 201 return false; | |
| 202 } | |
| 203 | |
| 204 if (!linkedit_range_.ContainsRange(subrange)) { | |
| 205 LOG(WARNING) << base::StringPrintf( | |
| 206 "%s at 0x%llx + 0x%llx outside of " SEG_LINKEDIT | |
| 207 " segment at 0x%llx + 0x%llx", | |
| 208 tag, | |
| 209 address, | |
| 210 size, | |
| 211 linkedit_range_.Base(), | |
| 212 linkedit_range_.Size()) << module_info_; | |
| 213 return false; | |
| 214 } | |
| 215 | |
| 216 return true; | |
| 217 } | |
| 218 | |
| 219 std::string module_info_; | |
| 220 CheckedMachAddressRange linkedit_range_; | |
| 221 ProcessReader* process_reader_; // weak | |
| 222 const MachOImageSegmentReader* linkedit_segment_; // weak | |
| 223 | |
| 224 DISALLOW_COPY_AND_ASSIGN(MachOImageSymbolTableReaderInitializer); | |
| 225 }; | |
| 226 | |
| 227 } // namespace internal | |
| 228 | |
| 229 MachOImageSymbolTableReader::MachOImageSymbolTableReader() | |
| 230 : external_defined_symbols_(), initialized_() { | |
| 231 } | |
| 232 | |
| 233 MachOImageSymbolTableReader::~MachOImageSymbolTableReader() { | |
| 234 } | |
| 235 | |
| 236 bool MachOImageSymbolTableReader::Initialize( | |
| 237 ProcessReader* process_reader, | |
| 238 const process_types::symtab_command* symtab_command, | |
| 239 const process_types::dysymtab_command* dysymtab_command, | |
| 240 const MachOImageSegmentReader* linkedit_segment, | |
|
Robert Sesek
2014/09/05 19:04:16
It seems like it'd be handy to have access to the
| |
| 241 mach_vm_address_t linkedit_address, | |
| 242 mach_vm_size_t linkedit_size, | |
| 243 const std::string& module_info) { | |
| 244 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); | |
| 245 | |
| 246 internal::MachOImageSymbolTableReaderInitializer initializer(process_reader, | |
| 247 linkedit_segment, | |
| 248 linkedit_address, | |
| 249 linkedit_size, | |
| 250 module_info); | |
| 251 if (!initializer.Initialize( | |
| 252 symtab_command, dysymtab_command, &external_defined_symbols_)) { | |
| 253 return false; | |
| 254 } | |
| 255 | |
| 256 INITIALIZATION_STATE_SET_VALID(initialized_); | |
| 257 return true; | |
| 258 } | |
| 259 | |
| 260 const MachOImageSymbolTableReader::SymbolInformation* | |
| 261 MachOImageSymbolTableReader::LookUpExternalDefinedSymbol( | |
| 262 const std::string& name) const { | |
| 263 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
| 264 | |
| 265 const auto& iterator = external_defined_symbols_.find(name); | |
| 266 if (iterator == external_defined_symbols_.end()) { | |
| 267 return NULL; | |
| 268 } | |
| 269 return &iterator->second; | |
| 270 } | |
| 271 | |
| 272 } // namespace crashpad | |
| OLD | NEW |