From acc5e810aaa875698ff032a3b8353194a6d3e247 Mon Sep 17 00:00:00 2001 From: Kevin Enderby Date: Tue, 17 Jun 2014 17:54:13 +0000 Subject: [PATCH] =?UTF-8?q?Add=20"-format=20darwin"=20to=20llvm-size=20to?= =?UTF-8?q?=20be=20like=20darwin's=20size(1)=20-m=20output,=20and=20and=20?= =?UTF-8?q?the=20-l=20option=20for=20the=20long=20format.=20=20Also=20when?= =?UTF-8?q?=20the=20object=20is=20a=20Mach-O=20file=20and=20the=20format?= =?UTF-8?q?=20is=20berkeley=20produce=20output=20like=20darwin=E2=80=99s?= =?UTF-8?q?=20default=20size(1)=20summary=20berkeley=20derived=20output.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Like System V format, there are also some small changes in how and where the file names and archive member names are printed for darwin and Mach-O. Like the changes to llvm-nm these are the first steps in seeing if it is possible to make llvm-size produce the same output as darwin's size(1). git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@211117 91177308-0d34-0410-b5e6-96231b3b80d8 --- test/Object/Inputs/hello-world.macho-x86_64 | Bin 0 -> 8496 bytes test/Object/Inputs/macho-archive-x86_64.a | Bin 0 -> 1304 bytes test/Object/size-trivial-macho.test | 56 ++++- tools/llvm-size/llvm-size.cpp | 245 ++++++++++++++++++-- 4 files changed, 275 insertions(+), 26 deletions(-) create mode 100755 test/Object/Inputs/hello-world.macho-x86_64 create mode 100644 test/Object/Inputs/macho-archive-x86_64.a diff --git a/test/Object/Inputs/hello-world.macho-x86_64 b/test/Object/Inputs/hello-world.macho-x86_64 new file mode 100755 index 0000000000000000000000000000000000000000..d004bedf6ad44f0965a1c9d49438f521f2206c75 GIT binary patch literal 8496 zcmeHM&r4KM6uwV0O)W=|sH}KNh83s_7lAFDaOg>rzafISIr?lS-i$Ky1~m`_ix|jo zAGB^02rZ&*1-9!is8z)T1uaFQ>HFT?r+3C5`{*7x-20t#?z!iFb055U=i}DTKea+w z)j}*+2_YiX8X+DD8#5tJLLI1-!_n)p+p*ChTFb>MhkGLC>A(h3j>m3|mxl!Fxv?$^ zqc(wZ#9`%=(svj9oxsX`-Ay-%;f!)}<^r98%`e^U3tI zOr~dM16=-lJX87jn^TAn5-Ht;oXJe4+)`9!eFc^8gi1n;ySA_Jo)n-`_D9E~S{Uv_ z**yvGi*`?#H$1PDX-{SsQ}J0(&iR>r^Mz@6@%6g+dj|Wd5~TDd56Tz8ddAnOcw#*t z@3D?|o~yxun>S+p1K~4aSDX`=(yfYN9=)zMJh$BpuuFT_VGOwr%)lr`bYo;3`Jw*> zFrSM}j8`x=K!>5aFg8@(uFng0#q*_{$1uVr8lhdeY^KXg#=E8$y=h=dE0lG(Z$CvB z*L!;&w?28*fB$v-JZ!929H|fVQiy4Tbl|*P$LCrX?!$A?$C@qcC5~t9?2Y}$7PG#a z>b%y8I+Ae&K`ubK-(9=J-$jKJz?92S`ic868ZitQ1`Gp+0mFb{z%XDK_`evKaPnWB zl^?dVS~G-`Z^VA{{Q0F#C!hP}XFs1BL;^fMLKeU>GnA7zPXj zh5^HXVc=h6pt&tF!n&LL*qjTR;WCc?Fq+pR8L=iZk>)-*hX%Ai0|c=o`temVm5}a& zJC*ZY^tvY*el9hcOpA7Yov0S;XBi6#CtIHz*~Y&L4L>imz{U`?^a2}Ou3X)qwdw#% zZBsg$te`U(1RoD@=|cz;@&gIRFt=bIQOBjSU_={_a~mDu-Di-&8N&1KVg&2{0(!F2 AbpQYW literal 0 HcmV?d00001 diff --git a/test/Object/Inputs/macho-archive-x86_64.a b/test/Object/Inputs/macho-archive-x86_64.a new file mode 100644 index 0000000000000000000000000000000000000000..9979ba9dd1e3862410a4b4d1cd9bc442cb8c626e GIT binary patch literal 1304 zcmbVML2DCH5T32I4XY+Ocu?^0L=QdHecL27Ad+ArJt&qof*xeDrYZ5zri3jmo_Y|W zxBdiwhR1>zZ~h9Q7yp0<^_zWfcDGVR{mAUhH*YdCZ!(+P_mkOZ@*uop@5PZRU>i}~ zZrXOMVT_IOFxJYcjiOcqw3P=BL+0hp7f*WqM`rWs#-QILpbohV>0A;$*aDT`8Vn7$Vj3)a9Yi-@bldBnqTl01^FO3#Gq}Z-l^ai}zvgBf(FOZ! zS7O_2=kLw={cYsiJ`C{|{BeKRr61$U%l@7@^TMa@*q* OutputFormat("format", cl::desc("Specify output format"), cl::values(clEnumVal(sysv, "System V format"), clEnumVal(berkeley, "Berkeley format"), - clEnumValEnd), + clEnumVal(darwin, "Darwin -m format"), clEnumValEnd), cl::init(berkeley)); static cl::opt @@ -47,6 +48,13 @@ static cl::opt clEnumValEnd), cl::init(berkeley)); +static bool berkeleyHeaderPrinted = false; +static bool moreThanOneFile = false; + +cl::opt DarwinLongFormat("l", + cl::desc("When format is darwin, use long format " + "to include addresses and offsets.")); + enum RadixTy {octal = 8, decimal = 10, hexadecimal = 16}; static cl::opt Radix("-radix", @@ -85,6 +93,182 @@ static size_t getNumLengthAsString(uint64_t num) { return result.size(); } +/// @brief Return the the printing format for the Radix. +static const char * getRadixFmt(void) { + switch (Radix) { + case octal: + return PRIo64; + case decimal: + return PRIu64; + case hexadecimal: + return PRIx64; + } + return nullptr; +} + +/// @brief Print the size of each Mach-O segment and section in @p MachO. +/// +/// This is when used when @c OutputFormat is darwin and produces the same +/// output as darwin's size(1) -m output. +static void PrintDarwinSectionSizes(MachOObjectFile *MachO) { + std::string fmtbuf; + raw_string_ostream fmt(fmtbuf); + const char *radix_fmt = getRadixFmt(); + if (Radix == hexadecimal) + fmt << "0x"; + fmt << "%" << radix_fmt; + + uint32_t LoadCommandCount = MachO->getHeader().ncmds; + uint32_t Filetype = MachO->getHeader().filetype; + MachOObjectFile::LoadCommandInfo Load = MachO->getFirstLoadCommandInfo(); + + uint64_t total = 0; + for (unsigned I = 0; ; ++I) { + if (Load.C.cmd == MachO::LC_SEGMENT_64) { + MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load); + outs() << "Segment " << Seg.segname << ": " + << format(fmt.str().c_str(), Seg.vmsize); + if (DarwinLongFormat) + outs() << " (vmaddr 0x" << format("%" PRIx64, Seg.vmaddr) + << " fileoff " << Seg.fileoff << ")"; + outs() << "\n"; + total += Seg.vmsize; + uint64_t sec_total = 0; + for (unsigned J = 0; J < Seg.nsects; ++J) { + MachO::section_64 Sec = MachO->getSection64(Load, J); + if (Filetype == MachO::MH_OBJECT) + outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", " + << format("%.16s", &Sec.sectname) << "): "; + else + outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": "; + outs() << format(fmt.str().c_str(), Sec.size); + if (DarwinLongFormat) + outs() << " (addr 0x" << format("%" PRIx64, Sec.addr) + << " offset " << Sec.offset << ")"; + outs() << "\n"; + sec_total += Sec.size; + } + if (Seg.nsects != 0) + outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n"; + } + else if (Load.C.cmd == MachO::LC_SEGMENT) { + MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load); + outs() << "Segment " << Seg.segname << ": " + << format(fmt.str().c_str(), Seg.vmsize); + if (DarwinLongFormat) + outs() << " (vmaddr 0x" << format("%" PRIx64, Seg.vmaddr) + << " fileoff " << Seg.fileoff << ")"; + outs() << "\n"; + total += Seg.vmsize; + uint64_t sec_total = 0; + for (unsigned J = 0; J < Seg.nsects; ++J) { + MachO::section Sec = MachO->getSection(Load, J); + if (Filetype == MachO::MH_OBJECT) + outs() << "\tSection (" << format("%.16s", &Sec.segname) << ", " + << format("%.16s", &Sec.sectname) << "): "; + else + outs() << "\tSection " << format("%.16s", &Sec.sectname) << ": "; + outs() << format(fmt.str().c_str(), Sec.size); + if (DarwinLongFormat) + outs() << " (addr 0x" << format("%" PRIx64, Sec.addr) + << " offset " << Sec.offset << ")"; + outs() << "\n"; + sec_total += Sec.size; + } + if (Seg.nsects != 0) + outs() << "\ttotal " << format(fmt.str().c_str(), sec_total) << "\n"; + } + if (I == LoadCommandCount - 1) + break; + else + Load = MachO->getNextLoadCommandInfo(Load); + } + outs() << "total " << format(fmt.str().c_str(), total) << "\n"; +} + +/// @brief Print the summary sizes of the standard Mach-O segments in @p MachO. +/// +/// This is when used when @c OutputFormat is berkeley with a Mach-O file and +/// produces the same output as darwin's size(1) default output. +static void PrintDarwinSegmentSizes(MachOObjectFile *MachO) { + uint32_t LoadCommandCount = MachO->getHeader().ncmds; + MachOObjectFile::LoadCommandInfo Load = MachO->getFirstLoadCommandInfo(); + + uint64_t total_text = 0; + uint64_t total_data = 0; + uint64_t total_objc = 0; + uint64_t total_others = 0; + for (unsigned I = 0; ; ++I) { + if (Load.C.cmd == MachO::LC_SEGMENT_64) { + MachO::segment_command_64 Seg = MachO->getSegment64LoadCommand(Load); + if (MachO->getHeader().filetype == MachO::MH_OBJECT) { + for (unsigned J = 0; J < Seg.nsects; ++J) { + MachO::section_64 Sec = MachO->getSection64(Load, J); + StringRef SegmentName = StringRef(Sec.segname); + if (SegmentName == "__TEXT") + total_text += Sec.size; + else if (SegmentName == "__DATA") + total_data += Sec.size; + else if (SegmentName == "__OBJC") + total_objc += Sec.size; + else + total_others += Sec.size; + } + } else { + StringRef SegmentName = StringRef(Seg.segname); + if (SegmentName == "__TEXT") + total_text += Seg.vmsize; + else if (SegmentName == "__DATA") + total_data += Seg.vmsize; + else if (SegmentName == "__OBJC") + total_objc += Seg.vmsize; + else + total_others += Seg.vmsize; + } + } + else if (Load.C.cmd == MachO::LC_SEGMENT) { + MachO::segment_command Seg = MachO->getSegmentLoadCommand(Load); + if (MachO->getHeader().filetype == MachO::MH_OBJECT) { + for (unsigned J = 0; J < Seg.nsects; ++J) { + MachO::section Sec = MachO->getSection(Load, J); + StringRef SegmentName = StringRef(Sec.segname); + if (SegmentName == "__TEXT") + total_text += Sec.size; + else if (SegmentName == "__DATA") + total_data += Sec.size; + else if (SegmentName == "__OBJC") + total_objc += Sec.size; + else + total_others += Sec.size; + } + } else { + StringRef SegmentName = StringRef(Seg.segname); + if (SegmentName == "__TEXT") + total_text += Seg.vmsize; + else if (SegmentName == "__DATA") + total_data += Seg.vmsize; + else if (SegmentName == "__OBJC") + total_objc += Seg.vmsize; + else + total_others += Seg.vmsize; + } + } + if (I == LoadCommandCount - 1) + break; + else + Load = MachO->getNextLoadCommandInfo(Load); + } + uint64_t total = total_text + total_data + total_objc + total_others; + + if (!berkeleyHeaderPrinted) { + outs() << "__TEXT\t__DATA\t__OBJC\tothers\tdec\thex\n"; + berkeleyHeaderPrinted = true; + } + outs() << total_text << "\t" << total_data << "\t" << total_objc << "\t" + << total_others << "\t" << total << "\t" << format("%" PRIx64, total) + << "\t"; +} + /// @brief Print the size of each section in @p Obj. /// /// The format used is determined by @c OutputFormat and @c Radix. @@ -92,20 +276,19 @@ static void PrintObjectSectionSizes(ObjectFile *Obj) { uint64_t total = 0; std::string fmtbuf; raw_string_ostream fmt(fmtbuf); + const char *radix_fmt = getRadixFmt(); - const char *radix_fmt = nullptr; - switch (Radix) { - case octal: - radix_fmt = PRIo64; - break; - case decimal: - radix_fmt = PRIu64; - break; - case hexadecimal: - radix_fmt = PRIx64; - break; - } - if (OutputFormat == sysv) { + // If OutputFormat is darwin and we have a MachOObjectFile print as darwin's + // size(1) -m output, else if OutputFormat is darwin and not a Mach-O object + // let it fall through to OutputFormat berkeley. + MachOObjectFile *MachO = dyn_cast(Obj); + if (OutputFormat == darwin && MachO) + PrintDarwinSectionSizes(MachO); + // If we have a MachOObjectFile and the OutputFormat is berkeley print as + // darwin's default berkeley format for Mach-O files. + else if (MachO && OutputFormat == berkeley) + PrintDarwinSegmentSizes(MachO); + else if (OutputFormat == sysv) { // Run two passes over all sections. The first gets the lengths needed for // formatting the output. The second actually does the output. std::size_t max_name_len = strlen("section"); @@ -204,6 +387,13 @@ static void PrintObjectSectionSizes(ObjectFile *Obj) { total = total_text + total_data + total_bss; + if (!berkeleyHeaderPrinted) { + outs() << " text data bss " + << (Radix == octal ? "oct" : "dec") + << " hex filename\n"; + berkeleyHeaderPrinted = true; + } + // Print result. fmt << "%#7" << radix_fmt << " " << "%#7" << radix_fmt << " " @@ -251,20 +441,31 @@ static void PrintFileSectionSizes(StringRef file) { continue; } if (ObjectFile *o = dyn_cast(&*ChildOrErr.get())) { + MachOObjectFile *MachO = dyn_cast(o); if (OutputFormat == sysv) outs() << o->getFileName() << " (ex " << a->getFileName() << "):\n"; + else if(MachO && OutputFormat == darwin) + outs() << a->getFileName() << "(" << o->getFileName() << "):\n"; PrintObjectSectionSizes(o); - if (OutputFormat == berkeley) - outs() << o->getFileName() << " (ex " << a->getFileName() << ")\n"; + if (OutputFormat == berkeley) { + if (MachO) + outs() << a->getFileName() << "(" << o->getFileName() << ")\n"; + else + outs() << o->getFileName() << " (ex " << a->getFileName() << ")\n"; + } } } } else if (ObjectFile *o = dyn_cast(binary.get())) { if (OutputFormat == sysv) outs() << o->getFileName() << " :\n"; PrintObjectSectionSizes(o); - if (OutputFormat == berkeley) - outs() << o->getFileName() << "\n"; + if (OutputFormat == berkeley) { + MachOObjectFile *MachO = dyn_cast(o); + if (!MachO || moreThanOneFile) + outs() << o->getFileName(); + outs() << "\n"; + } } else { errs() << ToolName << ": " << file << ": " << "Unrecognized file type.\n"; } @@ -290,11 +491,7 @@ int main(int argc, char **argv) { if (InputFilenames.size() == 0) InputFilenames.push_back("a.out"); - if (OutputFormat == berkeley) - outs() << " text data bss " - << (Radix == octal ? "oct" : "dec") - << " hex filename\n"; - + moreThanOneFile = InputFilenames.size() > 1; std::for_each(InputFilenames.begin(), InputFilenames.end(), PrintFileSectionSizes);