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_t* 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 E : FileOffsetAddress opt_header_start(file_header_start.value() +
100 : sizeof(IMAGE_FILE_HEADER));
101 E : FileOffsetAddress section_table_start(opt_header_start +
102 : file_header_->SizeOfOptionalHeader);
103 E : SizeType section_table_size(file_header_->NumberOfSections *
104 : sizeof(IMAGE_SECTION_HEADER));
105 E : FileOffsetAddress header_end(section_table_start + section_table_size);
106 :
107 : // Read the section headers.
108 E : if (!parser_.GetCountAt(section_table_start.value(),
109 : file_header_->NumberOfSections,
110 : §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 E : if (!InsertSection(kInvalidSection, FileOffsetAddress(0), header_size,
129 : 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_t* PECoffFile<AddressSpaceTraits>::GetImageData(
207 : AddressType addr,
208 E : SizeType len) const {
209 E : ImageAddressSpace::Range range(addr, len);
210 : ImageAddressSpace::RangeMap::const_iterator it(
211 E : address_space_.FindContaining(range));
212 :
213 E : if (it == address_space_.ranges().end())
214 E : return nullptr;
215 :
216 E : ptrdiff_t offs = addr - it->first.start();
217 E : DCHECK_GE(offs, 0);
218 E : const uint8_t* data = nullptr;
219 E : if (!it->second.parser.GetCountAt(offs, len, &data))
220 E : return nullptr;
221 :
222 E : return data;
223 E : }
224 :
225 : template <typename AddressSpaceTraits>
226 : uint8_t* PECoffFile<AddressSpaceTraits>::GetImageData(AddressType addr,
227 E : SizeType len) {
228 E : return const_cast<uint8_t*>(
229 : static_cast<const PECoffFile*>(this)->GetImageData(addr, len));
230 E : }
231 :
232 : template <typename AddressSpaceTraits>
233 : template <typename ItemType>
234 : bool PECoffFile<AddressSpaceTraits>::GetImageData(
235 E : AddressType addr, SizeType len, const ItemType** item_ptr) const {
236 E : const uint8_t* ptr = GetImageData(addr, len);
237 E : if (ptr == nullptr)
238 i : return false;
239 E : *item_ptr = reinterpret_cast<const ItemType*>(ptr);
240 E : return true;
241 E : }
242 :
243 : template <typename AddressSpaceTraits>
244 : template <typename ItemType>
245 : bool PECoffFile<AddressSpaceTraits>::GetImageData(
246 E : AddressType addr, SizeType len, ItemType** item_ptr) {
247 E : uint8_t* ptr = GetImageData(addr, len);
248 E : if (ptr == nullptr)
249 i : return false;
250 E : *item_ptr = reinterpret_cast<ItemType*>(ptr);
251 E : return true;
252 E : }
253 :
254 : template <typename AddressSpaceTraits>
255 : bool PECoffFile<AddressSpaceTraits>::ReadImage(AddressType addr,
256 E : void* data, SizeType len) const {
257 E : DCHECK(data != nullptr);
258 : // TODO(chrisha): Make this use BinaryBufferParser::CopyAt when it's ready.
259 E : const uint8_t* buf = GetImageData(addr, len);
260 E : if (buf == nullptr)
261 i : return false;
262 E : ::memcpy(data, buf, len);
263 E : return true;
264 E : }
265 :
266 : template <typename AddressSpaceTraits>
267 : bool PECoffFile<AddressSpaceTraits>::ReadImageString(AddressType addr,
268 E : std::string* str) const {
269 E : DCHECK(file_header_ != nullptr);
270 E : str->clear();
271 :
272 : // Locate the range that contains the first byte of the string.
273 E : ImageAddressSpace::Range range(addr, 1);
274 : ImageAddressSpace::RangeMap::const_iterator it(
275 E : address_space_.FindContaining(range));
276 E : if (it == address_space_.ranges().end())
277 i : return false;
278 :
279 E : ptrdiff_t offs = addr - it->first.start();
280 E : DCHECK_GE(offs, 0);
281 E : size_t length = 0;
282 E : const char* data = nullptr;
283 E : if (!it->second.parser.GetStringAt(offs, &data, &length))
284 i : return false;
285 :
286 E : str->assign(data, length);
287 E : return true;
288 E : }
289 :
290 : template <typename AddressSpaceTraits>
291 : const uint8_t* PECoffFile<AddressSpaceTraits>::GetImageDataByFileOffset(
292 : FileOffsetAddress addr,
293 E : SizeType len) const {
294 E : const uint8_t* data = nullptr;
295 E : if (!parser_.GetCountAt(addr.value(), len, &data))
296 i : return nullptr;
297 E : return data;
298 E : }
299 :
300 : } // namespace pe
301 :
302 : #endif // SYZYGY_PE_PE_COFF_FILE_IMPL_H_
|