Skip to content

Commit 764da64

Browse files
committed
Add segment-aware VM addressing for universal binary support
Introduces per-arch VM address spaces to allow multiple Mach-O architecture slices to coexist without address conflicts. RangeSink gains an arch_index field which gets passed to all ForEachLoadCommand calls. DualMap holds a map<int, RangeMap> for VM space per arch_index while the file map remains shared.
1 parent 4a601b6 commit 764da64

7 files changed

Lines changed: 412 additions & 65 deletions

File tree

src/bloaty.cc

Lines changed: 100 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,7 +1149,7 @@ bool RangeSink::IsVerboseForVMRange(uint64_t vmaddr, uint64_t vmsize) {
11491149
RangeMap vm_map;
11501150
RangeMap file_map;
11511151
bool contains = false;
1152-
vm_map.AddRangeWithTranslation(vmaddr, vmsize, "", translator_->vm_map,
1152+
vm_map.AddRangeWithTranslation(vmaddr, vmsize, "", translator_->GetVMMap(arch_index_),
11531153
false, &file_map);
11541154
file_map.ForEachRange(
11551155
[this, &contains](uint64_t fileoff, uint64_t filesize) {
@@ -1182,7 +1182,7 @@ bool RangeSink::IsVerboseForFileRange(uint64_t fileoff, uint64_t filesize) {
11821182
RangeMap file_map;
11831183
bool contains = false;
11841184
file_map.AddRangeWithTranslation(fileoff, filesize, "",
1185-
translator_->file_map, false, &vm_map);
1185+
translator_->GetFileMap(), false, &vm_map);
11861186
vm_map.ForEachRange([this, &contains](uint64_t vmaddr, uint64_t vmsize) {
11871187
if (ContainsVerboseVMAddr(vmaddr, vmsize)) {
11881188
contains = true;
@@ -1209,15 +1209,15 @@ void RangeSink::AddFileRange(const char* analyzer, string_view name,
12091209
for (auto& pair : outputs_) {
12101210
const std::string label = pair.second->Munge(name);
12111211
if (translator_) {
1212-
bool ok = pair.first->file_map.AddRangeWithTranslation(
1213-
fileoff, filesize, label, translator_->file_map, verbose,
1214-
&pair.first->vm_map);
1212+
bool ok = pair.first->GetFileMap().AddRangeWithTranslation(
1213+
fileoff, filesize, label, translator_->GetFileMap(), verbose,
1214+
&pair.first->GetVMMap(arch_index_));
12151215
if (!ok) {
12161216
WARN("File range ($0, $1) for label $2 extends beyond base map",
12171217
fileoff, filesize, name);
12181218
}
12191219
} else {
1220-
pair.first->file_map.AddRange(fileoff, filesize, label);
1220+
pair.first->GetFileMap().AddRange(fileoff, filesize, label);
12211221
}
12221222
}
12231223
}
@@ -1235,10 +1235,10 @@ void RangeSink::AddFileRangeForVMAddr(const char* analyzer,
12351235
assert(translator_);
12361236
for (auto& pair : outputs_) {
12371237
std::string label;
1238-
if (pair.first->vm_map.TryGetLabel(label_from_vmaddr, &label)) {
1239-
bool ok = pair.first->file_map.AddRangeWithTranslation(
1240-
file_offset, file_range.size(), label, translator_->file_map, verbose,
1241-
&pair.first->vm_map);
1238+
if (pair.first->GetVMMap(arch_index_).TryGetLabel(label_from_vmaddr, &label)) {
1239+
bool ok = pair.first->GetFileMap().AddRangeWithTranslation(
1240+
file_offset, file_range.size(), label, translator_->GetFileMap(), verbose,
1241+
&pair.first->GetVMMap(arch_index_));
12421242
if (!ok) {
12431243
WARN("File range ($0, $1) for label $2 extends beyond base map",
12441244
file_offset, file_range.size(), label);
@@ -1264,11 +1264,11 @@ void RangeSink::AddFileRangeForFileRange(const char* analyzer,
12641264
assert(translator_);
12651265
for (auto& pair : outputs_) {
12661266
std::string label;
1267-
if (pair.first->file_map.TryGetLabelForRange(
1267+
if (pair.first->GetFileMap().TryGetLabelForRange(
12681268
from_file_offset, from_file_range.size(), &label)) {
1269-
bool ok = pair.first->file_map.AddRangeWithTranslation(
1270-
file_offset, file_range.size(), label, translator_->file_map, verbose,
1271-
&pair.first->vm_map);
1269+
bool ok = pair.first->GetFileMap().AddRangeWithTranslation(
1270+
file_offset, file_range.size(), label, translator_->GetFileMap(), verbose,
1271+
&pair.first->GetVMMap(arch_index_));
12721272
if (!ok) {
12731273
WARN("File range ($0, $1) for label $2 extends beyond base map",
12741274
file_offset, file_range.size(), label);
@@ -1293,10 +1293,10 @@ void RangeSink::AddVMRangeForVMAddr(const char* analyzer,
12931293
assert(translator_);
12941294
for (auto& pair : outputs_) {
12951295
std::string label;
1296-
if (pair.first->vm_map.TryGetLabel(label_from_vmaddr, &label)) {
1297-
bool ok = pair.first->vm_map.AddRangeWithTranslation(
1298-
addr, size, label, translator_->vm_map, verbose,
1299-
&pair.first->file_map);
1296+
if (pair.first->GetVMMap(arch_index_).TryGetLabel(label_from_vmaddr, &label)) {
1297+
bool ok = pair.first->GetVMMap(arch_index_).AddRangeWithTranslation(
1298+
addr, size, label, translator_->GetVMMap(arch_index_), verbose,
1299+
&pair.first->GetFileMap());
13001300
if (!ok && verbose_level > 1) {
13011301
WARN("VM range ($0, $1) for label $2 extends beyond base map", addr,
13021302
size, label);
@@ -1318,9 +1318,9 @@ void RangeSink::AddVMRange(const char* analyzer, uint64_t vmaddr,
13181318
assert(translator_);
13191319
for (auto& pair : outputs_) {
13201320
const std::string label = pair.second->Munge(name);
1321-
bool ok = pair.first->vm_map.AddRangeWithTranslation(
1322-
vmaddr, vmsize, label, translator_->vm_map, verbose,
1323-
&pair.first->file_map);
1321+
bool ok = pair.first->GetVMMap(arch_index_).AddRangeWithTranslation(
1322+
vmaddr, vmsize, label, translator_->GetVMMap(arch_index_), verbose,
1323+
&pair.first->GetFileMap());
13241324
if (!ok) {
13251325
WARN("VM range ($0, $1) for label $2 extends beyond base map", vmaddr,
13261326
vmsize, name);
@@ -1361,8 +1361,8 @@ void RangeSink::AddRange(const char* analyzer, string_view name,
13611361
}
13621362

13631363
if (translator_) {
1364-
if (!translator_->vm_map.CoversRange(vmaddr, vmsize) ||
1365-
!translator_->file_map.CoversRange(fileoff, filesize)) {
1364+
if (!translator_->GetVMMap(arch_index_).CoversRange(vmaddr, vmsize) ||
1365+
!translator_->GetFileMap().CoversRange(fileoff, filesize)) {
13661366
WARN("AddRange($0, $1, $2, $3, $4) will be ignored, because it is not "
13671367
"covered by base map.",
13681368
name.data(), vmaddr, vmsize, fileoff, filesize);
@@ -1374,11 +1374,11 @@ void RangeSink::AddRange(const char* analyzer, string_view name,
13741374
const std::string label = pair.second->Munge(name);
13751375
uint64_t common = std::min(vmsize, filesize);
13761376

1377-
pair.first->vm_map.AddDualRange(vmaddr, common, fileoff, label);
1378-
pair.first->file_map.AddDualRange(fileoff, common, vmaddr, label);
1377+
pair.first->GetVMMap(arch_index_).AddDualRange(vmaddr, common, fileoff, label);
1378+
pair.first->GetFileMap().AddDualRange(fileoff, common, vmaddr, label);
13791379

1380-
pair.first->vm_map.AddRange(vmaddr + common, vmsize - common, label);
1381-
pair.first->file_map.AddRange(fileoff + common, filesize - common, label);
1380+
pair.first->GetVMMap(arch_index_).AddRange(vmaddr + common, vmsize - common, label);
1381+
pair.first->GetFileMap().AddRange(fileoff + common, filesize - common, label);
13821382
}
13831383
}
13841384

@@ -1387,18 +1387,18 @@ uint64_t RangeSink::TranslateFileToVM(const char* ptr) {
13871387
uint64_t offset = ptr - file_->data().data();
13881388
uint64_t translated;
13891389
if (!FileContainsPointer(ptr) ||
1390-
!translator_->file_map.Translate(offset, &translated)) {
1390+
!translator_->GetFileMap().Translate(offset, &translated)) {
13911391
THROWF("Can't translate file offset ($0) to VM, contains: $1, map:\n$2",
13921392
offset, FileContainsPointer(ptr),
1393-
translator_->file_map.DebugString().c_str());
1393+
translator_->GetFileMap().DebugString().c_str());
13941394
}
13951395
return translated;
13961396
}
13971397

13981398
std::string_view RangeSink::TranslateVMToFile(uint64_t address) {
13991399
assert(translator_);
14001400
uint64_t translated;
1401-
if (!translator_->vm_map.Translate(address, &translated) ||
1401+
if (!translator_->GetVMMap(arch_index_).Translate(address, &translated) ||
14021402
translated > file_->data().size()) {
14031403
THROWF("Can't translate VM pointer ($0) to file", address);
14041404

@@ -1864,14 +1864,33 @@ struct DualMaps {
18641864
}
18651865

18661866
void ComputeRollup(Rollup* rollup) {
1867+
// Collect all arch indices across all maps.
1868+
std::set<int> arch_indices;
18671869
for (auto& map : maps_) {
1868-
map->vm_map.Compress();
1869-
map->file_map.Compress();
1870+
for (int id : map->VMArchIndices()) {
1871+
arch_indices.insert(id);
1872+
}
1873+
}
1874+
// Compress and roll up each arch's VM maps separately.
1875+
for (int arch_id : arch_indices) {
1876+
std::vector<const RangeMap*> vm_maps;
1877+
for (auto& map : maps_) {
1878+
if (map->HasVMMap(arch_id)) {
1879+
map->GetVMMap(arch_id).Compress();
1880+
vm_maps.push_back(&map->GetVMMap(arch_id));
1881+
}
1882+
}
1883+
if (!vm_maps.empty()) {
1884+
RangeMap::ComputeRollup(vm_maps,
1885+
[=](const std::vector<std::string>& keys, uint64_t addr, uint64_t end) {
1886+
return rollup->AddSizes(keys, end - addr, true);
1887+
});
1888+
}
1889+
}
1890+
// File maps are shared across all arches.
1891+
for (auto& map : maps_) {
1892+
map->GetFileMap().Compress();
18701893
}
1871-
RangeMap::ComputeRollup(VmMaps(), [=](const std::vector<std::string>& keys,
1872-
uint64_t addr, uint64_t end) {
1873-
return rollup->AddSizes(keys, end - addr, true);
1874-
});
18751894
RangeMap::ComputeRollup(
18761895
FileMaps(),
18771896
[=](const std::vector<std::string>& keys, uint64_t addr, uint64_t end) {
@@ -1895,7 +1914,38 @@ struct DualMaps {
18951914
}
18961915

18971916
void PrintFileMaps() { PrintMaps(FileMaps()); }
1898-
void PrintVMMaps() { PrintMaps(VmMaps()); }
1917+
1918+
void PrintVMMaps() {
1919+
std::set<int> arch_indices;
1920+
for (auto& map : maps_) {
1921+
for (int id : map->VMArchIndices()) {
1922+
arch_indices.insert(id);
1923+
}
1924+
}
1925+
for (int arch_id : arch_indices) {
1926+
if (arch_indices.size() > 1) {
1927+
auto it = arch_names_.find(arch_id);
1928+
if (it != arch_names_.end()) {
1929+
printf("%s:\n", it->second.c_str());
1930+
} else {
1931+
printf("arch %d:\n", arch_id);
1932+
}
1933+
}
1934+
std::vector<const RangeMap*> vm_maps;
1935+
for (auto& map : maps_) {
1936+
if (map->HasVMMap(arch_id)) {
1937+
vm_maps.push_back(&map->GetVMMap(arch_id));
1938+
}
1939+
}
1940+
if (!vm_maps.empty()) {
1941+
PrintMaps(vm_maps);
1942+
}
1943+
}
1944+
}
1945+
1946+
void SetArchNames(std::map<int, std::string> names) {
1947+
arch_names_ = std::move(names);
1948+
}
18991949

19001950
std::string KeysToString(const std::vector<std::string>& keys) {
19011951
std::string ret;
@@ -1921,23 +1971,16 @@ struct DualMaps {
19211971
DualMap* base_map() { return maps_[0].get(); }
19221972

19231973
private:
1924-
std::vector<const RangeMap*> VmMaps() const {
1925-
std::vector<const RangeMap*> ret;
1926-
for (const auto& map : maps_) {
1927-
ret.push_back(&map->vm_map);
1928-
}
1929-
return ret;
1930-
}
1931-
19321974
std::vector<const RangeMap*> FileMaps() const {
19331975
std::vector<const RangeMap*> ret;
19341976
for (const auto& map : maps_) {
1935-
ret.push_back(&map->file_map);
1977+
ret.push_back(&map->GetFileMap());
19361978
}
19371979
return ret;
19381980
}
19391981

19401982
std::vector<std::unique_ptr<DualMap>> maps_;
1983+
std::map<int, std::string> arch_names_;
19411984
};
19421985

19431986
void Bloaty::ScanAndRollupFile(const std::string& filename, Rollup* rollup,
@@ -2002,14 +2045,19 @@ void Bloaty::ScanAndRollupFile(const std::string& filename, Rollup* rollup,
20022045
rollup->file_total() + rollup->filtered_file_total();
20032046
file->ProcessFile(sink_ptrs);
20042047

2048+
maps.SetArchNames(file->GetArchNames());
2049+
20052050
// kInputFile source: Copy the base map to the filename sink(s).
20062051
for (auto sink : filename_sink_ptrs) {
2007-
maps.base_map()->vm_map.ForEachRange(
2008-
[sink](uint64_t start, uint64_t length) {
2009-
sink->AddVMRange("inputfile_vmcopier", start, length,
2010-
sink->input_file().filename());
2011-
});
2012-
maps.base_map()->file_map.ForEachRange(
2052+
for (int arch_id : maps.base_map()->VMArchIndices()) {
2053+
sink->set_arch_index(arch_id);
2054+
maps.base_map()->GetVMMap(arch_id).ForEachRange(
2055+
[sink](uint64_t start, uint64_t length) {
2056+
sink->AddVMRange("inputfile_vmcopier", start, length,
2057+
sink->input_file().filename());
2058+
});
2059+
}
2060+
maps.base_map()->GetFileMap().ForEachRange(
20132061
[sink](uint64_t start, uint64_t length) {
20142062
sink->AddFileRange("inputfile_filecopier",
20152063
sink->input_file().filename(), start, length);

src/bloaty.h

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@
2020
#define BLOATY_H_
2121

2222
#include <stdlib.h>
23+
#include <cassert>
2324
#define __STDC_LIMIT_MACROS
2425
#define __STDC_FORMAT_MACROS
2526
#include <stdint.h>
2627
#include <inttypes.h>
2728

2829
#include <list>
30+
#include <map>
2931
#include <memory>
3032
#include <set>
3133
#include <string>
@@ -127,6 +129,8 @@ class RangeSink {
127129
DataSource data_source() const { return data_source_; }
128130
const InputFile &input_file() const { return *file_; }
129131
bool IsBaseMap() const { return translator_ == nullptr; }
132+
int arch_index() const { return arch_index_; }
133+
void set_arch_index(int arch_index) { arch_index_ = arch_index; }
130134

131135
// If vmsize or filesize is zero, this mapping is presumed not to exist in
132136
// that domain. For example, .bss mappings don't exist in the file, and
@@ -239,6 +243,7 @@ class RangeSink {
239243
const DualMap* translator_;
240244
std::vector<std::pair<DualMap*, const NameMunger*>> outputs_;
241245
google::protobuf::Arena *arena_;
246+
int arch_index_ = 0;
242247
};
243248

244249
// NameMunger //////////////////////////////////////////////////////////////////
@@ -329,6 +334,11 @@ class ObjectFile {
329334
// given here, otherwise it is |this|.
330335
virtual void ProcessFile(const std::vector<RangeSink*>& sinks) const = 0;
331336

337+
// Optionally return human-readable names keyed by arch index, e.g.
338+
// {0: "x86_64", 1: "arm64"}. Only meaningful for formats with multiple VM
339+
// address spaces, such as Mach-O universal binaries.
340+
virtual std::map<int, std::string> GetArchNames() const { return {}; }
341+
332342
virtual bool GetDisassemblyInfo(std::string_view symbol,
333343
DataSource symbol_source,
334344
DisassemblyInfo* info) const = 0;
@@ -376,11 +386,44 @@ std::string ItaniumDemangle(std::string_view symbol, DataSource source);
376386

377387
// DualMap /////////////////////////////////////////////////////////////////////
378388

379-
// Contains a RangeMap for VM space and file space for a given file.
389+
// Contains RangeMaps for VM space and file space for a given file.
390+
// For universal binaries, each architecture slice has its own VM address space
391+
// (keyed by arch_index). File offsets are shared across all arches and use a
392+
// single RangeMap. For single-arch binaries, only arch_index 0 is used.
380393

381394
struct DualMap {
382-
RangeMap vm_map;
395+
RangeMap& GetVMMap(int arch_index) { return vm_maps_[arch_index]; }
396+
const RangeMap& GetVMMap(int arch_index) const {
397+
auto it = vm_maps_.find(arch_index);
398+
if (it == vm_maps_.end()) {
399+
static const RangeMap empty;
400+
return empty;
401+
}
402+
return it->second;
403+
}
404+
405+
RangeMap& GetFileMap() { return file_map; }
406+
const RangeMap& GetFileMap() const { return file_map; }
407+
408+
bool HasVMMap(int arch_index) const {
409+
return vm_maps_.find(arch_index) != vm_maps_.end();
410+
}
411+
412+
std::vector<int> VMArchIndices() const {
413+
std::vector<int> ids;
414+
for (const auto& kv : vm_maps_) {
415+
ids.push_back(kv.first);
416+
}
417+
return ids;
418+
}
419+
420+
RangeMap& vm_map() { return GetVMMap(0); }
421+
const RangeMap& vm_map() const { return GetVMMap(0); }
422+
383423
RangeMap file_map;
424+
425+
private:
426+
std::map<int, RangeMap> vm_maps_;
384427
};
385428

386429
struct DisassemblyInfo {

src/disassemble.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ std::string DisassembleFunction(const DisassemblyInfo& info) {
212212
} else {
213213
op_str = "<" + std::to_string(iter->second);
214214
}
215-
} else if (info.symbol_map.vm_map.TryGetLabel(target, &label)) {
215+
} else if (info.symbol_map.vm_map().TryGetLabel(target, &label)) {
216216
op_str = label;
217217
}
218218
}

src/dwarf.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ void AddDIE(const dwarf::CU& cu, const GeneralDIE& die,
440440
// Unfortunately the location doesn't include a size, so we look that part
441441
// up in the symbol map.
442442
uint64_t size;
443-
if (symbol_map.vm_map.TryGetSize(addr, &size)) {
443+
if (symbol_map.GetVMMap(sink->arch_index()).TryGetSize(addr, &size)) {
444444
sink->AddVMRangeIgnoreDuplicate("dwarf_location", addr, size,
445445
cu.unit_name());
446446
} else {

0 commit comments

Comments
 (0)