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