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