Skip to content

Commit be40369

Browse files
committed
Add -d archs data source for Mach-O universal binaries
Implements the 'archs' data source to break down universal binaries by architecture. This allows users to: - View file size breakdown by architecture: bloaty -d archs - Filter to specific architecture: bloaty -d archs,segments --source-filter=arm64 - Hierarchical breakdown: bloaty -d archs,segments
1 parent 00340bf commit be40369

File tree

5 files changed

+352
-0
lines changed

5 files changed

+352
-0
lines changed

src/bloaty.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ struct DataSourceDefinition {
8585

8686
constexpr DataSourceDefinition data_sources[] = {
8787
{DataSource::kArchiveMembers, "armembers", "the .o files in a .a file"},
88+
{DataSource::kArchs, "archs", "architecture slices in universal binaries"},
8889
{DataSource::kCompileUnits, "compileunits",
8990
"source file for the .o file (translation unit). requires debug info."},
9091
{DataSource::kInputFiles, "inputfiles",

src/bloaty.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ enum class DataSource {
5858
kRawRanges,
5959
kSections,
6060
kSegments,
61+
kArchs,
6162

6263
// We always set this to one of the concrete symbol types below before
6364
// setting it on a sink.

src/elf.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1401,6 +1401,8 @@ class ElfObjectFile : public ObjectFile {
14011401
DoReadELFSections(sink, kReportByEscapedSectionName);
14021402
break;
14031403
}
1404+
case DataSource::kArchs:
1405+
THROW("ELF files do not support 'archs' data source");
14041406
default:
14051407
THROW("unknown data source");
14061408
}

src/macho.cc

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
#include <string_view>
2222

2323
#include "absl/strings/str_join.h"
24+
#include "absl/strings/str_format.h"
2425
#include "absl/strings/substitute.h"
26+
#include "third_party/darwin_xnu_macho/mach/machine.h"
2527
#include "third_party/darwin_xnu_macho/mach-o/loader.h"
2628
#include "third_party/darwin_xnu_macho/mach-o/fat.h"
2729
#include "third_party/darwin_xnu_macho/mach-o/nlist.h"
@@ -69,6 +71,57 @@ void MaybeAddOverhead(RangeSink* sink, const char* label, string_view data) {
6971
}
7072
}
7173

74+
// ARM64E capability field constants
75+
static constexpr uint32_t ARM64E_SUBTYPE_MASK = 0x00FFFFFF; // Low 24 bits: subtype proper
76+
77+
static bool IsArm64eSubtype(uint32_t cpusubtype) {
78+
uint32_t subtype_proper = cpusubtype & ARM64E_SUBTYPE_MASK;
79+
return subtype_proper == CPU_SUBTYPE_ARM64E;
80+
}
81+
82+
std::string CpuTypeToString(uint32_t cputype, uint32_t cpusubtype) {
83+
switch (cputype) {
84+
case CPU_TYPE_X86_64:
85+
switch (cpusubtype) {
86+
case CPU_SUBTYPE_X86_64_H:
87+
return "x86_64h";
88+
default:
89+
return "x86_64";
90+
}
91+
case CPU_TYPE_ARM64:
92+
if (IsArm64eSubtype(cpusubtype)) {
93+
return "arm64e";
94+
}
95+
switch (cpusubtype) {
96+
case CPU_SUBTYPE_ARM64_V8:
97+
return "arm64v8";
98+
default:
99+
return "arm64";
100+
}
101+
case CPU_TYPE_X86:
102+
return "i386";
103+
case CPU_TYPE_ARM:
104+
switch (cpusubtype) {
105+
case CPU_SUBTYPE_ARM_V6:
106+
return "armv6";
107+
case CPU_SUBTYPE_ARM_V7:
108+
return "armv7";
109+
case CPU_SUBTYPE_ARM_V7F:
110+
return "armv7f";
111+
case CPU_SUBTYPE_ARM_V7S:
112+
return "armv7s";
113+
case CPU_SUBTYPE_ARM_V7K:
114+
return "armv7k";
115+
case CPU_SUBTYPE_ARM_V8:
116+
return "armv8";
117+
default:
118+
return "arm";
119+
}
120+
default:
121+
return absl::StrFormat("cpu_%d", cputype);
122+
}
123+
}
124+
72125
struct LoadCommand {
73126
bool is64bit;
74127
uint32_t cmd;
@@ -652,6 +705,10 @@ class MachOObjectFile : public ObjectFile {
652705
ReadDWARFInlines(dwarf, sink, true);
653706
break;
654707
}
708+
case DataSource::kArchs: {
709+
ProcessArchitectures(sink);
710+
break;
711+
}
655712
case DataSource::kArchiveMembers:
656713
default:
657714
THROW("Mach-O doesn't support this data source");
@@ -660,6 +717,34 @@ class MachOObjectFile : public ObjectFile {
660717
}
661718
}
662719

720+
void ProcessArchitectures(RangeSink* sink) const {
721+
uint32_t magic = ReadMagic(file_data().data());
722+
723+
if (magic == FAT_CIGAM) {
724+
string_view header_data = file_data().data();
725+
auto header = GetStructPointerAndAdvance<fat_header>(&header_data);
726+
uint32_t nfat_arch = ByteSwap(header->nfat_arch);
727+
728+
for (uint32_t i = 0; i < nfat_arch; i++) {
729+
auto arch = GetStructPointerAndAdvance<fat_arch>(&header_data);
730+
uint32_t cputype = ByteSwap(arch->cputype);
731+
uint32_t cpusubtype = ByteSwap(arch->cpusubtype);
732+
uint32_t offset = ByteSwap(arch->offset);
733+
uint32_t size = ByteSwap(arch->size);
734+
735+
std::string arch_name = CpuTypeToString(cputype, cpusubtype);
736+
string_view slice_data = StrictSubstr(file_data().data(), offset, size);
737+
738+
sink->AddFileRange("archs", arch_name, slice_data);
739+
}
740+
} else {
741+
auto header = GetStructPointer<mach_header>(file_data().data());
742+
std::string arch_name = CpuTypeToString(header->cputype, header->cpusubtype);
743+
744+
sink->AddFileRange("archs", arch_name, file_data().data());
745+
}
746+
}
747+
663748
bool GetDisassemblyInfo(std::string_view /*symbol*/,
664749
DataSource /*symbol_source*/,
665750
DisassemblyInfo* /*info*/) const override {

0 commit comments

Comments
 (0)