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