1 //===-- llvm-size.cpp - Print the size of each object section -------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This program is a utility that works like traditional Unix "size", 11 // that is, it prints out the size of each section, and the total size of all 12 // sections. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "llvm/ADT/APInt.h" 17 #include "llvm/Object/Archive.h" 18 #include "llvm/Object/ObjectFile.h" 19 #include "llvm/Object/MachO.h" 20 #include "llvm/Support/Casting.h" 21 #include "llvm/Support/CommandLine.h" 22 #include "llvm/Support/FileSystem.h" 23 #include "llvm/Support/Format.h" 24 #include "llvm/Support/ManagedStatic.h" 25 #include "llvm/Support/MemoryBuffer.h" 26 #include "llvm/Support/PrettyStackTrace.h" 27 #include "llvm/Support/Signals.h" 28 #include "llvm/Support/raw_ostream.h" 29 #include <algorithm> 30 #include <string> 31 #include <system_error> 32 using namespace llvm; 33 using namespace object; 34 35 enum OutputFormatTy {berkeley, sysv, darwin}; 36 static cl::opt<OutputFormatTy> 37 OutputFormat("format", 38 cl::desc("Specify output format"), 39 cl::values(clEnumVal(sysv, "System V format"), 40 clEnumVal(berkeley, "Berkeley format"), 41 clEnumVal(darwin, "Darwin -m format"), clEnumValEnd), 42 cl::init(berkeley)); 43 44 static cl::opt<OutputFormatTy> 45 OutputFormatShort(cl::desc("Specify output format"), 46 cl::values(clEnumValN(sysv, "A", "System V format"), 47 clEnumValN(berkeley, "B", "Berkeley format"), 48 clEnumValEnd), 49 cl::init(berkeley)); 50 51 static bool berkeleyHeaderPrinted = false; 52 static bool moreThanOneFile = false; 53 54 cl::opt<bool> DarwinLongFormat("l", 55 cl::desc("When format is darwin, use long format " 56 "to include addresses and offsets.")); 57 58 enum RadixTy {octal = 8, decimal = 10, hexadecimal = 16}; 59 static cl::opt<unsigned int> 60 Radix("-radix", 61 cl::desc("Print size in radix. Only 8, 10, and 16 are valid"), 62 cl::init(decimal)); 63 64 static cl::opt<RadixTy> 65 RadixShort(cl::desc("Print size in radix:"), 66 cl::values(clEnumValN(octal, "o", "Print size in octal"), 67 clEnumValN(decimal, "d", "Print size in decimal"), 68 clEnumValN(hexadecimal, "x", "Print size in hexadecimal"), 69 clEnumValEnd), 70 cl::init(decimal)); 71 72 static cl::list<std::string> 73 InputFilenames(cl::Positional, cl::desc("<input files>"), 74 cl::ZeroOrMore); 75 76 static std::string ToolName; 77 78 /// @brief If ec is not success, print the error and return true. 79 static bool error(std::error_code ec) { 80 if (!ec) return false; 81 82 outs() << ToolName << ": error reading file: " << ec.message() << ".\n"; 83 outs().flush(); 84 return true; 85 } 86 87 /// @brief Get the length of the string that represents @p num in Radix 88 /// including the leading 0x or 0 for hexadecimal and octal respectively. 89 static size_t getNumLengthAsString(uint64_t num) { 90 APInt conv(64, num); 91 SmallString<32> result; 92 conv.toString(result, Radix, false, true); 93 return result.size(); 94 } 95 96 /// @brief Return the the printing format for the Radix. 97 static const char * getRadixFmt(void) { 98 switch (Radix) { 99 case octal: 100 return PRIo64; 101 case decimal: 102 return PRIu64; 103 case hexadecimal: 104 return PRIx64; 105 } 106 return nullptr; 107 } 108 109 /// @brief Print the size of each Mach-O segment and section in @p MachO. 110 /// 111 /// This is when used when @c OutputFormat is darwin and produces the same 112 /// output as darwin's size(1) -m output. 113 static void PrintDarwinSectionSizes(MachOObjectFile *MachO) { 114 std::string fmtbuf; 115 raw_string_ostream fmt(fmtbuf); 116 const char *radix_fmt = getRadixFmt(); 117 if (Radix == hexadecimal) 118 fmt << "0x"; 119 fmt << "%" << radix_fmt; 120 121 uint32_t LoadCommandCount = MachO->getHeader().ncmds; 122 uint32_t Filetype = MachO->getHeader().filetype; 123 MachOObjectFile::LoadCommandInfo Load = MachO->getFirstLoadCommandInfo(); 124 125 uint64_t total = 0; 126 for (unsigned I = 0; ; ++I) { 127 if (Load.C.cmd == MachO::LC_SEGMENT_64) { 128 MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load); 129 outs() << "Segment " << Seg.segname << ": " 130 << format(fmt.str().c_str(), Seg.vmsize); 131 if (DarwinLongFormat) 132 outs() << " (vmaddr 0x" << format("%" PRIx64, Seg.vmaddr) 133 << " fileoff " << Seg.fileoff << ")"; 134 outs() << "\n"; 135 total += Seg.vmsize; 136 uint64_t sec_total = 0; 137 for (unsigned J = 0; J < Seg.nsects; ++J) { 138 MachO::section_64 Sec = MachO->getSection64(Load, J); 139 if (Filetype == MachO::MH_OBJECT) 140 outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", " 141 << format("%.16s", &Sec.sectname) << "): "; 142 else 143 outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": "; 144 outs() << format(fmt.str().c_str(), Sec.size); 145 if (DarwinLongFormat) 146 outs() << " (addr 0x" << format("%" PRIx64, Sec.addr) 147 << " offset " << Sec.offset << ")"; 148 outs() << "\n"; 149 sec_total += Sec.size; 150 } 151 if (Seg.nsects != 0) 152 outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n"; 153 } 154 else if (Load.C.cmd == MachO::LC_SEGMENT) { 155 MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load); 156 outs() << "Segment " << Seg.segname << ": " 157 << format(fmt.str().c_str(), Seg.vmsize); 158 if (DarwinLongFormat) 159 outs() << " (vmaddr 0x" << format("%" PRIx64, Seg.vmaddr) 160 << " fileoff " << Seg.fileoff << ")"; 161 outs() << "\n"; 162 total += Seg.vmsize; 163 uint64_t sec_total = 0; 164 for (unsigned J = 0; J < Seg.nsects; ++J) { 165 MachO::section Sec = MachO->getSection(Load, J); 166 if (Filetype == MachO::MH_OBJECT) 167 outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", " 168 << format("%.16s", &Sec.sectname) << "): "; 169 else 170 outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": "; 171 outs() << format(fmt.str().c_str(), Sec.size); 172 if (DarwinLongFormat) 173 outs() << " (addr 0x" << format("%" PRIx64, Sec.addr) 174 << " offset " << Sec.offset << ")"; 175 outs() << "\n"; 176 sec_total += Sec.size; 177 } 178 if (Seg.nsects != 0) 179 outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n"; 180 } 181 if (I == LoadCommandCount - 1) 182 break; 183 else 184 Load = MachO->getNextLoadCommandInfo(Load); 185 } 186 outs() << "total " << format(fmt.str().c_str(), total) << "\n"; 187 } 188 189 /// @brief Print the summary sizes of the standard Mach-O segments in @p MachO. 190 /// 191 /// This is when used when @c OutputFormat is berkeley with a Mach-O file and 192 /// produces the same output as darwin's size(1) default output. 193 static void PrintDarwinSegmentSizes(MachOObjectFile *MachO) { 194 uint32_t LoadCommandCount = MachO->getHeader().ncmds; 195 MachOObjectFile::LoadCommandInfo Load = MachO->getFirstLoadCommandInfo(); 196 197 uint64_t total_text = 0; 198 uint64_t total_data = 0; 199 uint64_t total_objc = 0; 200 uint64_t total_others = 0; 201 for (unsigned I = 0; ; ++I) { 202 if (Load.C.cmd == MachO::LC_SEGMENT_64) { 203 MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load); 204 if (MachO->getHeader().filetype == MachO::MH_OBJECT) { 205 for (unsigned J = 0; J < Seg.nsects; ++J) { 206 MachO::section_64 Sec = MachO->getSection64(Load, J); 207 StringRef SegmentName = StringRef(Sec.segname); 208 if (SegmentName == "__TEXT") 209 total_text += Sec.size; 210 else if (SegmentName == "__DATA") 211 total_data += Sec.size; 212 else if (SegmentName == "__OBJC") 213 total_objc += Sec.size; 214 else 215 total_others += Sec.size; 216 } 217 } else { 218 StringRef SegmentName = StringRef(Seg.segname); 219 if (SegmentName == "__TEXT") 220 total_text += Seg.vmsize; 221 else if (SegmentName == "__DATA") 222 total_data += Seg.vmsize; 223 else if (SegmentName == "__OBJC") 224 total_objc += Seg.vmsize; 225 else 226 total_others += Seg.vmsize; 227 } 228 } 229 else if (Load.C.cmd == MachO::LC_SEGMENT) { 230 MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load); 231 if (MachO->getHeader().filetype == MachO::MH_OBJECT) { 232 for (unsigned J = 0; J < Seg.nsects; ++J) { 233 MachO::section Sec = MachO->getSection(Load, J); 234 StringRef SegmentName = StringRef(Sec.segname); 235 if (SegmentName == "__TEXT") 236 total_text += Sec.size; 237 else if (SegmentName == "__DATA") 238 total_data += Sec.size; 239 else if (SegmentName == "__OBJC") 240 total_objc += Sec.size; 241 else 242 total_others += Sec.size; 243 } 244 } else { 245 StringRef SegmentName = StringRef(Seg.segname); 246 if (SegmentName == "__TEXT") 247 total_text += Seg.vmsize; 248 else if (SegmentName == "__DATA") 249 total_data += Seg.vmsize; 250 else if (SegmentName == "__OBJC") 251 total_objc += Seg.vmsize; 252 else 253 total_others += Seg.vmsize; 254 } 255 } 256 if (I == LoadCommandCount - 1) 257 break; 258 else 259 Load = MachO->getNextLoadCommandInfo(Load); 260 } 261 uint64_t total = total_text + total_data + total_objc + total_others; 262 263 if (!berkeleyHeaderPrinted) { 264 outs() << "__TEXT\t__DATA\t__OBJC\tothers\tdec\thex\n"; 265 berkeleyHeaderPrinted = true; 266 } 267 outs() << total_text << "\t" << total_data << "\t" << total_objc << "\t" 268 << total_others << "\t" << total << "\t" << format("%" PRIx64, total) 269 << "\t"; 270 } 271 272 /// @brief Print the size of each section in @p Obj. 273 /// 274 /// The format used is determined by @c OutputFormat and @c Radix. 275 static void PrintObjectSectionSizes(ObjectFile *Obj) { 276 uint64_t total = 0; 277 std::string fmtbuf; 278 raw_string_ostream fmt(fmtbuf); 279 const char *radix_fmt = getRadixFmt(); 280 281 // If OutputFormat is darwin and we have a MachOObjectFile print as darwin's 282 // size(1) -m output, else if OutputFormat is darwin and not a Mach-O object 283 // let it fall through to OutputFormat berkeley. 284 MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(Obj); 285 if (OutputFormat == darwin && MachO) 286 PrintDarwinSectionSizes(MachO); 287 // If we have a MachOObjectFile and the OutputFormat is berkeley print as 288 // darwin's default berkeley format for Mach-O files. 289 else if (MachO && OutputFormat == berkeley) 290 PrintDarwinSegmentSizes(MachO); 291 else if (OutputFormat == sysv) { 292 // Run two passes over all sections. The first gets the lengths needed for 293 // formatting the output. The second actually does the output. 294 std::size_t max_name_len = strlen("section"); 295 std::size_t max_size_len = strlen("size"); 296 std::size_t max_addr_len = strlen("addr"); 297 for (const SectionRef &Section : Obj->sections()) { 298 uint64_t size = 0; 299 if (error(Section.getSize(size))) 300 return; 301 total += size; 302 303 StringRef name; 304 uint64_t addr = 0; 305 if (error(Section.getName(name))) 306 return; 307 if (error(Section.getAddress(addr))) 308 return; 309 max_name_len = std::max(max_name_len, name.size()); 310 max_size_len = std::max(max_size_len, getNumLengthAsString(size)); 311 max_addr_len = std::max(max_addr_len, getNumLengthAsString(addr)); 312 } 313 314 // Add extra padding. 315 max_name_len += 2; 316 max_size_len += 2; 317 max_addr_len += 2; 318 319 // Setup header format. 320 fmt << "%-" << max_name_len << "s " 321 << "%" << max_size_len << "s " 322 << "%" << max_addr_len << "s\n"; 323 324 // Print header 325 outs() << format(fmt.str().c_str(), 326 static_cast<const char*>("section"), 327 static_cast<const char*>("size"), 328 static_cast<const char*>("addr")); 329 fmtbuf.clear(); 330 331 // Setup per section format. 332 fmt << "%-" << max_name_len << "s " 333 << "%#" << max_size_len << radix_fmt << " " 334 << "%#" << max_addr_len << radix_fmt << "\n"; 335 336 // Print each section. 337 for (const SectionRef &Section : Obj->sections()) { 338 StringRef name; 339 uint64_t size = 0; 340 uint64_t addr = 0; 341 if (error(Section.getName(name))) 342 return; 343 if (error(Section.getSize(size))) 344 return; 345 if (error(Section.getAddress(addr))) 346 return; 347 std::string namestr = name; 348 349 outs() << format(fmt.str().c_str(), namestr.c_str(), size, addr); 350 } 351 352 // Print total. 353 fmtbuf.clear(); 354 fmt << "%-" << max_name_len << "s " 355 << "%#" << max_size_len << radix_fmt << "\n"; 356 outs() << format(fmt.str().c_str(), 357 static_cast<const char*>("Total"), 358 total); 359 } else { 360 // The Berkeley format does not display individual section sizes. It 361 // displays the cumulative size for each section type. 362 uint64_t total_text = 0; 363 uint64_t total_data = 0; 364 uint64_t total_bss = 0; 365 366 // Make one pass over the section table to calculate sizes. 367 for (const SectionRef &Section : Obj->sections()) { 368 uint64_t size = 0; 369 bool isText = false; 370 bool isData = false; 371 bool isBSS = false; 372 if (error(Section.getSize(size))) 373 return; 374 if (error(Section.isText(isText))) 375 return; 376 if (error(Section.isData(isData))) 377 return; 378 if (error(Section.isBSS(isBSS))) 379 return; 380 if (isText) 381 total_text += size; 382 else if (isData) 383 total_data += size; 384 else if (isBSS) 385 total_bss += size; 386 } 387 388 total = total_text + total_data + total_bss; 389 390 if (!berkeleyHeaderPrinted) { 391 outs() << " text data bss " 392 << (Radix == octal ? "oct" : "dec") 393 << " hex filename\n"; 394 berkeleyHeaderPrinted = true; 395 } 396 397 // Print result. 398 fmt << "%#7" << radix_fmt << " " 399 << "%#7" << radix_fmt << " " 400 << "%#7" << radix_fmt << " "; 401 outs() << format(fmt.str().c_str(), 402 total_text, 403 total_data, 404 total_bss); 405 fmtbuf.clear(); 406 fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << " " 407 << "%7" PRIx64 " "; 408 outs() << format(fmt.str().c_str(), 409 total, 410 total); 411 } 412 } 413 414 /// @brief Print the section sizes for @p file. If @p file is an archive, print 415 /// the section sizes for each archive member. 416 static void PrintFileSectionSizes(StringRef file) { 417 // If file is not stdin, check that it exists. 418 if (file != "-") { 419 bool exists; 420 if (sys::fs::exists(file, exists) || !exists) { 421 errs() << ToolName << ": '" << file << "': " << "No such file\n"; 422 return; 423 } 424 } 425 426 // Attempt to open the binary. 427 ErrorOr<Binary *> BinaryOrErr = createBinary(file); 428 if (std::error_code EC = BinaryOrErr.getError()) { 429 errs() << ToolName << ": " << file << ": " << EC.message() << ".\n"; 430 return; 431 } 432 std::unique_ptr<Binary> binary(BinaryOrErr.get()); 433 434 if (Archive *a = dyn_cast<Archive>(binary.get())) { 435 // This is an archive. Iterate over each member and display its sizes. 436 for (object::Archive::child_iterator i = a->child_begin(), 437 e = a->child_end(); i != e; ++i) { 438 ErrorOr<std::unique_ptr<Binary>> ChildOrErr = i->getAsBinary(); 439 if (std::error_code EC = ChildOrErr.getError()) { 440 errs() << ToolName << ": " << file << ": " << EC.message() << ".\n"; 441 continue; 442 } 443 if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) { 444 MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o); 445 if (OutputFormat == sysv) 446 outs() << o->getFileName() << " (ex " << a->getFileName() 447 << "):\n"; 448 else if(MachO && OutputFormat == darwin) 449 outs() << a->getFileName() << "(" << o->getFileName() << "):\n"; 450 PrintObjectSectionSizes(o); 451 if (OutputFormat == berkeley) { 452 if (MachO) 453 outs() << a->getFileName() << "(" << o->getFileName() << ")\n"; 454 else 455 outs() << o->getFileName() << " (ex " << a->getFileName() << ")\n"; 456 } 457 } 458 } 459 } else if (ObjectFile *o = dyn_cast<ObjectFile>(binary.get())) { 460 if (OutputFormat == sysv) 461 outs() << o->getFileName() << " :\n"; 462 PrintObjectSectionSizes(o); 463 if (OutputFormat == berkeley) { 464 MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o); 465 if (!MachO || moreThanOneFile) 466 outs() << o->getFileName(); 467 outs() << "\n"; 468 } 469 } else { 470 errs() << ToolName << ": " << file << ": " << "Unrecognized file type.\n"; 471 } 472 // System V adds an extra newline at the end of each file. 473 if (OutputFormat == sysv) 474 outs() << "\n"; 475 } 476 477 int main(int argc, char **argv) { 478 // Print a stack trace if we signal out. 479 sys::PrintStackTraceOnErrorSignal(); 480 PrettyStackTraceProgram X(argc, argv); 481 482 llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 483 cl::ParseCommandLineOptions(argc, argv, "llvm object size dumper\n"); 484 485 ToolName = argv[0]; 486 if (OutputFormatShort.getNumOccurrences()) 487 OutputFormat = OutputFormatShort; 488 if (RadixShort.getNumOccurrences()) 489 Radix = RadixShort; 490 491 if (InputFilenames.size() == 0) 492 InputFilenames.push_back("a.out"); 493 494 moreThanOneFile = InputFilenames.size() > 1; 495 std::for_each(InputFilenames.begin(), InputFilenames.end(), 496 PrintFileSectionSizes); 497 498 return 0; 499 } 500