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 if (ObjectFile *o = dyn_cast<ObjectFile>(Val: &*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 }
628 } else if (auto E = isNotObjectErrorInvalidFileType(
629 Err: UO.takeError())) {
630 error(E: std::move(E), FileName: file, ArchitectureName: ArchFlags.size() > 1 ?
631 StringRef(I->getArchFlagName()) : StringRef());
632 return;
633 } else if (Expected<std::unique_ptr<Archive>> AOrErr =
634 I->getAsArchive()) {
635 std::unique_ptr<Archive> &UA = *AOrErr;
636 // This is an archive. Iterate over each member and display its
637 // sizes.
638 Error Err = Error::success();
639 for (auto &C : UA->children(Err)) {
640 Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
641 if (!ChildOrErr) {
642 if (auto E = isNotObjectErrorInvalidFileType(
643 Err: ChildOrErr.takeError()))
644 error(E: std::move(E), FileName: UA->getFileName(), C,
645 ArchitectureName: ArchFlags.size() > 1 ?
646 StringRef(I->getArchFlagName()) : StringRef());
647 continue;
648 }
649 if (ObjectFile *o = dyn_cast<ObjectFile>(Val: &*ChildOrErr.get())) {
650 MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Val: o);
651 if (OutputFormat == sysv)
652 outs() << o->getFileName() << " (ex " << UA->getFileName()
653 << "):\n";
654 else if (MachO && OutputFormat == darwin)
655 outs() << UA->getFileName() << "(" << o->getFileName()
656 << ")"
657 << " (for architecture " << I->getArchFlagName()
658 << "):\n";
659 printObjectSectionSizes(Obj: o);
660 if (OutputFormat == berkeley) {
661 if (MachO) {
662 outs() << UA->getFileName() << "(" << o->getFileName()
663 << ")";
664 if (ArchFlags.size() > 1)
665 outs() << " (for architecture " << I->getArchFlagName()
666 << ")";
667 outs() << "\n";
668 } else
669 outs() << o->getFileName() << " (ex " << UA->getFileName()
670 << ")\n";
671 }
672 }
673 }
674 if (Err)
675 error(E: std::move(Err), FileName: UA->getFileName());
676 } else {
677 consumeError(Err: AOrErr.takeError());
678 error(Message: "mach-o universal file for architecture " +
679 StringRef(I->getArchFlagName()) +
680 " is not a mach-o file or an archive file",
681 File: file);
682 }
683 }
684 }
685 if (!ArchFound) {
686 error(Message: "file does not contain architecture " + ArchFlags[i], File: file);
687 return;
688 }
689 }
690 return;
691 }
692 // No architecture flags were specified so if this contains a slice that
693 // matches the host architecture dump only that.
694 if (!ArchAll) {
695 StringRef HostArchName = MachOObjectFile::getHostArch().getArchName();
696 for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
697 E = UB->end_objects();
698 I != E; ++I) {
699 if (HostArchName == I->getArchFlagName()) {
700 Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
701 if (UO) {
702 if (ObjectFile *o = dyn_cast<ObjectFile>(Val: &*UO.get())) {
703 MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Val: o);
704 if (OutputFormat == sysv)
705 outs() << o->getFileName() << " :\n";
706 else if (MachO && OutputFormat == darwin) {
707 if (MoreThanOneFile)
708 outs() << o->getFileName() << " (for architecture "
709 << I->getArchFlagName() << "):\n";
710 }
711 printObjectSectionSizes(Obj: o);
712 if (OutputFormat == berkeley) {
713 if (!MachO || MoreThanOneFile)
714 outs() << o->getFileName() << " (for architecture "
715 << I->getArchFlagName() << ")";
716 outs() << "\n";
717 }
718 }
719 } else if (auto E = isNotObjectErrorInvalidFileType(Err: UO.takeError())) {
720 error(E: std::move(E), FileName: file);
721 return;
722 } else if (Expected<std::unique_ptr<Archive>> AOrErr =
723 I->getAsArchive()) {
724 std::unique_ptr<Archive> &UA = *AOrErr;
725 // This is an archive. Iterate over each member and display its
726 // sizes.
727 Error Err = Error::success();
728 for (auto &C : UA->children(Err)) {
729 Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
730 if (!ChildOrErr) {
731 if (auto E = isNotObjectErrorInvalidFileType(
732 Err: ChildOrErr.takeError()))
733 error(E: std::move(E), FileName: UA->getFileName(), C);
734 continue;
735 }
736 if (ObjectFile *o = dyn_cast<ObjectFile>(Val: &*ChildOrErr.get())) {
737 MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Val: o);
738 if (OutputFormat == sysv)
739 outs() << o->getFileName() << " (ex " << UA->getFileName()
740 << "):\n";
741 else if (MachO && OutputFormat == darwin)
742 outs() << UA->getFileName() << "(" << o->getFileName() << ")"
743 << " (for architecture " << I->getArchFlagName()
744 << "):\n";
745 printObjectSectionSizes(Obj: o);
746 if (OutputFormat == berkeley) {
747 if (MachO)
748 outs() << UA->getFileName() << "(" << o->getFileName()
749 << ")\n";
750 else
751 outs() << o->getFileName() << " (ex " << UA->getFileName()
752 << ")\n";
753 }
754 }
755 }
756 if (Err)
757 error(E: std::move(Err), FileName: UA->getFileName());
758 } else {
759 consumeError(Err: AOrErr.takeError());
760 error(Message: "mach-o universal file for architecture " +
761 StringRef(I->getArchFlagName()) +
762 " is not a mach-o file or an archive file",
763 File: file);
764 }
765 return;
766 }
767 }
768 }
769 // Either all architectures have been specified or none have been specified
770 // and this does not contain the host architecture so dump all the slices.
771 bool MoreThanOneArch = UB->getNumberOfObjects() > 1;
772 for (MachOUniversalBinary::object_iterator I = UB->begin_objects(),
773 E = UB->end_objects();
774 I != E; ++I) {
775 Expected<std::unique_ptr<ObjectFile>> UO = I->getAsObjectFile();
776 if (UO) {
777 if (ObjectFile *o = dyn_cast<ObjectFile>(Val: &*UO.get())) {
778 MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Val: o);
779 if (OutputFormat == sysv)
780 outs() << o->getFileName() << " :\n";
781 else if (MachO && OutputFormat == darwin) {
782 if (MoreThanOneFile || MoreThanOneArch)
783 outs() << o->getFileName() << " (for architecture "
784 << I->getArchFlagName() << "):";
785 outs() << "\n";
786 }
787 printObjectSectionSizes(Obj: o);
788 if (OutputFormat == berkeley) {
789 if (!MachO || MoreThanOneFile || MoreThanOneArch)
790 outs() << o->getFileName() << " (for architecture "
791 << I->getArchFlagName() << ")";
792 outs() << "\n";
793 }
794 }
795 } else if (auto E = isNotObjectErrorInvalidFileType(Err: UO.takeError())) {
796 error(E: std::move(E), FileName: file, ArchitectureName: MoreThanOneArch ?
797 StringRef(I->getArchFlagName()) : StringRef());
798 return;
799 } else if (Expected<std::unique_ptr<Archive>> AOrErr =
800 I->getAsArchive()) {
801 std::unique_ptr<Archive> &UA = *AOrErr;
802 // This is an archive. Iterate over each member and display its sizes.
803 Error Err = Error::success();
804 for (auto &C : UA->children(Err)) {
805 Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary();
806 if (!ChildOrErr) {
807 if (auto E = isNotObjectErrorInvalidFileType(
808 Err: ChildOrErr.takeError()))
809 error(E: std::move(E), FileName: UA->getFileName(), C, ArchitectureName: MoreThanOneArch ?
810 StringRef(I->getArchFlagName()) : StringRef());
811 continue;
812 }
813 if (ObjectFile *o = dyn_cast<ObjectFile>(Val: &*ChildOrErr.get())) {
814 MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Val: o);
815 if (OutputFormat == sysv)
816 outs() << o->getFileName() << " (ex " << UA->getFileName()
817 << "):\n";
818 else if (MachO && OutputFormat == darwin)
819 outs() << UA->getFileName() << "(" << o->getFileName() << ")"
820 << " (for architecture " << I->getArchFlagName() << "):\n";
821 printObjectSectionSizes(Obj: o);
822 if (OutputFormat == berkeley) {
823 if (MachO)
824 outs() << UA->getFileName() << "(" << o->getFileName() << ")"
825 << " (for architecture " << I->getArchFlagName()
826 << ")\n";
827 else
828 outs() << o->getFileName() << " (ex " << UA->getFileName()
829 << ")\n";
830 }
831 }
832 }
833 if (Err)
834 error(E: std::move(Err), FileName: UA->getFileName());
835 } else {
836 consumeError(Err: AOrErr.takeError());
837 error(Message: "mach-o universal file for architecture " +
838 StringRef(I->getArchFlagName()) +
839 " is not a mach-o file or an archive file",
840 File: file);
841 }
842 }
843 } else if (ObjectFile *o = dyn_cast<ObjectFile>(Val: &Bin)) {
844 if (!checkMachOAndArchFlags(O: o, Filename: file))
845 return;
846 MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Val: o);
847 if (OutputFormat == sysv)
848 outs() << o->getFileName() << " :\n";
849 else if (MachO && OutputFormat == darwin && MoreThanOneFile)
850 outs() << o->getFileName() << ":\n";
851 printObjectSectionSizes(Obj: o);
852 if (!MachO && OutputFormat == darwin)
853 outs() << o->getFileName() << "\n";
854 if (OutputFormat == berkeley) {
855 if (!MachO || MoreThanOneFile)
856 outs() << o->getFileName();
857 outs() << "\n";
858 }
859 } else {
860 error(Message: "unsupported file type", File: file);
861 }
862}
863
864static void printBerkeleyTotals() {
865 std::string fmtbuf;
866 raw_string_ostream fmt(fmtbuf);
867 const char *radix_fmt = getRadixFmt();
868
869 if (HasMachOFiles) {
870 // Darwin format totals: __TEXT __DATA __OBJC others dec hex
871 outs() << TotalObjectText << "\t" << TotalObjectData << "\t"
872 << TotalObjectObjc << "\t" << TotalObjectOthers << "\t"
873 << TotalObjectTotal << "\t" << format(Fmt: "%" PRIx64, Vals: TotalObjectTotal)
874 << "\t(TOTALS)\n";
875 } else {
876 fmt << "%#7" << radix_fmt << "\t"
877 << "%#7" << radix_fmt << "\t"
878 << "%#7" << radix_fmt << "\t";
879 outs() << format(Fmt: fmtbuf.c_str(), Vals: TotalObjectText, Vals: TotalObjectData,
880 Vals: TotalObjectBss);
881 fmtbuf.clear();
882 fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t"
883 << "%7" PRIx64 "\t";
884 outs() << format(Fmt: fmtbuf.c_str(), Vals: TotalObjectTotal, Vals: TotalObjectTotal)
885 << "(TOTALS)\n";
886 }
887}
888
889int llvm_size_main(int argc, char **argv, const llvm::ToolContext &) {
890 BumpPtrAllocator A;
891 StringSaver Saver(A);
892 SizeOptTable Tbl;
893 ToolName = argv[0];
894 opt::InputArgList Args =
895 Tbl.parseArgs(Argc: argc, Argv: argv, Unknown: OPT_UNKNOWN, Saver, ErrorFn: [&](StringRef Msg) {
896 error(Message: Msg);
897 exit(status: 1);
898 });
899 if (Args.hasArg(Ids: OPT_help)) {
900 Tbl.printHelp(
901 OS&: outs(),
902 Usage: (Twine(ToolName) + " [options] <input object files>").str().c_str(),
903 Title: "LLVM object size dumper");
904 // TODO Replace this with OptTable API once it adds extrahelp support.
905 outs() << "\nPass @FILE as argument to read options from FILE.\n";
906 return 0;
907 }
908 if (Args.hasArg(Ids: OPT_version)) {
909 outs() << ToolName << '\n';
910 cl::PrintVersionMessage();
911 return 0;
912 }
913
914 ELFCommons = Args.hasArg(Ids: OPT_common);
915 DarwinLongFormat = Args.hasArg(Ids: OPT_l);
916 ExcludePageZero = Args.hasArg(Ids: OPT_exclude_pagezero);
917 TotalSizes = Args.hasArg(Ids: OPT_totals);
918 StringRef V = Args.getLastArgValue(Id: OPT_format_EQ, Default: "berkeley");
919 if (V == "berkeley")
920 OutputFormat = berkeley;
921 else if (V == "darwin")
922 OutputFormat = darwin;
923 else if (V == "sysv")
924 OutputFormat = sysv;
925 else
926 error(Message: "--format value should be one of: 'berkeley', 'darwin', 'sysv'");
927 V = Args.getLastArgValue(Id: OPT_radix_EQ, Default: "10");
928 if (V == "8")
929 Radix = RadixTy::octal;
930 else if (V == "10")
931 Radix = RadixTy::decimal;
932 else if (V == "16")
933 Radix = RadixTy::hexadecimal;
934 else
935 error(Message: "--radix value should be one of: 8, 10, 16 ");
936
937 for (const auto *A : Args.filtered(Ids: OPT_arch_EQ)) {
938 SmallVector<StringRef, 2> Values;
939 llvm::SplitString(Source: A->getValue(), OutFragments&: Values, Delimiters: ",");
940 for (StringRef V : Values) {
941 if (V == "all")
942 ArchAll = true;
943 else if (MachOObjectFile::isValidArch(ArchFlag: V))
944 ArchFlags.push_back(x: V);
945 else {
946 outs() << ToolName << ": for the -arch option: Unknown architecture "
947 << "named '" << V << "'";
948 return 1;
949 }
950 }
951 }
952
953 InputFilenames = Args.getAllArgValues(Id: OPT_INPUT);
954 if (InputFilenames.empty())
955 InputFilenames.push_back(x: "a.out");
956
957 MoreThanOneFile = InputFilenames.size() > 1;
958 llvm::for_each(Range&: InputFilenames, F: printFileSectionSizes);
959 if (OutputFormat == berkeley && TotalSizes)
960 printBerkeleyTotals();
961
962 if (HadError)
963 return 1;
964 return 0;
965}
966