Coverage Report

Created: 2018-06-25 02:00

/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/lld/COFF/InputFiles.cpp
Line
Count
Source (jump to first uncovered line)
1
//===- InputFiles.cpp -----------------------------------------------------===//
2
//
3
//                             The LLVM Linker
4
//
5
// This file is distributed under the University of Illinois Open Source
6
// License. See LICENSE.TXT for details.
7
//
8
//===----------------------------------------------------------------------===//
9
10
#include "InputFiles.h"
11
#include "Chunks.h"
12
#include "Config.h"
13
#include "Driver.h"
14
#include "SymbolTable.h"
15
#include "Symbols.h"
16
#include "lld/Common/ErrorHandler.h"
17
#include "lld/Common/Memory.h"
18
#include "llvm-c/lto.h"
19
#include "llvm/ADT/SmallVector.h"
20
#include "llvm/ADT/Triple.h"
21
#include "llvm/ADT/Twine.h"
22
#include "llvm/BinaryFormat/COFF.h"
23
#include "llvm/Object/Binary.h"
24
#include "llvm/Object/COFF.h"
25
#include "llvm/Support/Casting.h"
26
#include "llvm/Support/Endian.h"
27
#include "llvm/Support/Error.h"
28
#include "llvm/Support/ErrorOr.h"
29
#include "llvm/Support/FileSystem.h"
30
#include "llvm/Target/TargetOptions.h"
31
#include <cstring>
32
#include <system_error>
33
#include <utility>
34
35
using namespace llvm;
36
using namespace llvm::COFF;
37
using namespace llvm::object;
38
using namespace llvm::support::endian;
39
40
using llvm::Triple;
41
using llvm::support::ulittle32_t;
42
43
namespace lld {
44
namespace coff {
45
46
std::vector<ObjFile *> ObjFile::Instances;
47
std::vector<ImportFile *> ImportFile::Instances;
48
std::vector<BitcodeFile *> BitcodeFile::Instances;
49
50
/// Checks that Source is compatible with being a weak alias to Target.
51
/// If Source is Undefined and has no weak alias set, makes it a weak
52
/// alias to Target.
53
static void checkAndSetWeakAlias(SymbolTable *Symtab, InputFile *F,
54
7
                                 Symbol *Source, Symbol *Target) {
55
7
  if (auto *U = dyn_cast<Undefined>(Source)) {
56
6
    if (U->WeakAlias && 
U->WeakAlias != Target0
)
57
0
      Symtab->reportDuplicate(Source, F);
58
6
    U->WeakAlias = Target;
59
6
  }
60
7
}
61
62
64
ArchiveFile::ArchiveFile(MemoryBufferRef M) : InputFile(ArchiveKind, M) {}
63
64
64
void ArchiveFile::parse() {
65
64
  // Parse a MemoryBufferRef as an archive file.
66
64
  File = CHECK(Archive::create(MB), this);
67
64
68
64
  // Read the symbol table to construct Lazy objects.
69
64
  for (const Archive::Symbol &Sym : File->symbols())
70
342
    Symtab->addLazy(this, Sym);
71
64
}
72
73
// Returns a buffer pointing to a member file containing a given symbol.
74
101
void ArchiveFile::addMember(const Archive::Symbol *Sym) {
75
101
  const Archive::Child &C =
76
101
      CHECK(Sym->getMember(),
77
101
            "could not get the member for symbol " + Sym->getName());
78
101
79
101
  // Return an empty buffer if we have already returned the same buffer.
80
101
  if (!Seen.insert(C.getChildOffset()).second)
81
0
    return;
82
101
83
101
  Driver->enqueueArchiveMember(C, Sym->getName(), getName());
84
101
}
85
86
4
std::vector<MemoryBufferRef> getArchiveMembers(Archive *File) {
87
4
  std::vector<MemoryBufferRef> V;
88
4
  Error Err = Error::success();
89
4
  for (const ErrorOr<Archive::Child> &COrErr : File->children(Err)) {
90
4
    Archive::Child C =
91
4
        CHECK(COrErr,
92
4
              File->getFileName() + ": could not get the child of the archive");
93
4
    MemoryBufferRef MBRef =
94
4
        CHECK(C.getMemoryBufferRef(),
95
4
              File->getFileName() +
96
4
                  ": could not get the buffer for a child of the archive");
97
4
    V.push_back(MBRef);
98
4
  }
99
4
  if (Err)
100
0
    fatal(File->getFileName() +
101
0
          ": Archive::children failed: " + toString(std::move(Err)));
102
4
  return V;
103
4
}
104
105
488
void ObjFile::parse() {
106
488
  // Parse a memory buffer as a COFF file.
107
488
  std::unique_ptr<Binary> Bin = CHECK(createBinary(MB), this);
108
488
109
488
  if (auto *Obj = dyn_cast<COFFObjectFile>(Bin.get())) {
110
488
    Bin.release();
111
488
    COFFObj.reset(Obj);
112
488
  } else {
113
0
    fatal(toString(this) + " is not a COFF file");
114
0
  }
115
488
116
488
  // Read section and symbol tables.
117
488
  initializeChunks();
118
488
  initializeSymbols();
119
488
}
120
121
// We set SectionChunk pointers in the SparseChunks vector to this value
122
// temporarily to mark comdat sections as having an unknown resolution. As we
123
// walk the object file's symbol table, once we visit either a leader symbol or
124
// an associative section definition together with the parent comdat's leader,
125
// we set the pointer to either nullptr (to mark the section as discarded) or a
126
// valid SectionChunk for that section.
127
static SectionChunk *const PendingComdat = reinterpret_cast<SectionChunk *>(1);
128
129
488
void ObjFile::initializeChunks() {
130
488
  uint32_t NumSections = COFFObj->getNumberOfSections();
131
488
  Chunks.reserve(NumSections);
132
488
  SparseChunks.resize(NumSections + 1);
133
2.12k
  for (uint32_t I = 1; I < NumSections + 1; 
++I1.63k
) {
134
1.63k
    const coff_section *Sec;
135
1.63k
    if (auto EC = COFFObj->getSection(I, Sec))
136
0
      fatal("getSection failed: #" + Twine(I) + ": " + EC.message());
137
1.63k
138
1.63k
    if (Sec->Characteristics & IMAGE_SCN_LNK_COMDAT)
139
346
      SparseChunks[I] = PendingComdat;
140
1.28k
    else
141
1.28k
      SparseChunks[I] = readSection(I, nullptr, "");
142
1.63k
  }
143
488
}
144
145
SectionChunk *ObjFile::readSection(uint32_t SectionNumber,
146
                                   const coff_aux_section_definition *Def,
147
1.61k
                                   StringRef LeaderName) {
148
1.61k
  const coff_section *Sec;
149
1.61k
  StringRef Name;
150
1.61k
  if (auto EC = COFFObj->getSection(SectionNumber, Sec))
151
0
    fatal("getSection failed: #" + Twine(SectionNumber) + ": " + EC.message());
152
1.61k
  if (auto EC = COFFObj->getSectionName(Sec, Name))
153
0
    fatal("getSectionName failed: #" + Twine(SectionNumber) + ": " +
154
0
          EC.message());
155
1.61k
156
1.61k
  if (Name == ".drectve") {
157
142
    ArrayRef<uint8_t> Data;
158
142
    COFFObj->getSectionContents(Sec, Data);
159
142
    Directives = std::string((const char *)Data.data(), Data.size());
160
142
    return nullptr;
161
142
  }
162
1.47k
163
1.47k
  // Object files may have DWARF debug info or MS CodeView debug info
164
1.47k
  // (or both).
165
1.47k
  //
166
1.47k
  // DWARF sections don't need any special handling from the perspective
167
1.47k
  // of the linker; they are just a data section containing relocations.
168
1.47k
  // We can just link them to complete debug info.
169
1.47k
  //
170
1.47k
  // CodeView needs a linker support. We need to interpret and debug
171
1.47k
  // info, and then write it to a separate .pdb file.
172
1.47k
173
1.47k
  // Ignore DWARF debug info unless /debug is given.
174
1.47k
  if (!Config->Debug && 
Name.startswith(".debug_")1.06k
)
175
0
    return nullptr;
176
1.47k
177
1.47k
  if (Sec->Characteristics & llvm::COFF::IMAGE_SCN_LNK_REMOVE)
178
0
    return nullptr;
179
1.47k
  auto *C = make<SectionChunk>(this, Sec);
180
1.47k
  if (Def)
181
330
    C->Checksum = Def->CheckSum;
182
1.47k
183
1.47k
  // CodeView sections are stored to a different vector because they are not
184
1.47k
  // linked in the regular manner.
185
1.47k
  if (C->isCodeView())
186
146
    DebugChunks.push_back(C);
187
1.32k
  else if (Config->GuardCF != GuardCFLevel::Off && 
Name == ".gfids$y"81
)
188
9
    GuardFidChunks.push_back(C);
189
1.31k
  else if (Config->GuardCF != GuardCFLevel::Off && 
Name == ".gljmp$y"72
)
190
1
    GuardLJmpChunks.push_back(C);
191
1.31k
  else if (Name == ".sxdata")
192
8
    SXDataChunks.push_back(C);
193
1.31k
  else if (Config->TailMerge && 
Sec->NumberOfRelocations == 0956
&&
194
1.31k
           
Name == ".rdata"795
&&
LeaderName.startswith("??_C@")28
)
195
10
    // COFF sections that look like string literal sections (i.e. no
196
10
    // relocations, in .rdata, leader symbol name matches the MSVC name mangling
197
10
    // for string literals) are subject to string tail merging.
198
10
    MergeChunk::addSection(C);
199
1.30k
  else
200
1.30k
    Chunks.push_back(C);
201
1.47k
202
1.47k
  return C;
203
1.47k
}
204
205
void ObjFile::readAssociativeDefinition(
206
99
    COFFSymbolRef Sym, const coff_aux_section_definition *Def) {
207
99
  SectionChunk *Parent = SparseChunks[Def->getNumber(Sym.isBigObj())];
208
99
209
99
  // If the parent is pending, it probably means that its section definition
210
99
  // appears after us in the symbol table. Leave the associated section as
211
99
  // pending; we will handle it during the second pass in initializeSymbols().
212
99
  if (Parent == PendingComdat)
213
19
    return;
214
80
215
80
  // Check whether the parent is prevailing. If it is, so are we, and we read
216
80
  // the section; otherwise mark it as discarded.
217
80
  int32_t SectionNumber = Sym.getSectionNumber();
218
80
  if (Parent) {
219
75
    SparseChunks[SectionNumber] = readSection(SectionNumber, Def, "");
220
75
    if (SparseChunks[SectionNumber])
221
75
      Parent->addAssociative(SparseChunks[SectionNumber]);
222
75
  } else {
223
5
    SparseChunks[SectionNumber] = nullptr;
224
5
  }
225
80
}
226
227
2.60k
Symbol *ObjFile::createRegular(COFFSymbolRef Sym) {
228
2.60k
  SectionChunk *SC = SparseChunks[Sym.getSectionNumber()];
229
2.60k
  if (Sym.isExternal()) {
230
718
    StringRef Name;
231
718
    COFFObj->getSymbolName(Sym, Name);
232
718
    if (SC)
233
718
      return Symtab->addRegular(this, Name, Sym.getGeneric(), SC);
234
0
    return Symtab->addUndefined(Name, this, false);
235
0
  }
236
1.88k
  if (SC)
237
1.80k
    return make<DefinedRegular>(this, /*Name*/ "", false,
238
1.80k
                                /*IsExternal*/ false, Sym.getGeneric(), SC);
239
82
  return nullptr;
240
82
}
241
242
488
void ObjFile::initializeSymbols() {
243
488
  uint32_t NumSymbols = COFFObj->getNumberOfSymbols();
244
488
  Symbols.resize(NumSymbols);
245
488
246
488
  SmallVector<std::pair<Symbol *, uint32_t>, 8> WeakAliases;
247
488
  std::vector<uint32_t> PendingIndexes;
248
488
  PendingIndexes.reserve(NumSymbols);
249
488
250
488
  std::vector<const coff_aux_section_definition *> ComdatDefs(
251
488
      COFFObj->getNumberOfSections() + 1);
252
488
253
3.72k
  for (uint32_t I = 0; I < NumSymbols; 
++I3.24k
) {
254
3.24k
    COFFSymbolRef COFFSym = check(COFFObj->getSymbol(I));
255
3.24k
    if (COFFSym.isUndefined()) {
256
206
      Symbols[I] = createUndefined(COFFSym);
257
3.03k
    } else if (COFFSym.isWeakExternal()) {
258
5
      Symbols[I] = createUndefined(COFFSym);
259
5
      uint32_t TagIndex = COFFSym.getAux<coff_aux_weak_external>()->TagIndex;
260
5
      WeakAliases.emplace_back(Symbols[I], TagIndex);
261
3.02k
    } else if (Optional<Symbol *> OptSym = createDefined(COFFSym, ComdatDefs)) {
262
2.74k
      Symbols[I] = *OptSym;
263
2.74k
    } else {
264
285
      // createDefined() returns None if a symbol belongs to a section that
265
285
      // was pending at the point when the symbol was read. This can happen in
266
285
      // two cases:
267
285
      // 1) section definition symbol for a comdat leader;
268
285
      // 2) symbol belongs to a comdat section associated with a section whose
269
285
      //    section definition symbol appears later in the symbol table.
270
285
      // In both of these cases, we can expect the section to be resolved by
271
285
      // the time we finish visiting the remaining symbols in the symbol
272
285
      // table. So we postpone the handling of this symbol until that time.
273
285
      PendingIndexes.push_back(I);
274
285
    }
275
3.24k
    I += COFFSym.getNumberOfAuxSymbols();
276
3.24k
  }
277
488
278
488
  for (uint32_t I : PendingIndexes) {
279
285
    COFFSymbolRef Sym = check(COFFObj->getSymbol(I));
280
285
    if (auto *Def = Sym.getSectionDefinition())
281
285
      if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
282
19
        readAssociativeDefinition(Sym, Def);
283
285
    Symbols[I] = createRegular(Sym);
284
285
  }
285
488
286
488
  for (auto &KV : WeakAliases) {
287
5
    Symbol *Sym = KV.first;
288
5
    uint32_t Idx = KV.second;
289
5
    checkAndSetWeakAlias(Symtab, this, Sym, Symbols[Idx]);
290
5
  }
291
488
}
292
293
211
Symbol *ObjFile::createUndefined(COFFSymbolRef Sym) {
294
211
  StringRef Name;
295
211
  COFFObj->getSymbolName(Sym, Name);
296
211
  return Symtab->addUndefined(Name, this, Sym.isWeakExternal());
297
211
}
298
299
Optional<Symbol *> ObjFile::createDefined(
300
    COFFSymbolRef Sym,
301
3.02k
    std::vector<const coff_aux_section_definition *> &ComdatDefs) {
302
3.02k
  StringRef Name;
303
3.02k
  if (Sym.isCommon()) {
304
11
    auto *C = make<CommonChunk>(Sym);
305
11
    Chunks.push_back(C);
306
11
    COFFObj->getSymbolName(Sym, Name);
307
11
    Symbol *S =
308
11
        Symtab->addCommon(this, Name, Sym.getValue(), Sym.getGeneric(), C);
309
11
    return S;
310
11
  }
311
3.01k
  if (Sym.isAbsolute()) {
312
150
    COFFObj->getSymbolName(Sym, Name);
313
150
    // Skip special symbols.
314
150
    if (Name == "@comp.id")
315
57
      return nullptr;
316
93
    if (Name == "@feat.00") {
317
83
      Feat00Flags = Sym.getValue();
318
83
      return nullptr;
319
83
    }
320
10
    if (Sym.isExternal())
321
6
      return Symtab->addAbsolute(Name, Sym);
322
4
    else
323
4
      return make<DefinedAbsolute>(Name, Sym);
324
2.86k
  }
325
2.86k
  int32_t SectionNumber = Sym.getSectionNumber();
326
2.86k
  if (SectionNumber == llvm::COFF::IMAGE_SYM_DEBUG)
327
1
    return nullptr;
328
2.86k
329
2.86k
  if (llvm::COFF::isReservedSectionNumber(SectionNumber))
330
0
    fatal(toString(this) + ": " + Name +
331
0
          " should not refer to special section " + Twine(SectionNumber));
332
2.86k
333
2.86k
  if ((uint32_t)SectionNumber >= SparseChunks.size())
334
0
    fatal(toString(this) + ": " + Name +
335
0
          " should not refer to non-existent section " + Twine(SectionNumber));
336
2.86k
337
2.86k
  // Handle comdat leader symbols.
338
2.86k
  if (const coff_aux_section_definition *Def = ComdatDefs[SectionNumber]) {
339
266
    ComdatDefs[SectionNumber] = nullptr;
340
266
    Symbol *Leader;
341
266
    bool Prevailing;
342
266
    if (Sym.isExternal()) {
343
250
      COFFObj->getSymbolName(Sym, Name);
344
250
      std::tie(Leader, Prevailing) =
345
250
          Symtab->addComdat(this, Name, Sym.getGeneric());
346
250
    } else {
347
16
      Leader = make<DefinedRegular>(this, /*Name*/ "", false,
348
16
                                    /*IsExternal*/ false, Sym.getGeneric());
349
16
      Prevailing = true;
350
16
    }
351
266
    if (Prevailing) {
352
255
      SectionChunk *C = readSection(SectionNumber, Def, Name);
353
255
      SparseChunks[SectionNumber] = C;
354
255
      C->Sym = cast<DefinedRegular>(Leader);
355
255
      cast<DefinedRegular>(Leader)->Data = &C->Repl;
356
255
    } else {
357
11
      SparseChunks[SectionNumber] = nullptr;
358
11
    }
359
266
    return Leader;
360
266
  }
361
2.60k
362
2.60k
  // Read associative section definitions and prepare to handle the comdat
363
2.60k
  // leader symbol by setting the section's ComdatDefs pointer if we encounter a
364
2.60k
  // non-associative comdat.
365
2.60k
  if (SparseChunks[SectionNumber] == PendingComdat) {
366
346
    if (auto *Def = Sym.getSectionDefinition()) {
367
346
      if (Def->Selection == IMAGE_COMDAT_SELECT_ASSOCIATIVE)
368
80
        readAssociativeDefinition(Sym, Def);
369
266
      else
370
266
        ComdatDefs[SectionNumber] = Def;
371
346
    }
372
346
  }
373
2.60k
374
2.60k
  if (SparseChunks[SectionNumber] == PendingComdat)
375
285
    return None;
376
2.31k
  return createRegular(Sym);
377
2.31k
}
378
379
439
MachineTypes ObjFile::getMachineType() {
380
439
  if (COFFObj)
381
439
    return static_cast<MachineTypes>(COFFObj->getMachine());
382
0
  return IMAGE_FILE_MACHINE_UNKNOWN;
383
0
}
384
385
21
StringRef ltrim1(StringRef S, const char *Chars) {
386
21
  if (!S.empty() && strchr(Chars, S[0]))
387
21
    return S.substr(1);
388
0
  return S;
389
0
}
390
391
78
void ImportFile::parse() {
392
78
  const char *Buf = MB.getBufferStart();
393
78
  const char *End = MB.getBufferEnd();
394
78
  const auto *Hdr = reinterpret_cast<const coff_import_header *>(Buf);
395
78
396
78
  // Check if the total size is valid.
397
78
  if ((size_t)(End - Buf) != (sizeof(*Hdr) + Hdr->SizeOfData))
398
0
    fatal("broken import library");
399
78
400
78
  // Read names and create an __imp_ symbol.
401
78
  StringRef Name = Saver.save(StringRef(Buf + sizeof(*Hdr)));
402
78
  StringRef ImpName = Saver.save("__imp_" + Name);
403
78
  const char *NameStart = Buf + sizeof(coff_import_header) + Name.size() + 1;
404
78
  DLLName = StringRef(NameStart);
405
78
  StringRef ExtName;
406
78
  switch (Hdr->getNameType()) {
407
78
  case IMPORT_ORDINAL:
408
9
    ExtName = "";
409
9
    break;
410
78
  case IMPORT_NAME:
411
48
    ExtName = Name;
412
48
    break;
413
78
  case IMPORT_NAME_NOPREFIX:
414
2
    ExtName = ltrim1(Name, "?@_");
415
2
    break;
416
78
  case IMPORT_NAME_UNDECORATE:
417
19
    ExtName = ltrim1(Name, "?@_");
418
19
    ExtName = ExtName.substr(0, ExtName.find('@'));
419
19
    break;
420
78
  }
421
78
422
78
  this->Hdr = Hdr;
423
78
  ExternalName = ExtName;
424
78
425
78
  ImpSym = Symtab->addImportData(ImpName, this);
426
78
427
78
  if (Hdr->getType() == llvm::COFF::IMPORT_CONST)
428
1
    static_cast<void>(Symtab->addImportData(Name, this));
429
78
430
78
  // If type is function, we need to create a thunk which jump to an
431
78
  // address pointed by the __imp_ symbol. (This allows you to call
432
78
  // DLL functions just like regular non-DLL functions.)
433
78
  if (Hdr->getType() == llvm::COFF::IMPORT_CODE)
434
76
    ThunkSym = Symtab->addImportThunk(Name, ImpSym, Hdr->Machine);
435
78
}
436
437
52
void BitcodeFile::parse() {
438
52
  Obj = check(lto::InputFile::create(MemoryBufferRef(
439
52
      MB.getBuffer(), Saver.save(ParentName + MB.getBufferIdentifier()))));
440
52
  std::vector<std::pair<Symbol *, bool>> Comdat(Obj->getComdatTable().size());
441
75
  for (size_t I = 0; I != Obj->getComdatTable().size(); 
++I23
)
442
23
    Comdat[I] = Symtab->addComdat(this, Saver.save(Obj->getComdatTable()[I]));
443
108
  for (const lto::InputFile::Symbol &ObjSym : Obj->symbols()) {
444
108
    StringRef SymName = Saver.save(ObjSym.getName());
445
108
    int ComdatIndex = ObjSym.getComdatIndex();
446
108
    Symbol *Sym;
447
108
    if (ObjSym.isUndefined()) {
448
22
      Sym = Symtab->addUndefined(SymName, this, false);
449
86
    } else if (ObjSym.isCommon()) {
450
0
      Sym = Symtab->addCommon(this, SymName, ObjSym.getCommonSize());
451
86
    } else if (ObjSym.isWeak() && 
ObjSym.isIndirect()17
) {
452
2
      // Weak external.
453
2
      Sym = Symtab->addUndefined(SymName, this, true);
454
2
      std::string Fallback = ObjSym.getCOFFWeakExternalFallback();
455
2
      Symbol *Alias = Symtab->addUndefined(Saver.save(Fallback));
456
2
      checkAndSetWeakAlias(Symtab, this, Sym, Alias);
457
84
    } else if (ComdatIndex != -1) {
458
23
      if (SymName == Obj->getComdatTable()[ComdatIndex])
459
23
        Sym = Comdat[ComdatIndex].first;
460
0
      else if (Comdat[ComdatIndex].second)
461
0
        Sym = Symtab->addRegular(this, SymName);
462
0
      else
463
0
        Sym = Symtab->addUndefined(SymName, this, false);
464
61
    } else {
465
61
      Sym = Symtab->addRegular(this, SymName);
466
61
    }
467
108
    Symbols.push_back(Sym);
468
108
  }
469
52
  Directives = Obj->getCOFFLinkerOpts();
470
52
}
471
472
52
MachineTypes BitcodeFile::getMachineType() {
473
52
  switch (Triple(Obj->getTargetTriple()).getArch()) {
474
52
  case Triple::x86_64:
475
50
    return AMD64;
476
52
  case Triple::x86:
477
2
    return I386;
478
52
  case Triple::arm:
479
0
    return ARMNT;
480
52
  case Triple::aarch64:
481
0
    return ARM64;
482
52
  default:
483
0
    return IMAGE_FILE_MACHINE_UNKNOWN;
484
52
  }
485
52
}
486
} // namespace coff
487
} // namespace lld
488
489
// Returns the last element of a path, which is supposed to be a filename.
490
126
static StringRef getBasename(StringRef Path) {
491
126
  size_t Pos = Path.find_last_of("\\/");
492
126
  if (Pos == StringRef::npos)
493
62
    return Path;
494
64
  return Path.substr(Pos + 1);
495
64
}
496
497
// Returns a string in the format of "foo.obj" or "foo.obj(bar.lib)".
498
839
std::string lld::toString(const coff::InputFile *File) {
499
839
  if (!File)
500
0
    return "<internal>";
501
839
  if (File->ParentName.empty())
502
776
    return File->getName();
503
63
504
63
  return (getBasename(File->ParentName) + "(" + getBasename(File->getName()) +
505
63
          ")")
506
63
      .str();
507
63
}