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_segment_reader.h" | |
16 | |
17 #include <mach-o/loader.h> | |
18 | |
19 #include "base/logging.h" | |
20 #include "base/strings/stringprintf.h" | |
21 #include "util/mac/checked_mach_address_range.h" | |
22 #include "util/mac/process_reader.h" | |
23 #include "util/stdlib/strnlen.h" | |
24 | |
25 namespace crashpad { | |
26 | |
27 namespace { | |
28 | |
29 std::string SizeLimitedCString(const char* c_string, size_t max_length) { | |
30 return std::string(c_string, strnlen(c_string, max_length)); | |
31 } | |
32 | |
33 } // namespace | |
34 | |
35 MachOImageSegmentReader::MachOImageSegmentReader() | |
36 : segment_command_(), | |
37 sections_(), | |
38 section_map_(), | |
39 slide_(0), | |
40 initialized_(), | |
41 initialized_slide_() { | |
42 } | |
43 | |
44 MachOImageSegmentReader::~MachOImageSegmentReader() { | |
45 } | |
46 | |
47 bool MachOImageSegmentReader::Initialize(ProcessReader* process_reader, | |
48 mach_vm_address_t load_command_address, | |
49 const std::string& load_command_info) { | |
50 INITIALIZATION_STATE_SET_INITIALIZING(initialized_); | |
51 | |
52 if (!segment_command_.Read(process_reader, load_command_address)) { | |
53 LOG(WARNING) << "could not read segment_command" << load_command_info; | |
54 return false; | |
55 } | |
56 | |
57 const uint32_t kExpectedSegmentCommand = | |
58 process_reader->Is64Bit() ? LC_SEGMENT_64 : LC_SEGMENT; | |
59 DCHECK_EQ(segment_command_.cmd, kExpectedSegmentCommand); | |
60 DCHECK_GE(segment_command_.cmdsize, segment_command_.Size()); | |
61 const size_t kSectionStructSize = | |
62 process_types::section::ExpectedSize(process_reader); | |
63 const size_t kRequiredSize = | |
64 segment_command_.Size() + segment_command_.nsects * kSectionStructSize; | |
65 if (segment_command_.cmdsize < kRequiredSize) { | |
66 LOG(WARNING) << base::StringPrintf( | |
67 "segment command cmdsize 0x%x insufficient for %u " | |
68 "section%s (0x%zx)", | |
69 segment_command_.cmdsize, | |
70 segment_command_.nsects, | |
71 segment_command_.nsects == 1 ? "" : "s", | |
72 kRequiredSize) << load_command_info; | |
73 return false; | |
74 } | |
75 | |
76 std::string segment_name = NameInternal(); | |
77 std::string segment_info = base::StringPrintf( | |
78 ", segment %s%s", segment_name.c_str(), load_command_info.c_str()); | |
79 | |
80 // This checks the unslid segment range. The slid range (as loaded into | |
81 // memory) will be checked later by MachOImageReader. | |
82 CheckedMachAddressRange segment_range( | |
83 process_reader, segment_command_.vmaddr, segment_command_.vmsize); | |
84 if (!segment_range.IsValid()) { | |
85 LOG(WARNING) << base::StringPrintf("invalid segment range 0x%llx + 0x%llx", | |
86 segment_command_.vmaddr, | |
87 segment_command_.vmsize) << segment_info; | |
88 return false; | |
89 } | |
90 | |
91 sections_.resize(segment_command_.nsects); | |
92 if (!process_types::section::ReadArrayInto( | |
93 process_reader, | |
94 load_command_address + segment_command_.Size(), | |
95 segment_command_.nsects, | |
96 §ions_[0])) { | |
97 LOG(WARNING) << "could not read sections" << segment_info; | |
98 return false; | |
99 } | |
100 | |
101 for (size_t section_index = 0; | |
102 section_index < sections_.size(); | |
103 ++section_index) { | |
104 const process_types::section& section = sections_[section_index]; | |
105 std::string section_segment_name = SegmentNameString(section.segname); | |
106 std::string section_name = SectionNameString(section.sectname); | |
107 std::string section_full_name = | |
108 SegmentAndSectionNameString(section.segname, section.sectname); | |
109 | |
110 std::string section_info = base::StringPrintf(", section %s %zu/%zu%s", | |
111 section_full_name.c_str(), | |
112 section_index, | |
113 sections_.size(), | |
114 load_command_info.c_str()); | |
115 | |
116 if (section_segment_name != segment_name) { | |
117 LOG(WARNING) << "section.segname incorrect in segment " << segment_name | |
118 << section_info; | |
119 return false; | |
120 } | |
121 | |
122 CheckedMachAddressRange section_range( | |
123 process_reader, section.addr, section.size); | |
124 if (!section_range.IsValid()) { | |
125 LOG(WARNING) << base::StringPrintf( | |
126 "invalid section range 0x%llx + 0x%llx", | |
127 section.addr, | |
128 section.size) << section_info; | |
129 return false; | |
130 } | |
131 | |
132 if (!segment_range.ContainsRange(section_range)) { | |
133 LOG(WARNING) << base::StringPrintf( | |
134 "section at 0x%llx + 0x%llx outside of segment at " | |
135 "0x%llx + 0x%llx", | |
136 section.addr, | |
137 section.size, | |
138 segment_command_.vmaddr, | |
139 segment_command_.vmsize) << section_info; | |
140 return false; | |
141 } | |
142 | |
143 uint32_t section_type = (section.flags & SECTION_TYPE); | |
144 bool zero_fill = section_type == S_ZEROFILL || | |
145 section_type == S_GB_ZEROFILL || | |
146 section_type == S_THREAD_LOCAL_ZEROFILL; | |
147 | |
148 // Zero-fill section types aren’t mapped from the file, so their |offset| | |
149 // fields are irrelevant and are typically 0. | |
150 if (!zero_fill && | |
151 section.offset - segment_command_.fileoff != | |
152 section.addr - segment_command_.vmaddr) { | |
153 LOG(WARNING) << base::StringPrintf( | |
154 "section type 0x%x at 0x%llx has unexpected offset " | |
155 "0x%x in segment at 0x%llx with offset 0x%llx", | |
156 section_type, | |
157 section.addr, | |
158 section.offset, | |
159 segment_command_.vmaddr, | |
160 segment_command_.fileoff) << section_info; | |
161 return false; | |
162 } | |
163 | |
164 const auto& iterator = section_map_.find(section_name); | |
165 if (iterator != section_map_.end()) { | |
166 LOG(WARNING) << base::StringPrintf("duplicate section name at %zu", | |
167 iterator->second) << section_info; | |
168 return false; | |
169 } | |
170 | |
171 section_map_[section_name] = section_index; | |
172 } | |
173 | |
174 INITIALIZATION_STATE_SET_VALID(initialized_); | |
175 return true; | |
176 } | |
177 | |
178 std::string MachOImageSegmentReader::Name() const { | |
179 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
180 return NameInternal(); | |
181 } | |
182 | |
183 mach_vm_address_t MachOImageSegmentReader::Address() const { | |
184 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
185 INITIALIZATION_STATE_DCHECK_VALID(initialized_slide_); | |
186 return vmaddr() + (SegmentSlides() ? slide_ : 0); | |
187 } | |
188 | |
189 mach_vm_size_t MachOImageSegmentReader::Size() const { | |
190 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
191 INITIALIZATION_STATE_DCHECK_VALID(initialized_slide_); | |
192 return vmsize() + (SegmentSlides() ? 0 : slide_); | |
193 } | |
194 | |
195 const process_types::section* MachOImageSegmentReader::GetSectionByName( | |
196 const std::string& section_name, | |
197 mach_vm_address_t* address) const { | |
198 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
199 | |
200 const auto& iterator = section_map_.find(section_name); | |
201 if (iterator == section_map_.end()) { | |
202 return nullptr; | |
203 } | |
204 | |
205 return GetSectionAtIndex(iterator->second, address); | |
206 } | |
207 | |
208 const process_types::section* MachOImageSegmentReader::GetSectionAtIndex( | |
209 size_t index, | |
210 mach_vm_address_t* address) const { | |
211 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
212 CHECK_LT(index, sections_.size()); | |
213 | |
214 const process_types::section* section = §ions_[index]; | |
215 | |
216 if (address) { | |
217 INITIALIZATION_STATE_DCHECK_VALID(initialized_slide_); | |
218 *address = section->addr + (SegmentSlides() ? slide_ : 0); | |
219 } | |
220 | |
221 return section; | |
222 } | |
223 | |
224 bool MachOImageSegmentReader::SegmentSlides() const { | |
225 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
226 | |
227 // These are the same rules that the kernel uses to identify __PAGEZERO. See | |
228 // 10.9.4 xnu-2422.110.17/bsd/kern/mach_loader.c load_segment(). | |
229 return !(segment_command_.vmaddr == 0 && segment_command_.filesize == 0 && | |
230 segment_command_.vmsize != 0 && | |
231 (segment_command_.initprot & VM_PROT_ALL) == VM_PROT_NONE && | |
232 (segment_command_.maxprot & VM_PROT_ALL) == VM_PROT_NONE); | |
233 } | |
234 | |
235 // static | |
236 std::string MachOImageSegmentReader::SegmentNameString( | |
237 const char* segment_name_c) { | |
238 // This is used to interpret the segname field of both the segment_command and | |
239 // section structures, so be sure that they’re identical. | |
240 static_assert(sizeof(process_types::segment_command::segname) == | |
241 sizeof(process_types::section::segname), | |
242 "sizes must be equal"); | |
243 | |
244 return SizeLimitedCString(segment_name_c, | |
245 sizeof(process_types::segment_command::segname)); | |
246 } | |
247 | |
248 // static | |
249 std::string MachOImageSegmentReader::SectionNameString( | |
250 const char* section_name_c) { | |
251 return SizeLimitedCString(section_name_c, | |
252 sizeof(process_types::section::sectname)); | |
253 } | |
254 | |
255 // static | |
256 std::string MachOImageSegmentReader::SegmentAndSectionNameString( | |
257 const char* segment_name_c, | |
258 const char* section_name_c) { | |
259 return base::StringPrintf("%s,%s", | |
260 SegmentNameString(segment_name_c).c_str(), | |
261 SectionNameString(section_name_c).c_str()); | |
262 } | |
263 | |
264 std::string MachOImageSegmentReader::NameInternal() const { | |
265 return SegmentNameString(segment_command_.segname); | |
266 } | |
267 | |
268 void MachOImageSegmentReader::SetSlide(mach_vm_size_t slide) { | |
269 INITIALIZATION_STATE_DCHECK_VALID(initialized_); | |
270 INITIALIZATION_STATE_SET_INITIALIZING(initialized_slide_); | |
271 slide_ = slide; | |
272 INITIALIZATION_STATE_SET_VALID(initialized_slide_); | |
273 } | |
274 | |
275 } // namespace crashpad | |
OLD | NEW |