xref: /src/contrib/llvm-project/lld/COFF/MapFile.cpp (revision 5f757f3ff9144b609b3c433dfd370cc6bdc191ad)
1d2d3ebb8SDimitry Andric //===- MapFile.cpp --------------------------------------------------------===//
2d2d3ebb8SDimitry Andric //
3f1e1c239SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4f1e1c239SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5f1e1c239SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6d2d3ebb8SDimitry Andric //
7d2d3ebb8SDimitry Andric //===----------------------------------------------------------------------===//
8d2d3ebb8SDimitry Andric //
9cfca06d7SDimitry Andric // This file implements the /map option in the same format as link.exe
10cfca06d7SDimitry Andric // (based on observations)
11d2d3ebb8SDimitry Andric //
12cfca06d7SDimitry Andric // Header (program name, timestamp info, preferred load address)
13d2d3ebb8SDimitry Andric //
14cfca06d7SDimitry Andric // Section list (Start = Section index:Base address):
15cfca06d7SDimitry Andric // Start         Length     Name                   Class
16cfca06d7SDimitry Andric // 0001:00001000 00000015H .text                   CODE
17cfca06d7SDimitry Andric //
18cfca06d7SDimitry Andric // Symbols list:
19cfca06d7SDimitry Andric // Address        Publics by Value    Rva + Base          Lib:Object
20cfca06d7SDimitry Andric // 0001:00001000  main                 0000000140001000    main.obj
21cfca06d7SDimitry Andric // 0001:00001300  ?__scrt_common_main@@YAHXZ  0000000140001300 libcmt:exe_main.obj
22cfca06d7SDimitry Andric //
23cfca06d7SDimitry Andric // entry point at        0001:00000360
24cfca06d7SDimitry Andric //
25cfca06d7SDimitry Andric // Static symbols
26cfca06d7SDimitry Andric //
27cfca06d7SDimitry Andric // 0000:00000000  __guard_fids__       0000000140000000     libcmt : exe_main.obj
28d2d3ebb8SDimitry Andric //===----------------------------------------------------------------------===//
29d2d3ebb8SDimitry Andric 
30d2d3ebb8SDimitry Andric #include "MapFile.h"
31c0981da4SDimitry Andric #include "COFFLinkerContext.h"
32274c9ff5SDimitry Andric #include "SymbolTable.h"
33d2d3ebb8SDimitry Andric #include "Symbols.h"
34d2d3ebb8SDimitry Andric #include "Writer.h"
35eb1ff93dSDimitry Andric #include "lld/Common/ErrorHandler.h"
36cfca06d7SDimitry Andric #include "lld/Common/Timer.h"
37cfca06d7SDimitry Andric #include "llvm/Support/Parallel.h"
38cfca06d7SDimitry Andric #include "llvm/Support/Path.h"
39b1c73532SDimitry Andric #include "llvm/Support/TimeProfiler.h"
40d2d3ebb8SDimitry Andric #include "llvm/Support/raw_ostream.h"
41d2d3ebb8SDimitry Andric 
42d2d3ebb8SDimitry Andric using namespace llvm;
43d2d3ebb8SDimitry Andric using namespace llvm::object;
44cfca06d7SDimitry Andric using namespace lld;
45cfca06d7SDimitry Andric using namespace lld::coff;
46d2d3ebb8SDimitry Andric 
47cfca06d7SDimitry Andric // Print out the first two columns of a line.
writeHeader(raw_ostream & os,uint32_t sec,uint64_t addr)48cfca06d7SDimitry Andric static void writeHeader(raw_ostream &os, uint32_t sec, uint64_t addr) {
49cfca06d7SDimitry Andric   os << format(" %04x:%08llx", sec, addr);
50d2d3ebb8SDimitry Andric }
51d2d3ebb8SDimitry Andric 
52cfca06d7SDimitry Andric // Write the time stamp with the format used by link.exe
53cfca06d7SDimitry Andric // It seems identical to strftime with "%c" on msvc build, but we need a
54cfca06d7SDimitry Andric // locale-agnostic version.
writeFormattedTimestamp(raw_ostream & os,time_t tds)55cfca06d7SDimitry Andric static void writeFormattedTimestamp(raw_ostream &os, time_t tds) {
56cfca06d7SDimitry Andric   constexpr const char *const days[7] = {"Sun", "Mon", "Tue", "Wed",
57cfca06d7SDimitry Andric                                          "Thu", "Fri", "Sat"};
58cfca06d7SDimitry Andric   constexpr const char *const months[12] = {"Jan", "Feb", "Mar", "Apr",
59cfca06d7SDimitry Andric                                             "May", "Jun", "Jul", "Aug",
60cfca06d7SDimitry Andric                                             "Sep", "Oct", "Nov", "Dec"};
61cfca06d7SDimitry Andric   tm *time = localtime(&tds);
62cfca06d7SDimitry Andric   os << format("%s %s %2d %02d:%02d:%02d %d", days[time->tm_wday],
63cfca06d7SDimitry Andric                months[time->tm_mon], time->tm_mday, time->tm_hour, time->tm_min,
64cfca06d7SDimitry Andric                time->tm_sec, time->tm_year + 1900);
65d2d3ebb8SDimitry Andric }
66d2d3ebb8SDimitry Andric 
sortUniqueSymbols(std::vector<Defined * > & syms,uint64_t imageBase)67e3b55780SDimitry Andric static void sortUniqueSymbols(std::vector<Defined *> &syms,
68e3b55780SDimitry Andric                               uint64_t imageBase) {
69cfca06d7SDimitry Andric   // Build helper vector
70cfca06d7SDimitry Andric   using SortEntry = std::pair<Defined *, size_t>;
71cfca06d7SDimitry Andric   std::vector<SortEntry> v;
72cfca06d7SDimitry Andric   v.resize(syms.size());
73cfca06d7SDimitry Andric   for (size_t i = 0, e = syms.size(); i < e; ++i)
74cfca06d7SDimitry Andric     v[i] = SortEntry(syms[i], i);
75274c9ff5SDimitry Andric 
76cfca06d7SDimitry Andric   // Remove duplicate symbol pointers
77cfca06d7SDimitry Andric   parallelSort(v, std::less<SortEntry>());
78cfca06d7SDimitry Andric   auto end = std::unique(v.begin(), v.end(),
79cfca06d7SDimitry Andric                          [](const SortEntry &a, const SortEntry &b) {
80cfca06d7SDimitry Andric                            return a.first == b.first;
81274c9ff5SDimitry Andric                          });
82cfca06d7SDimitry Andric   v.erase(end, v.end());
83cfca06d7SDimitry Andric 
84cfca06d7SDimitry Andric   // Sort by RVA then original order
85e3b55780SDimitry Andric   parallelSort(v, [imageBase](const SortEntry &a, const SortEntry &b) {
86e3b55780SDimitry Andric     // Add config.imageBase to avoid comparing "negative" RVAs.
87cfca06d7SDimitry Andric     // This can happen with symbols of Absolute kind
88e3b55780SDimitry Andric     uint64_t rvaa = imageBase + a.first->getRVA();
89e3b55780SDimitry Andric     uint64_t rvab = imageBase + b.first->getRVA();
90cfca06d7SDimitry Andric     return rvaa < rvab || (rvaa == rvab && a.second < b.second);
91cfca06d7SDimitry Andric   });
92cfca06d7SDimitry Andric 
93cfca06d7SDimitry Andric   syms.resize(v.size());
94cfca06d7SDimitry Andric   for (size_t i = 0, e = v.size(); i < e; ++i)
95cfca06d7SDimitry Andric     syms[i] = v[i].first;
96274c9ff5SDimitry Andric }
97cfca06d7SDimitry Andric 
98cfca06d7SDimitry Andric // Returns the lists of all symbols that we want to print out.
getSymbols(const COFFLinkerContext & ctx,std::vector<Defined * > & syms,std::vector<Defined * > & staticSyms)99c0981da4SDimitry Andric static void getSymbols(const COFFLinkerContext &ctx,
100c0981da4SDimitry Andric                        std::vector<Defined *> &syms,
101cfca06d7SDimitry Andric                        std::vector<Defined *> &staticSyms) {
102cfca06d7SDimitry Andric 
103c0981da4SDimitry Andric   for (ObjFile *file : ctx.objFileInstances)
104cfca06d7SDimitry Andric     for (Symbol *b : file->getSymbols()) {
105cfca06d7SDimitry Andric       if (!b || !b->isLive())
106cfca06d7SDimitry Andric         continue;
107cfca06d7SDimitry Andric       if (auto *sym = dyn_cast<DefinedCOFF>(b)) {
108cfca06d7SDimitry Andric         COFFSymbolRef symRef = sym->getCOFFSymbol();
109cfca06d7SDimitry Andric         if (!symRef.isSectionDefinition() &&
110cfca06d7SDimitry Andric             symRef.getStorageClass() != COFF::IMAGE_SYM_CLASS_LABEL) {
111cfca06d7SDimitry Andric           if (symRef.getStorageClass() == COFF::IMAGE_SYM_CLASS_STATIC)
112cfca06d7SDimitry Andric             staticSyms.push_back(sym);
113cfca06d7SDimitry Andric           else
114cfca06d7SDimitry Andric             syms.push_back(sym);
115cfca06d7SDimitry Andric         }
116cfca06d7SDimitry Andric       } else if (auto *sym = dyn_cast<Defined>(b)) {
117cfca06d7SDimitry Andric         syms.push_back(sym);
118cfca06d7SDimitry Andric       }
119cfca06d7SDimitry Andric     }
120cfca06d7SDimitry Andric 
121c0981da4SDimitry Andric   for (ImportFile *file : ctx.importFileInstances) {
122cfca06d7SDimitry Andric     if (!file->live)
123cfca06d7SDimitry Andric       continue;
124cfca06d7SDimitry Andric 
125cfca06d7SDimitry Andric     if (!file->thunkSym)
126cfca06d7SDimitry Andric       continue;
127cfca06d7SDimitry Andric 
128cfca06d7SDimitry Andric     if (!file->thunkLive)
129cfca06d7SDimitry Andric       continue;
130cfca06d7SDimitry Andric 
131cfca06d7SDimitry Andric     if (auto *thunkSym = dyn_cast<Defined>(file->thunkSym))
132cfca06d7SDimitry Andric       syms.push_back(thunkSym);
133cfca06d7SDimitry Andric 
134cfca06d7SDimitry Andric     if (auto *impSym = dyn_cast_or_null<Defined>(file->impSym))
135cfca06d7SDimitry Andric       syms.push_back(impSym);
136cfca06d7SDimitry Andric   }
137cfca06d7SDimitry Andric 
138e3b55780SDimitry Andric   sortUniqueSymbols(syms, ctx.config.imageBase);
139e3b55780SDimitry Andric   sortUniqueSymbols(staticSyms, ctx.config.imageBase);
140d2d3ebb8SDimitry Andric }
141d2d3ebb8SDimitry Andric 
142274c9ff5SDimitry Andric // Construct a map from symbols to their stringified representations.
143cfca06d7SDimitry Andric static DenseMap<Defined *, std::string>
getSymbolStrings(const COFFLinkerContext & ctx,ArrayRef<Defined * > syms)144c0981da4SDimitry Andric getSymbolStrings(const COFFLinkerContext &ctx, ArrayRef<Defined *> syms) {
145f1e1c239SDimitry Andric   std::vector<std::string> str(syms.size());
146145449b1SDimitry Andric   parallelFor((size_t)0, syms.size(), [&](size_t i) {
147f1e1c239SDimitry Andric     raw_string_ostream os(str[i]);
148cfca06d7SDimitry Andric     Defined *sym = syms[i];
149cfca06d7SDimitry Andric 
150cfca06d7SDimitry Andric     uint16_t sectionIdx = 0;
151cfca06d7SDimitry Andric     uint64_t address = 0;
152cfca06d7SDimitry Andric     SmallString<128> fileDescr;
153cfca06d7SDimitry Andric 
154cfca06d7SDimitry Andric     if (auto *absSym = dyn_cast<DefinedAbsolute>(sym)) {
155cfca06d7SDimitry Andric       address = absSym->getVA();
156cfca06d7SDimitry Andric       fileDescr = "<absolute>";
157cfca06d7SDimitry Andric     } else if (isa<DefinedSynthetic>(sym)) {
158cfca06d7SDimitry Andric       fileDescr = "<linker-defined>";
159cfca06d7SDimitry Andric     } else if (isa<DefinedCommon>(sym)) {
160cfca06d7SDimitry Andric       fileDescr = "<common>";
161cfca06d7SDimitry Andric     } else if (Chunk *chunk = sym->getChunk()) {
162cfca06d7SDimitry Andric       address = sym->getRVA();
163c0981da4SDimitry Andric       if (OutputSection *sec = ctx.getOutputSection(chunk))
164cfca06d7SDimitry Andric         address -= sec->header.VirtualAddress;
165cfca06d7SDimitry Andric 
166cfca06d7SDimitry Andric       sectionIdx = chunk->getOutputSectionIdx();
167cfca06d7SDimitry Andric 
168cfca06d7SDimitry Andric       InputFile *file;
169cfca06d7SDimitry Andric       if (auto *impSym = dyn_cast<DefinedImportData>(sym))
170cfca06d7SDimitry Andric         file = impSym->file;
171cfca06d7SDimitry Andric       else if (auto *thunkSym = dyn_cast<DefinedImportThunk>(sym))
172cfca06d7SDimitry Andric         file = thunkSym->wrappedSym->file;
173cfca06d7SDimitry Andric       else
174cfca06d7SDimitry Andric         file = sym->getFile();
175cfca06d7SDimitry Andric 
176cfca06d7SDimitry Andric       if (file) {
177cfca06d7SDimitry Andric         if (!file->parentName.empty()) {
178cfca06d7SDimitry Andric           fileDescr = sys::path::filename(file->parentName);
179cfca06d7SDimitry Andric           sys::path::replace_extension(fileDescr, "");
180cfca06d7SDimitry Andric           fileDescr += ":";
181cfca06d7SDimitry Andric         }
182cfca06d7SDimitry Andric         fileDescr += sys::path::filename(file->getName());
183cfca06d7SDimitry Andric       }
184cfca06d7SDimitry Andric     }
185cfca06d7SDimitry Andric     writeHeader(os, sectionIdx, address);
186cfca06d7SDimitry Andric     os << "       ";
187cfca06d7SDimitry Andric     os << left_justify(sym->getName(), 26);
188cfca06d7SDimitry Andric     os << " ";
189e3b55780SDimitry Andric     os << format_hex_no_prefix((ctx.config.imageBase + sym->getRVA()), 16);
190cfca06d7SDimitry Andric     if (!fileDescr.empty()) {
191cfca06d7SDimitry Andric       os << "     "; // FIXME : Handle "f" and "i" flags sometimes generated
192cfca06d7SDimitry Andric                      // by link.exe in those spaces
193cfca06d7SDimitry Andric       os << fileDescr;
194cfca06d7SDimitry Andric     }
195274c9ff5SDimitry Andric   });
196d2d3ebb8SDimitry Andric 
197cfca06d7SDimitry Andric   DenseMap<Defined *, std::string> ret;
198f1e1c239SDimitry Andric   for (size_t i = 0, e = syms.size(); i < e; ++i)
199f1e1c239SDimitry Andric     ret[syms[i]] = std::move(str[i]);
200f1e1c239SDimitry Andric   return ret;
201d2d3ebb8SDimitry Andric }
202d2d3ebb8SDimitry Andric 
writeMapFile(COFFLinkerContext & ctx)203c0981da4SDimitry Andric void lld::coff::writeMapFile(COFFLinkerContext &ctx) {
204e3b55780SDimitry Andric   if (ctx.config.mapFile.empty())
205d2d3ebb8SDimitry Andric     return;
206d2d3ebb8SDimitry Andric 
207b1c73532SDimitry Andric   llvm::TimeTraceScope timeScope("Map file");
208f1e1c239SDimitry Andric   std::error_code ec;
209e3b55780SDimitry Andric   raw_fd_ostream os(ctx.config.mapFile, ec, sys::fs::OF_None);
210f1e1c239SDimitry Andric   if (ec)
211e3b55780SDimitry Andric     fatal("cannot open " + ctx.config.mapFile + ": " + ec.message());
212274c9ff5SDimitry Andric 
213c0981da4SDimitry Andric   ScopedTimer t1(ctx.totalMapTimer);
214cfca06d7SDimitry Andric 
215274c9ff5SDimitry Andric   // Collect symbol info that we want to print out.
216c0981da4SDimitry Andric   ScopedTimer t2(ctx.symbolGatherTimer);
217cfca06d7SDimitry Andric   std::vector<Defined *> syms;
218cfca06d7SDimitry Andric   std::vector<Defined *> staticSyms;
219c0981da4SDimitry Andric   getSymbols(ctx, syms, staticSyms);
220cfca06d7SDimitry Andric   t2.stop();
221274c9ff5SDimitry Andric 
222c0981da4SDimitry Andric   ScopedTimer t3(ctx.symbolStringsTimer);
223c0981da4SDimitry Andric   DenseMap<Defined *, std::string> symStr = getSymbolStrings(ctx, syms);
224c0981da4SDimitry Andric   DenseMap<Defined *, std::string> staticSymStr =
225c0981da4SDimitry Andric       getSymbolStrings(ctx, staticSyms);
226cfca06d7SDimitry Andric   t3.stop();
227274c9ff5SDimitry Andric 
228c0981da4SDimitry Andric   ScopedTimer t4(ctx.writeTimer);
229e3b55780SDimitry Andric   SmallString<128> AppName = sys::path::filename(ctx.config.outputFile);
230cfca06d7SDimitry Andric   sys::path::replace_extension(AppName, "");
231cfca06d7SDimitry Andric 
232cfca06d7SDimitry Andric   // Print out the file header
233cfca06d7SDimitry Andric   os << " " << AppName << "\n";
234cfca06d7SDimitry Andric   os << "\n";
235cfca06d7SDimitry Andric 
236e3b55780SDimitry Andric   os << " Timestamp is " << format_hex_no_prefix(ctx.config.timestamp, 8)
237e3b55780SDimitry Andric      << " (";
238e3b55780SDimitry Andric   if (ctx.config.repro) {
239cfca06d7SDimitry Andric     os << "Repro mode";
240cfca06d7SDimitry Andric   } else {
241e3b55780SDimitry Andric     writeFormattedTimestamp(os, ctx.config.timestamp);
242cfca06d7SDimitry Andric   }
243cfca06d7SDimitry Andric   os << ")\n";
244cfca06d7SDimitry Andric 
245cfca06d7SDimitry Andric   os << "\n";
246cfca06d7SDimitry Andric   os << " Preferred load address is "
247e3b55780SDimitry Andric      << format_hex_no_prefix(ctx.config.imageBase, 16) << "\n";
248cfca06d7SDimitry Andric   os << "\n";
249cfca06d7SDimitry Andric 
250cfca06d7SDimitry Andric   // Print out section table.
251cfca06d7SDimitry Andric   os << " Start         Length     Name                   Class\n";
252cfca06d7SDimitry Andric 
253c0981da4SDimitry Andric   for (OutputSection *sec : ctx.outputSections) {
254cfca06d7SDimitry Andric     // Merge display of chunks with same sectionName
255cfca06d7SDimitry Andric     std::vector<std::pair<SectionChunk *, SectionChunk *>> ChunkRanges;
256f1e1c239SDimitry Andric     for (Chunk *c : sec->chunks) {
257f1e1c239SDimitry Andric       auto *sc = dyn_cast<SectionChunk>(c);
258f1e1c239SDimitry Andric       if (!sc)
259274c9ff5SDimitry Andric         continue;
260274c9ff5SDimitry Andric 
261cfca06d7SDimitry Andric       if (ChunkRanges.empty() ||
262cfca06d7SDimitry Andric           c->getSectionName() != ChunkRanges.back().first->getSectionName()) {
263cfca06d7SDimitry Andric         ChunkRanges.emplace_back(sc, sc);
264cfca06d7SDimitry Andric       } else {
265cfca06d7SDimitry Andric         ChunkRanges.back().second = sc;
266274c9ff5SDimitry Andric       }
267d2d3ebb8SDimitry Andric     }
268d2bd9e70SDimitry Andric 
269cfca06d7SDimitry Andric     const bool isCodeSection =
270cfca06d7SDimitry Andric         (sec->header.Characteristics & COFF::IMAGE_SCN_CNT_CODE) &&
271cfca06d7SDimitry Andric         (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_READ) &&
272cfca06d7SDimitry Andric         (sec->header.Characteristics & COFF::IMAGE_SCN_MEM_EXECUTE);
273cfca06d7SDimitry Andric     StringRef SectionClass = (isCodeSection ? "CODE" : "DATA");
274cfca06d7SDimitry Andric 
275cfca06d7SDimitry Andric     for (auto &cr : ChunkRanges) {
276cfca06d7SDimitry Andric       size_t size =
277cfca06d7SDimitry Andric           cr.second->getRVA() + cr.second->getSize() - cr.first->getRVA();
278cfca06d7SDimitry Andric 
279cfca06d7SDimitry Andric       auto address = cr.first->getRVA() - sec->header.VirtualAddress;
280cfca06d7SDimitry Andric       writeHeader(os, sec->sectionIndex, address);
281cfca06d7SDimitry Andric       os << " " << format_hex_no_prefix(size, 8) << "H";
282cfca06d7SDimitry Andric       os << " " << left_justify(cr.first->getSectionName(), 23);
283cfca06d7SDimitry Andric       os << " " << SectionClass;
284cfca06d7SDimitry Andric       os << '\n';
285cfca06d7SDimitry Andric     }
286cfca06d7SDimitry Andric   }
287cfca06d7SDimitry Andric 
288cfca06d7SDimitry Andric   // Print out the symbols table (without static symbols)
289cfca06d7SDimitry Andric   os << "\n";
290cfca06d7SDimitry Andric   os << "  Address         Publics by Value              Rva+Base"
291cfca06d7SDimitry Andric         "               Lib:Object\n";
292cfca06d7SDimitry Andric   os << "\n";
293cfca06d7SDimitry Andric   for (Defined *sym : syms)
294cfca06d7SDimitry Andric     os << symStr[sym] << '\n';
295cfca06d7SDimitry Andric 
296cfca06d7SDimitry Andric   // Print out the entry point.
297cfca06d7SDimitry Andric   os << "\n";
298cfca06d7SDimitry Andric 
299cfca06d7SDimitry Andric   uint16_t entrySecIndex = 0;
300cfca06d7SDimitry Andric   uint64_t entryAddress = 0;
301cfca06d7SDimitry Andric 
302e3b55780SDimitry Andric   if (!ctx.config.noEntry) {
303e3b55780SDimitry Andric     Defined *entry = dyn_cast_or_null<Defined>(ctx.config.entry);
304cfca06d7SDimitry Andric     if (entry) {
305cfca06d7SDimitry Andric       Chunk *chunk = entry->getChunk();
306cfca06d7SDimitry Andric       entrySecIndex = chunk->getOutputSectionIdx();
307cfca06d7SDimitry Andric       entryAddress =
308c0981da4SDimitry Andric           entry->getRVA() - ctx.getOutputSection(chunk)->header.VirtualAddress;
309cfca06d7SDimitry Andric     }
310cfca06d7SDimitry Andric   }
311cfca06d7SDimitry Andric   os << " entry point at         ";
312cfca06d7SDimitry Andric   os << format("%04x:%08llx", entrySecIndex, entryAddress);
313cfca06d7SDimitry Andric   os << "\n";
314cfca06d7SDimitry Andric 
315cfca06d7SDimitry Andric   // Print out the static symbols
316cfca06d7SDimitry Andric   os << "\n";
317cfca06d7SDimitry Andric   os << " Static symbols\n";
318cfca06d7SDimitry Andric   os << "\n";
319cfca06d7SDimitry Andric   for (Defined *sym : staticSyms)
320cfca06d7SDimitry Andric     os << staticSymStr[sym] << '\n';
321cfca06d7SDimitry Andric 
322e3b55780SDimitry Andric   // Print out the exported functions
323e3b55780SDimitry Andric   if (ctx.config.mapInfo) {
324e3b55780SDimitry Andric     os << "\n";
325e3b55780SDimitry Andric     os << " Exports\n";
326e3b55780SDimitry Andric     os << "\n";
327e3b55780SDimitry Andric     os << "  ordinal    name\n\n";
328e3b55780SDimitry Andric     for (Export &e : ctx.config.exports) {
329e3b55780SDimitry Andric       os << format("  %7d", e.ordinal) << "    " << e.name << "\n";
330e3b55780SDimitry Andric       if (!e.extName.empty() && e.extName != e.name)
331e3b55780SDimitry Andric         os << "               exported name: " << e.extName << "\n";
332e3b55780SDimitry Andric     }
333e3b55780SDimitry Andric   }
334e3b55780SDimitry Andric 
335cfca06d7SDimitry Andric   t4.stop();
336cfca06d7SDimitry Andric   t1.stop();
337cfca06d7SDimitry Andric }
338