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 | |
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(...) 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 | |
56 | static constexpr opt::OptTable::Info InfoTable[] = { |
57 | #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), |
58 | #include "Opts.inc" |
59 | #undef OPTION |
60 | }; |
61 | |
62 | class SizeOptTable : public opt::GenericOptTable { |
63 | public: |
64 | SizeOptTable() |
65 | : GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable) { |
66 | setGroupedShortOptions(true); |
67 | } |
68 | }; |
69 | |
70 | enum OutputFormatTy { berkeley, sysv, darwin }; |
71 | enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 }; |
72 | } // namespace |
73 | |
74 | static bool ArchAll = false; |
75 | static std::vector<StringRef> ArchFlags; |
76 | static bool ELFCommons; |
77 | static OutputFormatTy OutputFormat; |
78 | static bool DarwinLongFormat; |
79 | static RadixTy Radix = RadixTy::decimal; |
80 | static bool TotalSizes; |
81 | |
82 | static std::vector<std::string> InputFilenames; |
83 | |
84 | static std::string ToolName; |
85 | |
86 | // States |
87 | static bool HadError = false; |
88 | static bool = false; |
89 | static bool MoreThanOneFile = false; |
90 | static uint64_t TotalObjectText = 0; |
91 | static uint64_t TotalObjectData = 0; |
92 | static uint64_t TotalObjectBss = 0; |
93 | static uint64_t TotalObjectTotal = 0; |
94 | |
95 | static 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. |
107 | static 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. |
135 | static 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. |
152 | static 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. |
160 | static 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 |
173 | static 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 |
189 | static 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. |
206 | static 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. |
281 | static 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. |
355 | static 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. |
517 | static 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. |
542 | static 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 | |
851 | static 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 | |
867 | int 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 | |