1 //===-- llvm-size.cpp - Print the size of each object section ---*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This program is a utility that works like traditional Unix "size",
10 // that is, it prints out the size of each section, and the total size of all
11 // sections.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #include "llvm/ADT/APInt.h"
16 #include "llvm/Object/Archive.h"
17 #include "llvm/Object/ELFObjectFile.h"
18 #include "llvm/Object/MachO.h"
19 #include "llvm/Object/MachOUniversal.h"
20 #include "llvm/Object/ObjectFile.h"
21 #include "llvm/Option/Arg.h"
22 #include "llvm/Option/ArgList.h"
23 #include "llvm/Option/Option.h"
24 #include "llvm/Support/Casting.h"
25 #include "llvm/Support/CommandLine.h"
26 #include "llvm/Support/FileSystem.h"
27 #include "llvm/Support/Format.h"
28 #include "llvm/Support/InitLLVM.h"
29 #include "llvm/Support/MemoryBuffer.h"
30 #include "llvm/Support/WithColor.h"
31 #include "llvm/Support/raw_ostream.h"
32 #include <algorithm>
33 #include <string>
34 #include <system_error>
35 
36 using namespace llvm;
37 using namespace object;
38 
39 namespace {
40 using namespace llvm::opt; // for HelpHidden in Opts.inc
41 enum ID {
42   OPT_INVALID = 0, // This is not an option ID.
43 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
44                HELPTEXT, METAVAR, VALUES)                                      \
45   OPT_##ID,
46 #include "Opts.inc"
47 #undef OPTION
48 };
49 
50 #define PREFIX(NAME, VALUE)                                                    \
51   static constexpr StringLiteral NAME##_init[] = VALUE;                        \
52   static constexpr ArrayRef<StringLiteral> NAME(NAME##_init,                   \
53                                                 std::size(NAME##_init) - 1);
54 #include "Opts.inc"
55 #undef PREFIX
56 
57 static constexpr opt::OptTable::Info InfoTable[] = {
58 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
59                HELPTEXT, METAVAR, VALUES)                                      \
60   {                                                                            \
61       PREFIX,      NAME,      HELPTEXT,                                        \
62       METAVAR,     OPT_##ID,  opt::Option::KIND##Class,                        \
63       PARAM,       FLAGS,     OPT_##GROUP,                                     \
64       OPT_##ALIAS, ALIASARGS, VALUES},
65 #include "Opts.inc"
66 #undef OPTION
67 };
68 
69 class SizeOptTable : public opt::GenericOptTable {
70 public:
71   SizeOptTable() : GenericOptTable(InfoTable) { setGroupedShortOptions(true); }
72 };
73 
74 enum OutputFormatTy { berkeley, sysv, darwin };
75 enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 };
76 } // namespace
77 
78 static bool ArchAll = false;
79 static std::vector<StringRef> ArchFlags;
80 static bool ELFCommons;
81 static OutputFormatTy OutputFormat;
82 static bool DarwinLongFormat;
83 static RadixTy Radix;
84 static bool TotalSizes;
85 
86 static std::vector<std::string> InputFilenames;
87 
88 static std::string ToolName;
89 
90 // States
91 static bool HadError = false;
92 static bool BerkeleyHeaderPrinted = false;
93 static bool MoreThanOneFile = false;
94 static uint64_t TotalObjectText = 0;
95 static uint64_t TotalObjectData = 0;
96 static uint64_t TotalObjectBss = 0;
97 static uint64_t TotalObjectTotal = 0;
98 
99 static void error(const Twine &Message, StringRef File = "") {
100   HadError = true;
101   if (File.empty())
102     WithColor::error(errs(), ToolName) << Message << '\n';
103   else
104     WithColor::error(errs(), ToolName)
105         << "'" << File << "': " << Message << '\n';
106 }
107 
108 // This version of error() prints the archive name and member name, for example:
109 // "libx.a(foo.o)" after the ToolName before the error message.  It sets
110 // HadError but returns allowing the code to move on to other archive members.
111 static void error(llvm::Error E, StringRef FileName, const Archive::Child &C,
112                   StringRef ArchitectureName = StringRef()) {
113   HadError = true;
114   WithColor::error(errs(), ToolName) << "'" << FileName << "'";
115 
116   Expected<StringRef> NameOrErr = C.getName();
117   // TODO: if we have a error getting the name then it would be nice to print
118   // the index of which archive member this is and or its offset in the
119   // archive instead of "???" as the name.
120   if (!NameOrErr) {
121     consumeError(NameOrErr.takeError());
122     errs() << "(" << "???" << ")";
123   } else
124     errs() << "(" << NameOrErr.get() << ")";
125 
126   if (!ArchitectureName.empty())
127     errs() << " (for architecture " << ArchitectureName << ") ";
128 
129   std::string Buf;
130   raw_string_ostream OS(Buf);
131   logAllUnhandledErrors(std::move(E), OS);
132   OS.flush();
133   errs() << ": " << Buf << "\n";
134 }
135 
136 // This version of error() prints the file name and which architecture slice it // is from, for example: "foo.o (for architecture i386)" after the ToolName
137 // before the error message.  It sets HadError but returns allowing the code to
138 // move on to other architecture slices.
139 static void error(llvm::Error E, StringRef FileName,
140                   StringRef ArchitectureName = StringRef()) {
141   HadError = true;
142   WithColor::error(errs(), ToolName) << "'" << FileName << "'";
143 
144   if (!ArchitectureName.empty())
145     errs() << " (for architecture " << ArchitectureName << ") ";
146 
147   std::string Buf;
148   raw_string_ostream OS(Buf);
149   logAllUnhandledErrors(std::move(E), OS);
150   OS.flush();
151   errs() << ": " << Buf << "\n";
152 }
153 
154 /// Get the length of the string that represents @p num in Radix including the
155 /// leading 0x or 0 for hexadecimal and octal respectively.
156 static size_t getNumLengthAsString(uint64_t num) {
157   APInt conv(64, num);
158   SmallString<32> result;
159   conv.toString(result, Radix, false, true);
160   return result.size();
161 }
162 
163 /// Return the printing format for the Radix.
164 static const char *getRadixFmt() {
165   switch (Radix) {
166   case octal:
167     return PRIo64;
168   case decimal:
169     return PRIu64;
170   case hexadecimal:
171     return PRIx64;
172   }
173   return nullptr;
174 }
175 
176 /// Remove unneeded ELF sections from calculation
177 static bool considerForSize(ObjectFile *Obj, SectionRef Section) {
178   if (!Obj->isELF())
179     return true;
180   switch (static_cast<ELFSectionRef>(Section).getType()) {
181   case ELF::SHT_NULL:
182   case ELF::SHT_SYMTAB:
183     return false;
184   case ELF::SHT_STRTAB:
185   case ELF::SHT_REL:
186   case ELF::SHT_RELA:
187     return static_cast<ELFSectionRef>(Section).getFlags() & ELF::SHF_ALLOC;
188   }
189   return true;
190 }
191 
192 /// Total size of all ELF common symbols
193 static Expected<uint64_t> getCommonSize(ObjectFile *Obj) {
194   uint64_t TotalCommons = 0;
195   for (auto &Sym : Obj->symbols()) {
196     Expected<uint32_t> SymFlagsOrErr =
197         Obj->getSymbolFlags(Sym.getRawDataRefImpl());
198     if (!SymFlagsOrErr)
199       return SymFlagsOrErr.takeError();
200     if (*SymFlagsOrErr & SymbolRef::SF_Common)
201       TotalCommons += Obj->getCommonSymbolSize(Sym.getRawDataRefImpl());
202   }
203   return TotalCommons;
204 }
205 
206 /// Print the size of each Mach-O segment and section in @p MachO.
207 ///
208 /// This is when used when @c OutputFormat is darwin and produces the same
209 /// output as darwin's size(1) -m output.
210 static void printDarwinSectionSizes(MachOObjectFile *MachO) {
211   std::string fmtbuf;
212   raw_string_ostream fmt(fmtbuf);
213   const char *radix_fmt = getRadixFmt();
214   if (Radix == hexadecimal)
215     fmt << "0x";
216   fmt << "%" << radix_fmt;
217 
218   uint32_t Filetype = MachO->getHeader().filetype;
219 
220   uint64_t total = 0;
221   for (const auto &Load : MachO->load_commands()) {
222     if (Load.C.cmd == MachO::LC_SEGMENT_64) {
223       MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load);
224       outs() << "Segment " << Seg.segname << ": "
225              << format(fmt.str().c_str(), Seg.vmsize);
226       if (DarwinLongFormat)
227         outs() << " (vmaddr 0x" << format("%" PRIx64, Seg.vmaddr) << " fileoff "
228                << Seg.fileoff << ")";
229       outs() << "\n";
230       total += Seg.vmsize;
231       uint64_t sec_total = 0;
232       for (unsigned J = 0; J < Seg.nsects; ++J) {
233         MachO::section_64 Sec = MachO->getSection64(Load, J);
234         if (Filetype == MachO::MH_OBJECT)
235           outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", "
236                  << format("%.16s", &Sec.sectname) << "): ";
237         else
238           outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": ";
239         outs() << format(fmt.str().c_str(), Sec.size);
240         if (DarwinLongFormat)
241           outs() << " (addr 0x" << format("%" PRIx64, Sec.addr) << " offset "
242                  << Sec.offset << ")";
243         outs() << "\n";
244         sec_total += Sec.size;
245       }
246       if (Seg.nsects != 0)
247         outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n";
248     } else if (Load.C.cmd == MachO::LC_SEGMENT) {
249       MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load);
250       uint64_t Seg_vmsize = Seg.vmsize;
251       outs() << "Segment " << Seg.segname << ": "
252              << format(fmt.str().c_str(), Seg_vmsize);
253       if (DarwinLongFormat)
254         outs() << " (vmaddr 0x" << format("%" PRIx32, Seg.vmaddr) << " fileoff "
255                << Seg.fileoff << ")";
256       outs() << "\n";
257       total += Seg.vmsize;
258       uint64_t sec_total = 0;
259       for (unsigned J = 0; J < Seg.nsects; ++J) {
260         MachO::section Sec = MachO->getSection(Load, J);
261         if (Filetype == MachO::MH_OBJECT)
262           outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", "
263                  << format("%.16s", &Sec.sectname) << "): ";
264         else
265           outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": ";
266         uint64_t Sec_size = Sec.size;
267         outs() << format(fmt.str().c_str(), Sec_size);
268         if (DarwinLongFormat)
269           outs() << " (addr 0x" << format("%" PRIx32, Sec.addr) << " offset "
270                  << Sec.offset << ")";
271         outs() << "\n";
272         sec_total += Sec.size;
273       }
274       if (Seg.nsects != 0)
275         outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n";
276     }
277   }
278   outs() << "total " << format(fmt.str().c_str(), total) << "\n";
279 }
280 
281 /// Print the summary sizes of the standard Mach-O segments in @p MachO.
282 ///
283 /// This is when used when @c OutputFormat is berkeley with a Mach-O file and
284 /// produces the same output as darwin's size(1) default output.
285 static void printDarwinSegmentSizes(MachOObjectFile *MachO) {
286   uint64_t total_text = 0;
287   uint64_t total_data = 0;
288   uint64_t total_objc = 0;
289   uint64_t total_others = 0;
290   for (const auto &Load : MachO->load_commands()) {
291     if (Load.C.cmd == MachO::LC_SEGMENT_64) {
292       MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load);
293       if (MachO->getHeader().filetype == MachO::MH_OBJECT) {
294         for (unsigned J = 0; J < Seg.nsects; ++J) {
295           MachO::section_64 Sec = MachO->getSection64(Load, J);
296           StringRef SegmentName = StringRef(Sec.segname);
297           if (SegmentName == "__TEXT")
298             total_text += Sec.size;
299           else if (SegmentName == "__DATA")
300             total_data += Sec.size;
301           else if (SegmentName == "__OBJC")
302             total_objc += Sec.size;
303           else
304             total_others += Sec.size;
305         }
306       } else {
307         StringRef SegmentName = StringRef(Seg.segname);
308         if (SegmentName == "__TEXT")
309           total_text += Seg.vmsize;
310         else if (SegmentName == "__DATA")
311           total_data += Seg.vmsize;
312         else if (SegmentName == "__OBJC")
313           total_objc += Seg.vmsize;
314         else
315           total_others += Seg.vmsize;
316       }
317     } else if (Load.C.cmd == MachO::LC_SEGMENT) {
318       MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load);
319       if (MachO->getHeader().filetype == MachO::MH_OBJECT) {
320         for (unsigned J = 0; J < Seg.nsects; ++J) {
321           MachO::section Sec = MachO->getSection(Load, J);
322           StringRef SegmentName = StringRef(Sec.segname);
323           if (SegmentName == "__TEXT")
324             total_text += Sec.size;
325           else if (SegmentName == "__DATA")
326             total_data += Sec.size;
327           else if (SegmentName == "__OBJC")
328             total_objc += Sec.size;
329           else
330             total_others += Sec.size;
331         }
332       } else {
333         StringRef SegmentName = StringRef(Seg.segname);
334         if (SegmentName == "__TEXT")
335           total_text += Seg.vmsize;
336         else if (SegmentName == "__DATA")
337           total_data += Seg.vmsize;
338         else if (SegmentName == "__OBJC")
339           total_objc += Seg.vmsize;
340         else
341           total_others += Seg.vmsize;
342       }
343     }
344   }
345   uint64_t total = total_text + total_data + total_objc + total_others;
346 
347   if (!BerkeleyHeaderPrinted) {
348     outs() << "__TEXT\t__DATA\t__OBJC\tothers\tdec\thex\n";
349     BerkeleyHeaderPrinted = true;
350   }
351   outs() << total_text << "\t" << total_data << "\t" << total_objc << "\t"
352          << total_others << "\t" << total << "\t" << format("%" PRIx64, total)
353          << "\t";
354 }
355 
356 /// Print the size of each section in @p Obj.
357 ///
358 /// The format used is determined by @c OutputFormat and @c Radix.
359 static void printObjectSectionSizes(ObjectFile *Obj) {
360   uint64_t total = 0;
361   std::string fmtbuf;
362   raw_string_ostream fmt(fmtbuf);
363   const char *radix_fmt = getRadixFmt();
364 
365   // If OutputFormat is darwin and we have a MachOObjectFile print as darwin's
366   // size(1) -m output, else if OutputFormat is darwin and not a Mach-O object
367   // let it fall through to OutputFormat berkeley.
368   MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Obj);
369   if (OutputFormat == darwin && MachO)
370     printDarwinSectionSizes(MachO);
371   // If we have a MachOObjectFile and the OutputFormat is berkeley print as
372   // darwin's default berkeley format for Mach-O files.
373   else if (MachO && OutputFormat == berkeley)
374     printDarwinSegmentSizes(MachO);
375   else if (OutputFormat == sysv) {
376     // Run two passes over all sections. The first gets the lengths needed for
377     // formatting the output. The second actually does the output.
378     std::size_t max_name_len = strlen("section");
379     std::size_t max_size_len = strlen("size");
380     std::size_t max_addr_len = strlen("addr");
381     for (const SectionRef &Section : Obj->sections()) {
382       if (!considerForSize(Obj, Section))
383         continue;
384       uint64_t size = Section.getSize();
385       total += size;
386 
387       Expected<StringRef> name_or_err = Section.getName();
388       if (!name_or_err) {
389         error(name_or_err.takeError(), Obj->getFileName());
390         return;
391       }
392 
393       uint64_t addr = Section.getAddress();
394       max_name_len = std::max(max_name_len, name_or_err->size());
395       max_size_len = std::max(max_size_len, getNumLengthAsString(size));
396       max_addr_len = std::max(max_addr_len, getNumLengthAsString(addr));
397     }
398 
399     // Add extra padding.
400     max_name_len += 2;
401     max_size_len += 2;
402     max_addr_len += 2;
403 
404     // Setup header format.
405     fmt << "%-" << max_name_len << "s "
406         << "%" << max_size_len << "s "
407         << "%" << max_addr_len << "s\n";
408 
409     // Print header
410     outs() << format(fmt.str().c_str(), static_cast<const char *>("section"),
411                      static_cast<const char *>("size"),
412                      static_cast<const char *>("addr"));
413     fmtbuf.clear();
414 
415     // Setup per section format.
416     fmt << "%-" << max_name_len << "s "
417         << "%#" << max_size_len << radix_fmt << " "
418         << "%#" << max_addr_len << radix_fmt << "\n";
419 
420     // Print each section.
421     for (const SectionRef &Section : Obj->sections()) {
422       if (!considerForSize(Obj, Section))
423         continue;
424 
425       Expected<StringRef> name_or_err = Section.getName();
426       if (!name_or_err) {
427         error(name_or_err.takeError(), Obj->getFileName());
428         return;
429       }
430 
431       uint64_t size = Section.getSize();
432       uint64_t addr = Section.getAddress();
433       outs() << format(fmt.str().c_str(), name_or_err->str().c_str(), size, addr);
434     }
435 
436     if (ELFCommons) {
437       if (Expected<uint64_t> CommonSizeOrErr = getCommonSize(Obj)) {
438         total += *CommonSizeOrErr;
439         outs() << format(fmt.str().c_str(), std::string("*COM*").c_str(),
440                          *CommonSizeOrErr, static_cast<uint64_t>(0));
441       } else {
442         error(CommonSizeOrErr.takeError(), Obj->getFileName());
443         return;
444       }
445     }
446 
447     // Print total.
448     fmtbuf.clear();
449     fmt << "%-" << max_name_len << "s "
450         << "%#" << max_size_len << radix_fmt << "\n";
451     outs() << format(fmt.str().c_str(), static_cast<const char *>("Total"),
452                      total)
453            << "\n\n";
454   } else {
455     // The Berkeley format does not display individual section sizes. It
456     // displays the cumulative size for each section type.
457     uint64_t total_text = 0;
458     uint64_t total_data = 0;
459     uint64_t total_bss = 0;
460 
461     // Make one pass over the section table to calculate sizes.
462     for (const SectionRef &Section : Obj->sections()) {
463       uint64_t size = Section.getSize();
464       bool isText = Section.isBerkeleyText();
465       bool isData = Section.isBerkeleyData();
466       bool isBSS = Section.isBSS();
467       if (isText)
468         total_text += size;
469       else if (isData)
470         total_data += size;
471       else if (isBSS)
472         total_bss += size;
473     }
474 
475     if (ELFCommons) {
476       if (Expected<uint64_t> CommonSizeOrErr = getCommonSize(Obj))
477         total_bss += *CommonSizeOrErr;
478       else {
479         error(CommonSizeOrErr.takeError(), Obj->getFileName());
480         return;
481       }
482     }
483 
484     total = total_text + total_data + total_bss;
485 
486     if (TotalSizes) {
487       TotalObjectText += total_text;
488       TotalObjectData += total_data;
489       TotalObjectBss += total_bss;
490       TotalObjectTotal += total;
491     }
492 
493     if (!BerkeleyHeaderPrinted) {
494       outs() << "   text\t"
495                 "   data\t"
496                 "    bss\t"
497                 "    "
498              << (Radix == octal ? "oct" : "dec")
499              << "\t"
500                 "    hex\t"
501                 "filename\n";
502       BerkeleyHeaderPrinted = true;
503     }
504 
505     // Print result.
506     fmt << "%#7" << radix_fmt << "\t"
507         << "%#7" << radix_fmt << "\t"
508         << "%#7" << radix_fmt << "\t";
509     outs() << format(fmt.str().c_str(), total_text, total_data, total_bss);
510     fmtbuf.clear();
511     fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t"
512         << "%7" PRIx64 "\t";
513     outs() << format(fmt.str().c_str(), total, total);
514   }
515 }
516 
517 /// Checks to see if the @p O ObjectFile is a Mach-O file and if it is and there
518 /// is a list of architecture flags specified then check to make sure this
519 /// Mach-O file is one of those architectures or all architectures was
520 /// specificed.  If not then an error is generated and this routine returns
521 /// false.  Else it returns true.
522 static bool checkMachOAndArchFlags(ObjectFile *O, StringRef Filename) {
523   auto *MachO = dyn_cast<MachOObjectFile>(O);
524 
525   if (!MachO || ArchAll || ArchFlags.empty())
526     return true;
527 
528   MachO::mach_header H;
529   MachO::mach_header_64 H_64;
530   Triple T;
531   if (MachO->is64Bit()) {
532     H_64 = MachO->MachOObjectFile::getHeader64();
533     T = MachOObjectFile::getArchTriple(H_64.cputype, H_64.cpusubtype);
534   } else {
535     H = MachO->MachOObjectFile::getHeader();
536     T = MachOObjectFile::getArchTriple(H.cputype, H.cpusubtype);
537   }
538   if (!is_contained(ArchFlags, T.getArchName())) {
539     error("no architecture specified", Filename);
540     return false;
541   }
542   return true;
543 }
544 
545 /// Print the section sizes for @p file. If @p file is an archive, print the
546 /// section sizes for each archive member.
547 static void printFileSectionSizes(StringRef file) {
548 
549   // Attempt to open the binary.
550   Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(file);
551   if (!BinaryOrErr) {
552     error(BinaryOrErr.takeError(), file);
553     return;
554   }
555   Binary &Bin = *BinaryOrErr.get().getBinary();
556 
557   if (Archive *a = dyn_cast<Archive>(&Bin)) {
558     // This is an archive. Iterate over each member and display its sizes.
559     Error Err = Error::success();
560     for (auto &C : a->children(Err)) {
561       Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
562       if (!ChildOrErr) {
563         if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError()))
564           error(std::move(E), a->getFileName(), C);
565         continue;
566       }
567       if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
568         MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
569         if (!checkMachOAndArchFlags(o, file))
570           return;
571         if (OutputFormat == sysv)
572           outs() << o->getFileName() << "   (ex " << a->getFileName() << "):\n";
573         else if (MachO && OutputFormat == darwin)
574           outs() << a->getFileName() << "(" << o->getFileName() << "):\n";
575         printObjectSectionSizes(o);
576         if (!MachO && OutputFormat == darwin)
577           outs() << o->getFileName() << " (ex " << a->getFileName() << ")\n";
578         if (OutputFormat == berkeley) {
579           if (MachO)
580             outs() << a->getFileName() << "(" << o->getFileName() << ")\n";
581           else
582             outs() << o->getFileName() << " (ex " << a->getFileName() << ")\n";
583         }
584       }
585     }
586     if (Err)
587       error(std::move(Err), a->getFileName());
588   } else if (MachOUniversalBinary *UB =
589                  dyn_cast<MachOUniversalBinary>(&Bin)) {
590     // If we have a list of architecture flags specified dump only those.
591     if (!ArchAll && !ArchFlags.empty()) {
592       // Look for a slice in the universal binary that matches each ArchFlag.
593       bool ArchFound;
594       for (unsigned i = 0; i < ArchFlags.size(); ++i) {
595         ArchFound = false;
596         for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
597                                                    E = UB->end_objects();
598              I != E; ++I) {
599           if (ArchFlags[i] == I->getArchFlagName()) {
600             ArchFound = true;
601             Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
602             if (UO) {
603               if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
604                 MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
605                 if (OutputFormat == sysv)
606                   outs() << o->getFileName() << "  :\n";
607                 else if (MachO && OutputFormat == darwin) {
608                   if (MoreThanOneFile || ArchFlags.size() > 1)
609                     outs() << o->getFileName() << " (for architecture "
610                            << I->getArchFlagName() << "): \n";
611                 }
612                 printObjectSectionSizes(o);
613                 if (OutputFormat == berkeley) {
614                   if (!MachO || MoreThanOneFile || ArchFlags.size() > 1)
615                     outs() << o->getFileName() << " (for architecture "
616                            << I->getArchFlagName() << ")";
617                   outs() << "\n";
618                 }
619               }
620             } else if (auto E = isNotObjectErrorInvalidFileType(
621                        UO.takeError())) {
622               error(std::move(E), file, ArchFlags.size() > 1 ?
623                     StringRef(I->getArchFlagName()) : StringRef());
624               return;
625             } else if (Expected<std::unique_ptr<Archive>> AOrErr =
626                            I->getAsArchive()) {
627               std::unique_ptr<Archive> &UA = *AOrErr;
628               // This is an archive. Iterate over each member and display its
629               // sizes.
630               Error Err = Error::success();
631               for (auto &C : UA->children(Err)) {
632                 Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
633                 if (!ChildOrErr) {
634                   if (auto E = isNotObjectErrorInvalidFileType(
635                                     ChildOrErr.takeError()))
636                     error(std::move(E), UA->getFileName(), C,
637                           ArchFlags.size() > 1 ?
638                           StringRef(I->getArchFlagName()) : StringRef());
639                   continue;
640                 }
641                 if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
642                   MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
643                   if (OutputFormat == sysv)
644                     outs() << o->getFileName() << "   (ex " << UA->getFileName()
645                            << "):\n";
646                   else if (MachO && OutputFormat == darwin)
647                     outs() << UA->getFileName() << "(" << o->getFileName()
648                            << ")"
649                            << " (for architecture " << I->getArchFlagName()
650                            << "):\n";
651                   printObjectSectionSizes(o);
652                   if (OutputFormat == berkeley) {
653                     if (MachO) {
654                       outs() << UA->getFileName() << "(" << o->getFileName()
655                              << ")";
656                       if (ArchFlags.size() > 1)
657                         outs() << " (for architecture " << I->getArchFlagName()
658                                << ")";
659                       outs() << "\n";
660                     } else
661                       outs() << o->getFileName() << " (ex " << UA->getFileName()
662                              << ")\n";
663                   }
664                 }
665               }
666               if (Err)
667                 error(std::move(Err), UA->getFileName());
668             } else {
669               consumeError(AOrErr.takeError());
670               error("mach-o universal file for architecture " +
671                         StringRef(I->getArchFlagName()) +
672                         " is not a mach-o file or an archive file",
673                     file);
674             }
675           }
676         }
677         if (!ArchFound) {
678           error("file does not contain architecture " + ArchFlags[i], file);
679           return;
680         }
681       }
682       return;
683     }
684     // No architecture flags were specified so if this contains a slice that
685     // matches the host architecture dump only that.
686     if (!ArchAll) {
687       StringRef HostArchName = MachOObjectFile::getHostArch().getArchName();
688       for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
689                                                  E = UB->end_objects();
690            I != E; ++I) {
691         if (HostArchName == I->getArchFlagName()) {
692           Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
693           if (UO) {
694             if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
695               MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
696               if (OutputFormat == sysv)
697                 outs() << o->getFileName() << "  :\n";
698               else if (MachO && OutputFormat == darwin) {
699                 if (MoreThanOneFile)
700                   outs() << o->getFileName() << " (for architecture "
701                          << I->getArchFlagName() << "):\n";
702               }
703               printObjectSectionSizes(o);
704               if (OutputFormat == berkeley) {
705                 if (!MachO || MoreThanOneFile)
706                   outs() << o->getFileName() << " (for architecture "
707                          << I->getArchFlagName() << ")";
708                 outs() << "\n";
709               }
710             }
711           } else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) {
712             error(std::move(E), file);
713             return;
714           } else if (Expected<std::unique_ptr<Archive>> AOrErr =
715                          I->getAsArchive()) {
716             std::unique_ptr<Archive> &UA = *AOrErr;
717             // This is an archive. Iterate over each member and display its
718             // sizes.
719             Error Err = Error::success();
720             for (auto &C : UA->children(Err)) {
721               Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
722               if (!ChildOrErr) {
723                 if (auto E = isNotObjectErrorInvalidFileType(
724                                 ChildOrErr.takeError()))
725                   error(std::move(E), UA->getFileName(), C);
726                 continue;
727               }
728               if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
729                 MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
730                 if (OutputFormat == sysv)
731                   outs() << o->getFileName() << "   (ex " << UA->getFileName()
732                          << "):\n";
733                 else if (MachO && OutputFormat == darwin)
734                   outs() << UA->getFileName() << "(" << o->getFileName() << ")"
735                          << " (for architecture " << I->getArchFlagName()
736                          << "):\n";
737                 printObjectSectionSizes(o);
738                 if (OutputFormat == berkeley) {
739                   if (MachO)
740                     outs() << UA->getFileName() << "(" << o->getFileName()
741                            << ")\n";
742                   else
743                     outs() << o->getFileName() << " (ex " << UA->getFileName()
744                            << ")\n";
745                 }
746               }
747             }
748             if (Err)
749               error(std::move(Err), UA->getFileName());
750           } else {
751             consumeError(AOrErr.takeError());
752             error("mach-o universal file for architecture " +
753                       StringRef(I->getArchFlagName()) +
754                       " is not a mach-o file or an archive file",
755                   file);
756           }
757           return;
758         }
759       }
760     }
761     // Either all architectures have been specified or none have been specified
762     // and this does not contain the host architecture so dump all the slices.
763     bool MoreThanOneArch = UB->getNumberOfObjects() > 1;
764     for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
765                                                E = UB->end_objects();
766          I != E; ++I) {
767       Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
768       if (UO) {
769         if (ObjectFile *o = dyn_cast<ObjectFile>(&*UO.get())) {
770           MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
771           if (OutputFormat == sysv)
772             outs() << o->getFileName() << "  :\n";
773           else if (MachO && OutputFormat == darwin) {
774             if (MoreThanOneFile || MoreThanOneArch)
775               outs() << o->getFileName() << " (for architecture "
776                      << I->getArchFlagName() << "):";
777             outs() << "\n";
778           }
779           printObjectSectionSizes(o);
780           if (OutputFormat == berkeley) {
781             if (!MachO || MoreThanOneFile || MoreThanOneArch)
782               outs() << o->getFileName() << " (for architecture "
783                      << I->getArchFlagName() << ")";
784             outs() << "\n";
785           }
786         }
787       } else if (auto E = isNotObjectErrorInvalidFileType(UO.takeError())) {
788         error(std::move(E), file, MoreThanOneArch ?
789               StringRef(I->getArchFlagName()) : StringRef());
790         return;
791       } else if (Expected<std::unique_ptr<Archive>> AOrErr =
792                          I->getAsArchive()) {
793         std::unique_ptr<Archive> &UA = *AOrErr;
794         // This is an archive. Iterate over each member and display its sizes.
795         Error Err = Error::success();
796         for (auto &C : UA->children(Err)) {
797           Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
798           if (!ChildOrErr) {
799             if (auto E = isNotObjectErrorInvalidFileType(
800                               ChildOrErr.takeError()))
801               error(std::move(E), UA->getFileName(), C, MoreThanOneArch ?
802                     StringRef(I->getArchFlagName()) : StringRef());
803             continue;
804           }
805           if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) {
806             MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
807             if (OutputFormat == sysv)
808               outs() << o->getFileName() << "   (ex " << UA->getFileName()
809                      << "):\n";
810             else if (MachO && OutputFormat == darwin)
811               outs() << UA->getFileName() << "(" << o->getFileName() << ")"
812                      << " (for architecture " << I->getArchFlagName() << "):\n";
813             printObjectSectionSizes(o);
814             if (OutputFormat == berkeley) {
815               if (MachO)
816                 outs() << UA->getFileName() << "(" << o->getFileName() << ")"
817                        << " (for architecture " << I->getArchFlagName()
818                        << ")\n";
819               else
820                 outs() << o->getFileName() << " (ex " << UA->getFileName()
821                        << ")\n";
822             }
823           }
824         }
825         if (Err)
826           error(std::move(Err), UA->getFileName());
827       } else {
828         consumeError(AOrErr.takeError());
829         error("mach-o universal file for architecture " +
830                   StringRef(I->getArchFlagName()) +
831                   " is not a mach-o file or an archive file",
832               file);
833       }
834     }
835   } else if (ObjectFile *o = dyn_cast<ObjectFile>(&Bin)) {
836     if (!checkMachOAndArchFlags(o, file))
837       return;
838     MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o);
839     if (OutputFormat == sysv)
840       outs() << o->getFileName() << "  :\n";
841     else if (MachO && OutputFormat == darwin && MoreThanOneFile)
842       outs() << o->getFileName() << ":\n";
843     printObjectSectionSizes(o);
844     if (!MachO && OutputFormat == darwin)
845       outs() << o->getFileName() << "\n";
846     if (OutputFormat == berkeley) {
847       if (!MachO || MoreThanOneFile)
848         outs() << o->getFileName();
849       outs() << "\n";
850     }
851   } else {
852     error("unsupported file type", file);
853   }
854 }
855 
856 static void printBerkeleyTotals() {
857   std::string fmtbuf;
858   raw_string_ostream fmt(fmtbuf);
859   const char *radix_fmt = getRadixFmt();
860   fmt << "%#7" << radix_fmt << "\t"
861       << "%#7" << radix_fmt << "\t"
862       << "%#7" << radix_fmt << "\t";
863   outs() << format(fmt.str().c_str(), TotalObjectText, TotalObjectData,
864                    TotalObjectBss);
865   fmtbuf.clear();
866   fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t"
867       << "%7" PRIx64 "\t";
868   outs() << format(fmt.str().c_str(), TotalObjectTotal, TotalObjectTotal)
869          << "(TOTALS)\n";
870 }
871 
872 int llvm_size_main(int argc, char **argv) {
873   InitLLVM X(argc, argv);
874   BumpPtrAllocator A;
875   StringSaver Saver(A);
876   SizeOptTable Tbl;
877   ToolName = argv[0];
878   opt::InputArgList Args =
879       Tbl.parseArgs(argc, argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
880         error(Msg);
881         exit(1);
882       });
883   if (Args.hasArg(OPT_help)) {
884     Tbl.printHelp(
885         outs(),
886         (Twine(ToolName) + " [options] <input object files>").str().c_str(),
887         "LLVM object size dumper");
888     // TODO Replace this with OptTable API once it adds extrahelp support.
889     outs() << "\nPass @FILE as argument to read options from FILE.\n";
890     return 0;
891   }
892   if (Args.hasArg(OPT_version)) {
893     outs() << ToolName << '\n';
894     cl::PrintVersionMessage();
895     return 0;
896   }
897 
898   ELFCommons = Args.hasArg(OPT_common);
899   DarwinLongFormat = Args.hasArg(OPT_l);
900   TotalSizes = Args.hasArg(OPT_totals);
901   StringRef V = Args.getLastArgValue(OPT_format_EQ, "berkeley");
902   if (V == "berkeley")
903     OutputFormat = berkeley;
904   else if (V == "darwin")
905     OutputFormat = darwin;
906   else if (V == "sysv")
907     OutputFormat = sysv;
908   else
909     error("--format value should be one of: 'berkeley', 'darwin', 'sysv'");
910   V = Args.getLastArgValue(OPT_radix_EQ, "10");
911   if (V == "8")
912     Radix = RadixTy::octal;
913   else if (V == "10")
914     Radix = RadixTy::decimal;
915   else if (V == "16")
916     Radix = RadixTy::hexadecimal;
917   else
918     error("--radix value should be one of: 8, 10, 16 ");
919 
920   for (const auto *A : Args.filtered(OPT_arch_EQ)) {
921     SmallVector<StringRef, 2> Values;
922     llvm::SplitString(A->getValue(), Values, ",");
923     for (StringRef V : Values) {
924       if (V == "all")
925         ArchAll = true;
926       else if (MachOObjectFile::isValidArch(V))
927         ArchFlags.push_back(V);
928       else {
929         outs() << ToolName << ": for the -arch option: Unknown architecture "
930                << "named '" << V << "'";
931         return 1;
932       }
933     }
934   }
935 
936   InputFilenames = Args.getAllArgValues(OPT_INPUT);
937   if (InputFilenames.empty())
938     InputFilenames.push_back("a.out");
939 
940   MoreThanOneFile = InputFilenames.size() > 1;
941   llvm::for_each(InputFilenames, printFileSectionSizes);
942   if (OutputFormat == berkeley && TotalSizes)
943     printBerkeleyTotals();
944 
945   if (HadError)
946     return 1;
947   return 0;
948 }
949