1 : // Copyright 2013 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 :
15 : // Template implementation of common definitions and helper routines
16 : // for reading both PE and COFF file formats.
17 :
18 : #ifndef SYZYGY_PE_PE_COFF_FILE_IMPL_H_
19 : #define SYZYGY_PE_PE_COFF_FILE_IMPL_H_
20 :
21 : #include "base/files/file_util.h"
22 :
23 : #include "syzygy/block_graph/block_graph.h"
24 :
25 : namespace pe {
26 :
27 : template <typename AddressSpaceTraits>
28 E : bool PECoffFile<AddressSpaceTraits>::Init(const base::FilePath& path) {
29 E : path_ = path;
30 : // ReadFileToString doesn't like relative paths.
31 E : if (!base::ReadFileToString(base::MakeAbsoluteFilePath(path), &image_data_))
32 E : return false;
33 E : parser_.SetData(image_data_.c_str(), image_data_.size());
34 E : return true;
35 E : }
36 :
37 : template <typename AddressSpaceTraits>
38 : bool PECoffFile<AddressSpaceTraits>::Contains(AddressType addr,
39 E : SizeType len) const {
40 E : const ImageAddressSpace::Range range(addr, len);
41 E : return address_space_.FindContaining(range) != address_space_.ranges().end();
42 E : }
43 :
44 : template <typename AddressSpaceTraits>
45 : size_t PECoffFile<AddressSpaceTraits>::GetSectionIndex(AddressType addr,
46 E : SizeType len) const {
47 E : const ImageAddressSpace::Range range(addr, len);
48 : ImageAddressSpace::RangeMap::const_iterator it =
49 E : address_space_.FindContaining(range);
50 E : if (it == address_space_.ranges().end())
51 E : return kInvalidSection;
52 E : return it->second.id;
53 E : }
54 :
55 : template <typename AddressSpaceTraits>
56 : const IMAGE_SECTION_HEADER* PECoffFile<AddressSpaceTraits>::GetSectionHeader(
57 E : AddressType addr, SizeType len) const {
58 E : size_t id = GetSectionIndex(addr, len);
59 E : if (id == kInvalidSection)
60 E : return nullptr;
61 E : DCHECK_LT(id, file_header_->NumberOfSections);
62 E : return section_headers_ + id;
63 E : }
64 :
65 : template <typename AddressSpaceTraits>
66 : std::string PECoffFile<AddressSpaceTraits>::GetSectionName(
67 E : const IMAGE_SECTION_HEADER& section) {
68 E : const char* name = reinterpret_cast<const char*>(section.Name);
69 E : return std::string(name, strnlen(name, arraysize(section.Name)));
70 E : }
71 :
72 : template <typename AddressSpaceTraits>
73 : std::string PECoffFile<AddressSpaceTraits>::GetSectionName(
74 E : size_t section_index) const {
75 E : DCHECK_LT(section_index, file_header_->NumberOfSections);
76 :
77 E : const IMAGE_SECTION_HEADER* section = section_headers_ + section_index;
78 E : return GetSectionName(*section);
79 E : }
80 :
81 : template <typename AddressSpaceTraits>
82 : bool PECoffFile<AddressSpaceTraits>::ReadCommonHeaders(
83 E : FileOffsetAddress file_header_start) {
84 : // Test for unsupported object files.
85 E : const uint16* obj_sig = nullptr;
86 E : if (!parser_.GetCountAt(0, 2, &obj_sig))
87 i : return false;
88 E : if (obj_sig[0] == 0 && obj_sig[1] == 0xFFFF) {
89 E : LOG(ERROR) << "Unsupported anonymous object file.";
90 E : return false;
91 : }
92 :
93 : // Read the COFF file header.
94 E : if (!parser_.GetAt(file_header_start.value(), &file_header_))
95 i : return false;
96 :
97 : // Compute size of all headers, from the beginning of the file to
98 : // the end of the section table.
99 : FileOffsetAddress opt_header_start(file_header_start.value() +
100 E : sizeof(IMAGE_FILE_HEADER));
101 : FileOffsetAddress section_table_start(opt_header_start +
102 E : file_header_->SizeOfOptionalHeader);
103 : SizeType section_table_size(file_header_->NumberOfSections *
104 E : sizeof(IMAGE_SECTION_HEADER));
105 E : FileOffsetAddress header_end(section_table_start + section_table_size);
106 :
107 : // Read the section headers.
108 : if (!parser_.GetCountAt(section_table_start.value(),
109 : file_header_->NumberOfSections,
110 E : §ion_headers_)) {
111 i : return false;
112 : }
113 :
114 E : SizeType header_size = header_end.value();
115 E : if (file_header_->SizeOfOptionalHeader != 0) {
116 E : const IMAGE_OPTIONAL_HEADER* opt_header = nullptr;
117 E : if (!parser_.GetAt(opt_header_start.value(), &opt_header))
118 i : return false;
119 : // In a sane world the stated header size will match that manually
120 : // calculated by walking the headers and aligning up by the file alignment.
121 : // However, this is not necessary for the PE file to be valid, and there may
122 : // be a gap between the two.
123 E : header_size = opt_header->SizeOfHeaders;
124 : }
125 :
126 : // We now know how large the headers are, so create a range for them.
127 E : ImageAddressSpace::Range header_range(header_address(), header_size);
128 : if (!InsertSection(kInvalidSection, FileOffsetAddress(0), header_size,
129 E : header_range)) {
130 i : return false;
131 : }
132 :
133 E : return true;
134 E : }
135 :
136 : template <typename AddressSpaceTraits>
137 E : bool PECoffFile<AddressSpaceTraits>::ReadSections() {
138 E : DCHECK(file_header_ != nullptr);
139 E : DCHECK(section_headers_ != nullptr);
140 :
141 E : size_t num_sections = file_header_->NumberOfSections;
142 E : for (size_t i = 0; i < num_sections; ++i) {
143 E : const IMAGE_SECTION_HEADER* hdr = section_headers_ + i;
144 :
145 : // Construct address in the new address space; FromSectionHeader()
146 : // returns header_address() if unmapped.
147 E : AddressType addr = AddressSpaceTraits::GetSectionAddress(*hdr);
148 :
149 : // Ignore unmapped sections, as those, by definition, have no
150 : // address to map to within our address space. They need to be
151 : // handled separately during decomposition.
152 E : if (addr == AddressSpaceTraits::invalid_address())
153 E : continue;
154 :
155 : // Empty sections are ignored at this level of the parsing.
156 E : size_t section_size = AddressSpaceTraits::GetSectionSize(*hdr);
157 E : if (section_size == 0)
158 i : continue;
159 :
160 : // Insert the range for the new section.
161 E : ImageAddressSpace::Range section_range(addr, section_size);
162 E : FileOffsetAddress off(hdr->PointerToRawData);
163 E : if (!InsertSection(i, off, hdr->SizeOfRawData, section_range)) {
164 i : LOG(ERROR) << "Unable to insert range for section " << hdr->Name << ".";
165 i : return false;
166 : }
167 E : }
168 :
169 E : return true;
170 E : }
171 :
172 : template <typename AddressSpaceTraits>
173 : bool PECoffFile<AddressSpaceTraits>::InsertSection(
174 : size_t id,
175 : FileOffsetAddress start,
176 : size_t size,
177 E : const typename ImageAddressSpace::Range& range) {
178 E : const void* section_data = nullptr;
179 E : if (!parser_.GetAt(start.value(), size, §ion_data))
180 i : return false;
181 E : SectionInfo section_info(id, section_data, size);
182 :
183 E : ImageAddressSpace::RangeMap::iterator it;
184 E : bool inserted = address_space_.Insert(range, section_info, &it);
185 E : if (!inserted) {
186 i : LOG(ERROR) << "Unable to create new range in address space.";
187 i : return false;
188 : }
189 :
190 E : return true;
191 E : }
192 :
193 : template <typename AddressSpaceTraits>
194 : bool PECoffFile<AddressSpaceTraits>::ReadAt(size_t offset,
195 : void* destination,
196 E : size_t size) const {
197 : // TODO(chrisha): Use BinaryBufferParser::CopyAt when that's available.
198 E : const void* data = nullptr;
199 E : if (!parser_.GetAt(offset, size, &data))
200 i : return false;
201 E : ::memcpy(destination, data, size);
202 E : return true;
203 E : }
204 :
205 : template <typename AddressSpaceTraits>
206 : const uint8* PECoffFile<AddressSpaceTraits>::GetImageData(AddressType addr,
207 E : SizeType len) const {
208 E : ImageAddressSpace::Range range(addr, len);
209 : ImageAddressSpace::RangeMap::const_iterator it(
210 E : address_space_.FindContaining(range));
211 :
212 E : if (it == address_space_.ranges().end())
213 E : return nullptr;
214 :
215 E : ptrdiff_t offs = addr - it->first.start();
216 E : DCHECK_GE(offs, 0);
217 E : const uint8* data = nullptr;
218 E : if (!it->second.parser.GetCountAt(offs, len, &data))
219 E : return nullptr;
220 :
221 E : return data;
222 E : }
223 :
224 : template <typename AddressSpaceTraits>
225 : uint8* PECoffFile<AddressSpaceTraits>::GetImageData(AddressType addr,
226 E : SizeType len) {
227 : return const_cast<uint8*>(
228 E : static_cast<const PECoffFile*>(this)->GetImageData(addr, len));
229 E : }
230 :
231 : template <typename AddressSpaceTraits>
232 : template <typename ItemType>
233 : bool PECoffFile<AddressSpaceTraits>::GetImageData(
234 E : AddressType addr, SizeType len, const ItemType** item_ptr) const {
235 E : const uint8* ptr = GetImageData(addr, len);
236 E : if (ptr == nullptr)
237 i : return false;
238 E : *item_ptr = reinterpret_cast<const ItemType*>(ptr);
239 E : return true;
240 E : }
241 :
242 : template <typename AddressSpaceTraits>
243 : template <typename ItemType>
244 : bool PECoffFile<AddressSpaceTraits>::GetImageData(
245 E : AddressType addr, SizeType len, ItemType** item_ptr) {
246 E : uint8* ptr = GetImageData(addr, len);
247 E : if (ptr == nullptr)
248 i : return false;
249 E : *item_ptr = reinterpret_cast<ItemType*>(ptr);
250 E : return true;
251 E : }
252 :
253 : template <typename AddressSpaceTraits>
254 : bool PECoffFile<AddressSpaceTraits>::ReadImage(AddressType addr,
255 E : void* data, SizeType len) const {
256 E : DCHECK(data != nullptr);
257 : // TODO(chrisha): Make this use BinaryBufferParser::CopyAt when it's ready.
258 E : const uint8* buf = GetImageData(addr, len);
259 E : if (buf == nullptr)
260 i : return false;
261 E : ::memcpy(data, buf, len);
262 E : return true;
263 E : }
264 :
265 : template <typename AddressSpaceTraits>
266 : bool PECoffFile<AddressSpaceTraits>::ReadImageString(AddressType addr,
267 E : std::string* str) const {
268 E : DCHECK(file_header_ != nullptr);
269 E : str->clear();
270 :
271 : // Locate the range that contains the first byte of the string.
272 E : ImageAddressSpace::Range range(addr, 1);
273 : ImageAddressSpace::RangeMap::const_iterator it(
274 E : address_space_.FindContaining(range));
275 E : if (it == address_space_.ranges().end())
276 i : return false;
277 :
278 E : ptrdiff_t offs = addr - it->first.start();
279 E : DCHECK_GE(offs, 0);
280 E : size_t length = 0;
281 E : const char* data = nullptr;
282 E : if (!it->second.parser.GetStringAt(offs, &data, &length))
283 i : return false;
284 :
285 E : str->assign(data, length);
286 E : return true;
287 E : }
288 :
289 : template <typename AddressSpaceTraits>
290 : const uint8* PECoffFile<AddressSpaceTraits>::GetImageDataByFileOffset(
291 E : FileOffsetAddress addr, SizeType len) const {
292 E : const uint8* data = nullptr;
293 E : if (!parser_.GetCountAt(addr.value(), len, &data))
294 i : return nullptr;
295 E : return data;
296 E : }
297 :
298 : } // namespace pe
299 :
300 : #endif // SYZYGY_PE_PE_COFF_FILE_IMPL_H_
|