1 : // Copyright 2014 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 : // Declares a class for writing an archive of COFF object files to a .lib
16 : // file. See ar_reader.h for details of MSVS version of the file format.
17 :
18 : #include "syzygy/ar/ar_writer.h"
19 :
20 : #include <windows.h>
21 : #include <sys/stat.h>
22 :
23 : #include "base/logging.h"
24 : #include "base/sys_byteorder.h"
25 : #include "base/files/file_util.h"
26 : #include "base/memory/scoped_ptr.h"
27 : #include "base/strings/stringprintf.h"
28 : #include "base/strings/utf_string_conversions.h"
29 : #include "syzygy/common/align.h"
30 : #include "syzygy/common/buffer_parser.h"
31 : #include "syzygy/common/buffer_writer.h"
32 : #include "syzygy/core/file_util.h"
33 :
34 : namespace ar {
35 :
36 : namespace {
37 :
38 : typedef ArWriter::FileVector FileVector;
39 :
40 : // Contains a list of file offsets at which each file starts in the archive.
41 : typedef std::vector<uint32> FileOffsets;
42 :
43 : // Determines if a symbol should be added to the symbol table. The rules as to
44 : // what symbols should be exported has been derived by observation of inputs
45 : // and outputs to lib.exe, guided by available documentation.
46 E : bool ShouldAddSymbolToTable(const IMAGE_SYMBOL& symbol, bool* is_weak) {
47 E : DCHECK_NE(reinterpret_cast<bool*>(NULL), is_weak);
48 :
49 E : *is_weak = false;
50 :
51 E : switch (symbol.StorageClass) {
52 : case IMAGE_SYM_CLASS_EXTERNAL: {
53 E : if (symbol.SectionNumber == 0 && symbol.Type == 0 && symbol.Value > 0) {
54 i : *is_weak = true;
55 i : return true;
56 : }
57 : if ((symbol.SectionNumber == -1 || symbol.SectionNumber > 0) &&
58 E : symbol.NumberOfAuxSymbols == 0) {
59 E : return true;
60 : }
61 E : break;
62 : }
63 :
64 : case IMAGE_SYM_CLASS_WEAK_EXTERNAL: {
65 : if (symbol.SectionNumber == 0 && symbol.Type == 0 &&
66 E : symbol.NumberOfAuxSymbols == 1) {
67 E : *is_weak = true;
68 E : return true;
69 : }
70 : break;
71 : }
72 :
73 : default:
74 : break;
75 : }
76 :
77 E : return false;
78 E : }
79 :
80 : // Updates the symbol table. Returns true if the symbol was a duplicate entry,
81 : // false otherwise. Updates the symbol table with the following logic:
82 : //
83 : // - The first non-weak definition of a symbol wins.
84 : // - The first weak definition of a symbol with no non-weak definitions wins.
85 : bool UpdateSymbolTable(uint32 file_index,
86 : const base::StringPiece& name,
87 : bool is_weak,
88 : SymbolIndexMap* symbols,
89 E : SymbolIndexMap* weak_symbols) {
90 : SymbolIndexMap::value_type value = std::make_pair(name.as_string(),
91 E : file_index);
92 E : SymbolIndexMap::iterator it = symbols->find(value.first);
93 E : SymbolIndexMap::iterator weak_it = weak_symbols->find(value.first);
94 :
95 E : if (is_weak) {
96 : // Case 1: Weak symbol appears, nothing with same name in table.
97 : // Symbol should be added to table.
98 E : if (it == symbols->end()) {
99 i : DCHECK(weak_it == weak_symbols->end());
100 i : CHECK(symbols->insert(value).second);
101 i : CHECK(weak_symbols->insert(value).second);
102 i : return false;
103 : }
104 :
105 : // Case 2: Weak symbol appears, weak symbol already in table.
106 : // Symbol should be ignored, marked as duplicate.
107 E : if (weak_it != weak_symbols->end()) {
108 i : DCHECK(it != symbols->end());
109 i : DCHECK(*it == *weak_it);
110 i : return true;
111 : }
112 :
113 : // Case 3: Weak symbol appears, non-weak symbol already in table.
114 : // Symbol should be ignored, marked as duplicate.
115 E : DCHECK(it != symbols->end());
116 E : DCHECK(weak_it == weak_symbols->end());
117 E : return true;
118 : }
119 E : DCHECK(!is_weak);
120 :
121 : // Case 4: Non-weak symbol appears, nothing with same name in table.
122 : // Symbol should be added to table.
123 E : if (it == symbols->end()) {
124 E : DCHECK(weak_it == weak_symbols->end());
125 E : CHECK(symbols->insert(value).second);
126 E : return false;
127 : }
128 :
129 : // Case 5: Non-weak symbol appears, weak symbol already in table.
130 : // Symbol should replace weak symbol, marked as duplicate.
131 E : if (weak_it != weak_symbols->end()) {
132 i : DCHECK(it != symbols->end());
133 i : DCHECK(*it == *weak_it);
134 i : it->second = file_index;
135 i : weak_symbols->erase(weak_it);
136 i : return true;
137 : }
138 :
139 : // Case 6: Non-weak symbol appears, non-weak symbol already in table.
140 : // Symbol should be ignored, marked as duplicate.
141 E : DCHECK(it != symbols->end());
142 E : DCHECK(weak_it == weak_symbols->end());
143 E : return true;
144 E : }
145 :
146 : // Extracts exported symbol names from the given COFF object file, adding them
147 : // to |symbols|. Returns true on success, false otherwise. This contains some
148 : // code that is similar to what is found in CoffImage or CoffDecomposer, but
149 : // using those classes is a little overkill for our purposes.
150 : bool ExtractSymbolsCoff(uint32 file_index,
151 : const ParsedArFileHeader& header,
152 : const DataBuffer& file_contents,
153 : SymbolIndexMap* symbols,
154 E : SymbolIndexMap* weak_symbols) {
155 E : DCHECK_NE(reinterpret_cast<SymbolIndexMap*>(NULL), symbols);
156 E : DCHECK_NE(reinterpret_cast<SymbolIndexMap*>(NULL), weak_symbols);
157 :
158 : common::BinaryBufferReader reader(file_contents.data(),
159 E : file_contents.size());
160 E : const IMAGE_FILE_HEADER* file_header = NULL;
161 E : if (!reader.Read(&file_header))
162 i : return false;
163 :
164 : // Object files should never contain an optional header.
165 E : if (file_header->SizeOfOptionalHeader != 0) {
166 i : LOG(ERROR) << "Unrecognized object file: " << header.name;
167 i : return false;
168 : }
169 :
170 : // If there are no symbols then there's no work to be done.
171 E : if (file_header->NumberOfSymbols == 0)
172 i : return true;
173 :
174 : // Get the string table offset.
175 : size_t string_table_offset = file_header->PointerToSymbolTable +
176 E : file_header->NumberOfSymbols * sizeof(IMAGE_SYMBOL);
177 :
178 : // Keep track of how many symbols have already been defined.
179 E : size_t duplicate_symbols = 0;
180 :
181 : // Parse the symbols.
182 E : reader.set_pos(file_header->PointerToSymbolTable);
183 E : const IMAGE_SYMBOL* symbol = NULL;
184 E : for (size_t i = 0; i < file_header->NumberOfSymbols;
185 E : i += 1 + symbol->NumberOfAuxSymbols) {
186 : if (!reader.Read(&symbol) ||
187 : !reader.Consume(sizeof(IMAGE_AUX_SYMBOL) *
188 E : symbol->NumberOfAuxSymbols)) {
189 i : LOG(ERROR) << "Failed to read symbol " << i << " of object file: "
190 : << header.name;
191 i : return false;
192 : }
193 :
194 : // Filter out symbols that don't belong in the symbol table.
195 E : bool is_weak = false;
196 E : if (!ShouldAddSymbolToTable(*symbol, &is_weak))
197 E : continue;
198 :
199 : // Get the symbol name.
200 E : base::StringPiece name;
201 : {
202 E : const char* s = NULL;
203 E : size_t max_len = 0;
204 E : if (symbol->N.Name.Short == 0) {
205 E : if (symbol->N.Name.Long >= file_contents.size()) {
206 i : LOG(ERROR) << "Invalid symbol name pointer in object file: "
207 : << header.name;
208 i : return false;
209 : }
210 E : size_t offset = string_table_offset + symbol->N.Name.Long;
211 : s = reinterpret_cast<const char*>(file_contents.data()) +
212 E : offset;
213 E : max_len = file_contents.size() - offset;
214 E : } else {
215 E : s = reinterpret_cast<const char*>(symbol->N.ShortName);
216 E : max_len = sizeof(symbol->N.ShortName);
217 : }
218 E : size_t len = ::strnlen(s, max_len);
219 E : name = base::StringPiece(s, len);
220 : }
221 :
222 : // Update the symbol tables with this symbol, keeping track of whether or
223 : // not this was a duplicate symbol name.
224 E : if (UpdateSymbolTable(file_index, name, is_weak, symbols, weak_symbols))
225 E : ++duplicate_symbols;
226 E : }
227 :
228 E : if (duplicate_symbols) {
229 E : LOG(INFO) << "Ignored " << duplicate_symbols
230 : << " duplicate symbols in object file: " << header.name;
231 : }
232 :
233 E : return true;
234 E : }
235 :
236 : // Extracts the symbol name from the given COFF import definition, adding it to
237 : // |symbols|. Returns true on success, false otherwise.
238 : bool ExtractSymbolsImportDef(uint32 file_index,
239 : const ParsedArFileHeader& header,
240 : const DataBuffer& file_contents,
241 : SymbolIndexMap* symbols,
242 E : SymbolIndexMap* weak_symbols) {
243 E : DCHECK_NE(reinterpret_cast<SymbolIndexMap*>(NULL), symbols);
244 E : DCHECK_NE(reinterpret_cast<SymbolIndexMap*>(NULL), weak_symbols);
245 :
246 : common::BinaryBufferReader reader(file_contents.data(),
247 E : file_contents.size());
248 E : const IMPORT_OBJECT_HEADER* import = NULL;
249 E : if (!reader.Read(&import))
250 i : return false;
251 :
252 E : const char* name = NULL;
253 E : size_t size = 0;
254 E : if (!reader.ReadString(&name, &size))
255 i : return false;
256 :
257 E : std::string imp_name("__imp_");
258 E : imp_name += name;
259 :
260 E : bool is_duplicate = false;
261 E : if (UpdateSymbolTable(file_index, name, false, symbols, weak_symbols))
262 i : is_duplicate = true;
263 E : if (UpdateSymbolTable(file_index, imp_name, false, symbols, weak_symbols))
264 i : is_duplicate = true;
265 :
266 E : if (is_duplicate) {
267 i : LOG(INFO) << "Ignored duplicate symbol \"" << name
268 : << "\" from import definition file: " << header.name;
269 : }
270 :
271 E : return true;
272 E : }
273 :
274 : // Extracts symbols from the given file. If the file is not of a recognized
275 : // type, then this does nothing.
276 : bool ExtractSymbols(uint32 file_index,
277 : const ParsedArFileHeader& header,
278 : const DataBuffer& file_contents,
279 : SymbolIndexMap* symbols,
280 E : SymbolIndexMap* weak_symbols) {
281 E : core::FileType file_type = core::kUnknownFileType;
282 : if (!core::GuessFileType(file_contents.data(), file_contents.size(),
283 E : &file_type)) {
284 i : LOG(ERROR) << "Unable to determine file type: " << header.name;
285 i : return false;
286 : }
287 :
288 E : switch (file_type) {
289 : case core::kCoffFileType:
290 : case core::kCoff64FileType: {
291 : if (!ExtractSymbolsCoff(file_index, header, file_contents, symbols,
292 E : weak_symbols)) {
293 i : return false;
294 : }
295 E : break;
296 : }
297 :
298 : case core::kImportDefinitionFileType: {
299 : if (!ExtractSymbolsImportDef(file_index, header, file_contents, symbols,
300 E : weak_symbols)) {
301 i : return false;
302 : }
303 E : break;
304 : }
305 :
306 : // Files that we recognize but don't have to process.
307 : case core::kResourceFileType: {
308 i : break;
309 : }
310 :
311 : // We don't know how to process anonymous COFF files, so can't extract
312 : // symbol information.
313 : case core::kAnonymousCoffFileType: {
314 i : LOG(ERROR) << "Unable to extract symbols from anonymous COFF object: "
315 : << header.name;
316 i : return false;
317 : }
318 :
319 : case core::kUnknownFileType: {
320 E : LOG(ERROR) << "Unable to add file of unknown type to archive: "
321 : << header.name;
322 E : return false;
323 : }
324 :
325 : default: {
326 i : LOG(ERROR) << "Unable to add file of invalid type to archive: "
327 : << header.name;
328 i : return false;
329 : }
330 : }
331 :
332 E : return true;
333 E : }
334 :
335 : // Fills in a raw ArFileHeader with the data from |parsed_header|.
336 : bool PopulateArFileHeader(const ParsedArFileHeader& parsed_header,
337 E : ArFileHeader* raw_header) {
338 E : DCHECK_NE(reinterpret_cast<ArFileHeader*>(NULL), raw_header);
339 :
340 : // Convert value types.
341 : std::string timestamp = base::StringPrintf(
342 E : "%lld", static_cast<uint64>(parsed_header.timestamp.ToDoubleT()));
343 E : std::string mode = base::StringPrintf("%d", parsed_header.mode);
344 E : std::string size = base::StringPrintf("%lld", parsed_header.size);
345 :
346 : // Validate sizes of inputs.
347 E : if (parsed_header.name.size() > sizeof(raw_header->name)) {
348 i : LOG(ERROR) << "Filename too long for ArFileHeader: " << parsed_header.name;
349 i : return false;
350 : }
351 E : if (timestamp.size() > sizeof(raw_header->timestamp)) {
352 i : LOG(ERROR) << "Timestamp too large for ArFileHeader: " << timestamp;
353 i : return false;
354 : }
355 E : if (mode.size() > sizeof(raw_header->mode)) {
356 i : LOG(ERROR) << "Mode too large for ArFileHeader: " << mode;
357 i : return false;
358 : }
359 E : if (size.size() > sizeof(raw_header->size)) {
360 i : LOG(ERROR) << "Size too large for ArFileHeader: " << size;
361 i : return false;
362 : }
363 :
364 : // Fill the header with spaces.
365 E : ::memset(raw_header, ' ', sizeof(*raw_header));
366 :
367 : // Populate the various fields.
368 : ::memcpy(raw_header->name, parsed_header.name.c_str(),
369 E : parsed_header.name.size());
370 E : ::memcpy(raw_header->timestamp, timestamp.c_str(), timestamp.size());
371 E : ::memcpy(raw_header->mode, mode.c_str(), mode.size());
372 E : ::memcpy(raw_header->size, size.c_str(), size.size());
373 E : ::memcpy(raw_header->magic, kArFileMagic, sizeof(kArFileMagic));
374 :
375 E : return true;
376 E : }
377 :
378 : // Writes the given file to an archive, prepended by its header.
379 : bool WriteFile(const ArFileHeader& header,
380 : const DataBuffer& contents,
381 E : FILE* file) {
382 E : DCHECK_NE(reinterpret_cast<FILE*>(NULL), file);
383 :
384 : // Write the header.
385 E : if (::fwrite(&header, sizeof(header), 1, file) != 1) {
386 i : LOG(ERROR) << "Failed to write file header.";
387 i : return false;
388 : }
389 :
390 : // Write the contents.
391 : if (::fwrite(contents.data(), 1, contents.size(), file) !=
392 E : contents.size()) {
393 i : LOG(ERROR) << "Failed to write file contents.";
394 i : return false;
395 : }
396 :
397 E : return true;
398 E : }
399 :
400 : // Writes a primary symbol table using the legacy symbol table format.
401 : bool WritePrimarySymbolTable(const base::Time& timestamp,
402 : const SymbolIndexMap& symbols,
403 : const FileOffsets& offsets,
404 E : FILE* file) {
405 E : DCHECK_NE(reinterpret_cast<FILE*>(NULL), file);
406 :
407 : // Invert the symbol map. We require the symbols sorted by
408 : // increasing offset and not by name.
409 : typedef std::pair<size_t, std::string> SymbolPair;
410 : typedef std::vector<SymbolPair> SymbolVector;
411 E : SymbolVector syms;
412 E : syms.reserve(symbols.size());
413 E : SymbolIndexMap::const_iterator sym_it = symbols.begin();
414 E : for (; sym_it != symbols.end(); ++sym_it)
415 E : syms.push_back(std::make_pair(sym_it->second, sym_it->first));
416 E : std::sort(syms.begin(), syms.end());
417 :
418 : // Generate the content.
419 E : DataBuffer buffer;
420 E : common::VectorBufferWriter writer(&buffer);
421 E : CHECK(writer.Write<uint32>(base::ByteSwap(symbols.size())));
422 E : for (size_t i = 0; i < syms.size(); ++i) {
423 E : DCHECK_LE(syms[i].first, offsets.size());
424 E : uint32 offset = offsets[syms[i].first];
425 E : CHECK(writer.Write<uint32>(base::ByteSwap(offset)));
426 E : }
427 E : for (size_t i = 0; i < syms.size(); ++i) {
428 : CHECK(writer.Write<char>(syms[i].second.size() + 1,
429 E : syms[i].second.data()));
430 E : }
431 :
432 : // Generate the header and write the content.
433 E : ParsedArFileHeader header;
434 E : header.name = "/";
435 E : header.timestamp = timestamp;
436 E : header.mode = 0;
437 E : header.size = buffer.size();
438 : ArFileHeader raw_header;
439 E : if (!PopulateArFileHeader(header, &raw_header))
440 i : return false;
441 E : if (!WriteFile(raw_header, buffer, file))
442 i : return false;
443 :
444 E : return true;
445 E : }
446 :
447 : // Writes an MSVS-style symbol table.
448 : bool WriteSecondarySymbolTable(const base::Time& timestamp,
449 : const SymbolIndexMap& symbols,
450 : const FileOffsets& offsets,
451 E : FILE* file) {
452 E : DCHECK_NE(reinterpret_cast<FILE*>(NULL), file);
453 :
454 : // Generate the content.
455 E : DataBuffer buffer;
456 E : common::VectorBufferWriter writer(&buffer);
457 E : CHECK(writer.Write<uint32>(offsets.size()));
458 E : CHECK(writer.Write<uint32>(offsets.size(), offsets.data()));
459 E : CHECK(writer.Write<uint32>(symbols.size()));
460 E : SymbolIndexMap::const_iterator sym_it = symbols.begin();
461 : // File indices are 1 based.
462 E : for (; sym_it != symbols.end(); ++sym_it)
463 E : CHECK(writer.Write<uint16>(sym_it->second + 1));
464 E : for (sym_it = symbols.begin(); sym_it != symbols.end(); ++sym_it) {
465 : CHECK(writer.Write<char>(sym_it->first.size() + 1,
466 E : sym_it->first.data()));
467 E : }
468 :
469 : // Generate the header and write the content.
470 E : ParsedArFileHeader header;
471 E : header.name = "/";
472 E : header.timestamp = timestamp;
473 E : header.mode = 0;
474 E : header.size = buffer.size();
475 : ArFileHeader raw_header;
476 E : if (!PopulateArFileHeader(header, &raw_header))
477 i : return false;
478 E : if (!WriteFile(raw_header, buffer, file))
479 i : return false;
480 :
481 E : return true;
482 E : }
483 :
484 : // Writes an extended name table.
485 : bool WriteNameTable(const base::Time& timestamp,
486 : const DataBuffer& names,
487 E : FILE* file) {
488 E : DCHECK_NE(reinterpret_cast<FILE*>(NULL), file);
489 :
490 : // Populate the header.
491 E : ParsedArFileHeader header;
492 E : header.name = "//";
493 E : header.timestamp = timestamp;
494 E : header.mode = 0;
495 E : header.size = names.size();
496 :
497 : ArFileHeader raw_header;
498 E : if (!PopulateArFileHeader(header, &raw_header))
499 i : return false;
500 :
501 E : if (!WriteFile(raw_header, names, file))
502 i : return false;
503 :
504 E : return true;
505 E : }
506 :
507 : // Aligns the file cursor to the alignment required by the archive file
508 : // and returns the aligned cursor position.
509 E : uint32 AlignAndGetPosition(FILE* file) {
510 E : DCHECK_NE(reinterpret_cast<FILE*>(NULL), file);
511 :
512 E : uint32 pos = ::ftell(file);
513 E : uint32 aligned_pos = common::AlignUp(pos, kArFileAlignment);
514 E : if (aligned_pos != pos) {
515 E : for (size_t i = 0; i < aligned_pos - pos; ++i)
516 E : ::fputc(0, file);
517 : }
518 :
519 E : return aligned_pos;
520 E : }
521 :
522 : } // namespace
523 :
524 E : ArWriter::ArWriter() {
525 E : }
526 :
527 : bool ArWriter::AddFile(const base::StringPiece& filename,
528 : const base::Time& timestamp,
529 : uint32 mode,
530 E : const DataBuffer* contents) {
531 E : DCHECK_NE(reinterpret_cast<DataBuffer*>(NULL), contents);
532 :
533 E : if (contents->size() == 0) {
534 E : LOG(ERROR) << "Unable to add empty file to archive: " << filename;
535 E : return false;
536 : }
537 :
538 : // Try to insert the file into the map. If this fails then there's a
539 : // collision.
540 E : std::string name = filename.as_string();
541 : std::pair<FileIndexMap::const_iterator, bool> result =
542 E : file_index_map_.insert(std::make_pair(name, files_.size()));
543 E : CHECK(result.second);
544 E : DCHECK_EQ(files_.size(), result.first->second);
545 :
546 : // Build the file header.
547 E : ParsedArFileHeader header;
548 E : header.name = name;
549 E : header.timestamp = timestamp;
550 E : header.mode = mode;
551 E : header.size = contents->size();
552 :
553 : // Try to parse the symbols from the file. We keep a copy of the
554 : // symbol tables so as not to corrupt them if the operation fails.
555 E : SymbolIndexMap symbols = symbols_;
556 E : SymbolIndexMap weak_symbols = weak_symbols_;
557 : if (!ExtractSymbols(files_.size(), header, *contents, &symbols,
558 E : &weak_symbols)) {
559 E : return false;
560 : }
561 :
562 : // If all goes well then commit the file to the archive.
563 E : std::swap(symbols_, symbols);
564 E : std::swap(weak_symbols_, weak_symbols);
565 E : files_.push_back(std::make_pair(header, contents));
566 E : return true;
567 E : }
568 :
569 E : bool ArWriter::AddFile(const base::FilePath& path) {
570 E : std::string name = base::WideToUTF8(path.value());
571 :
572 : // Get the mode of the file.
573 : struct _stat stat;
574 E : if (_wstat(path.value().c_str(), &stat) != 0) {
575 i : LOG(ERROR) << "Unable to get file status: " << path.value();
576 i : return false;
577 : }
578 E : if (stat.st_size == 0) {
579 E : LOG(ERROR) << "Unable to add empty file to archive: " << path.value();
580 E : return false;
581 : }
582 :
583 E : scoped_ptr<DataBuffer> buffer(new DataBuffer(stat.st_size));
584 : int read = base::ReadFile(
585 : path,
586 : reinterpret_cast<char*>(buffer->data()),
587 E : static_cast<int>(stat.st_size));
588 E : if (read != static_cast<int>(stat.st_size)) {
589 i : LOG(ERROR) << "Unable to read file: " << path.value();
590 i : return false;
591 : }
592 :
593 E : base::Time timestamp = base::Time::FromTimeT(stat.st_mtime);
594 E : uint32 mode = stat.st_mode;
595 E : if (!AddFile(name, timestamp, mode, buffer.get()))
596 E : return false;
597 :
598 : // Transfer ownership of the buffer to the object.
599 E : buffers_.push_back(buffer.release());
600 :
601 E : return true;
602 E : }
603 :
604 E : bool ArWriter::Write(const base::FilePath& path) {
605 E : if (files_.empty()) {
606 i : LOG(ERROR) << "Unable to write an empty archive.";
607 i : return false;
608 : }
609 :
610 E : std::vector<ArFileHeader> raw_headers(files_.size());
611 E : DataBuffer names;
612 E : for (size_t i = 0; i < files_.size(); ++i) {
613 : // Grab a copy of the header because we are going to modify it.
614 E : ParsedArFileHeader header = files_[i].first;
615 E : ArFileHeader& raw_header = raw_headers[i];
616 :
617 : // Translate the filename.
618 E : if (header.name.size() >= sizeof(raw_header.name)) {
619 : // Copy the extended filename to the name table, with a terminating
620 : // null.
621 E : size_t offset = names.size();
622 E : names.resize(offset + header.name.size() + 1);
623 : ::memcpy(names.data() + offset, header.name.data(),
624 E : header.name.size() + 1);
625 :
626 : // Name the file with a reference to the name table.
627 E : header.name = base::StringPrintf("/%d", offset);
628 E : } else {
629 : // Simply append a trailing '/' to the name.
630 E : header.name += "/";
631 : }
632 :
633 : // Fill in the raw file header.
634 E : if (!PopulateArFileHeader(header, &raw_header))
635 i : return false;
636 E : }
637 :
638 : // Open the file and write the global header.
639 E : base::ScopedFILE file(base::OpenFile(path, "w+b"));
640 E : if (file.get() == NULL) {
641 i : LOG(ERROR) << "Unable to open file for writing: " << path.value();
642 i : return false;
643 : }
644 E : if (::fwrite(kArGlobalMagic, sizeof(kArGlobalMagic), 1, file.get()) != 1) {
645 i : LOG(ERROR) << "Failed to write global archive header.";
646 i : return false;
647 : }
648 :
649 : // Write the symbol tables. We initially use a set of dummy offsets, and
650 : // reach back and write the actual offsets once we've laid out the object
651 : // files.
652 E : FileOffsets offsets(files_.size());
653 E : base::Time timestamp = base::Time::Now();
654 E : uint32 symbols1_pos = AlignAndGetPosition(file.get());
655 E : if (!WritePrimarySymbolTable(timestamp, symbols_, offsets, file.get()))
656 i : return false;
657 E : uint32 symbols2_pos = AlignAndGetPosition(file.get());
658 E : if (!WriteSecondarySymbolTable(timestamp, symbols_, offsets, file.get()))
659 i : return false;
660 :
661 : // Write the name table.
662 E : AlignAndGetPosition(file.get());
663 E : if (!WriteNameTable(timestamp, names, file.get()))
664 i : return false;
665 :
666 : // Write the files, keeping track of their offsets.
667 E : for (size_t i = 0; i < files_.size(); ++i) {
668 E : const DataBuffer& buffer = *files_[i].second;
669 E : const ArFileHeader& raw_header = raw_headers[i];
670 :
671 E : offsets[i] = AlignAndGetPosition(file.get());
672 E : if (!WriteFile(raw_header, buffer, file.get()))
673 i : return false;
674 E : }
675 :
676 : // Rewrite the symbol streams using the actual file offsets this time around.
677 E : if (::fseek(file.get(), symbols1_pos, SEEK_SET) != 0) {
678 i : LOG(ERROR) << "Failed to seek to primary symbol stream.";
679 i : return false;
680 : }
681 E : if (!WritePrimarySymbolTable(timestamp, symbols_, offsets, file.get()))
682 i : return false;
683 E : if (::fseek(file.get(), symbols2_pos, SEEK_SET) != 0) {
684 i : LOG(ERROR) << "Failed to seek to secondary symbol stream.";
685 i : return false;
686 : }
687 E : if (!WriteSecondarySymbolTable(timestamp, symbols_, offsets, file.get()))
688 i : return false;
689 :
690 E : return true;
691 E : }
692 :
693 : } // namespace ar
|