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