Coverage for /Syzygy/ar/ar_writer.cc

CoverageLines executed / instrumented / missingexe / inst / missLanguageGroup
78.4%2733480.C++source

Line-by-line coverage:

   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

Coverage information generated Thu Jan 14 17:40:38 2016.