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