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