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