[compiler-rt] [llvm] [Profile] Enable binary profile correlation for Mach-O binaries (PR #179937)
Marina Taylor via llvm-commits
llvm-commits at lists.llvm.org
Thu Feb 5 05:09:15 PST 2026
https://github.com/citymarina created https://github.com/llvm/llvm-project/pull/179937
The existing ELF/COFF correlation code mostly "just works" on Mach-O files, with one gotcha: on disk, the pointers in `__llvm_covdata` are stored in an encoded format due to dyld fixup chains. (In memory, they would normally be fixed up at load time in a running binary, but the correlator only looks at the on-disk values.)
LLVM's Mach-O reader knows how to decode the format, so this patch walks the fixup table to create a set of mappings that the correlator can use to resolve the values.
rdar://168259786
>From 2f3a40404848a0809b4951cda5b65433e59e4dbb Mon Sep 17 00:00:00 2001
From: Marina Taylor <marina_taylor at apple.com>
Date: Thu, 5 Feb 2026 13:02:31 +0000
Subject: [PATCH] [Profile] Enable binary profile correlation for Mach-O
binaries
The existing ELF/COFF correlation code mostly "just works" on Mach-O files, with one gotcha: on disk, the pointers in `__llvm_covdata` are stored in an encoded format due to dyld fixup chains. (In memory, they would normally be fixed up at load time in a running binary, but the correlator only looks at the on-disk values.)
LLVM's Mach-O reader knows how to decode the format, so this patch walks the fixup table to create a set of mappings that the correlator can use to resolve the values.
rdar://168259786
---
.../test/profile/instrprof-binary-correlate.c | 2 +-
.../llvm/ProfileData/InstrProfCorrelator.h | 7 ++-
llvm/lib/ProfileData/InstrProfCorrelator.cpp | 48 +++++++++++++++----
3 files changed, 46 insertions(+), 11 deletions(-)
diff --git a/compiler-rt/test/profile/instrprof-binary-correlate.c b/compiler-rt/test/profile/instrprof-binary-correlate.c
index 0e96745a9b58f..69648513943a6 100644
--- a/compiler-rt/test/profile/instrprof-binary-correlate.c
+++ b/compiler-rt/test/profile/instrprof-binary-correlate.c
@@ -1,4 +1,4 @@
-// REQUIRES: linux || windows
+// REQUIRES: linux || windows || darwin
// Default
// RUN: %clang -o %t.normal -fprofile-instr-generate -fcoverage-mapping %S/Inputs/instrprof-debug-info-correlate-main.cpp %S/Inputs/instrprof-debug-info-correlate-foo.cpp
// RUN: env LLVM_PROFILE_FILE=%t.profraw %run %t.normal
diff --git a/llvm/include/llvm/ProfileData/InstrProfCorrelator.h b/llvm/include/llvm/ProfileData/InstrProfCorrelator.h
index 1617ae782307c..acc7ef82ec1b7 100644
--- a/llvm/include/llvm/ProfileData/InstrProfCorrelator.h
+++ b/llvm/include/llvm/ProfileData/InstrProfCorrelator.h
@@ -12,6 +12,7 @@
#ifndef LLVM_PROFILEDATA_INSTRPROFCORRELATOR_H
#define LLVM_PROFILEDATA_INSTRPROFCORRELATOR_H
+#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/Debuginfod/BuildIDFetcher.h"
@@ -77,7 +78,7 @@ class InstrProfCorrelator {
protected:
struct Context {
LLVM_ABI static llvm::Expected<std::unique_ptr<Context>>
- get(std::unique_ptr<MemoryBuffer> Buffer, const object::ObjectFile &Obj,
+ get(std::unique_ptr<MemoryBuffer> Buffer, object::ObjectFile *Obj,
ProfCorrelatorKind FileKind);
std::unique_ptr<MemoryBuffer> Buffer;
/// The address range of the __llvm_prf_cnts section.
@@ -89,6 +90,10 @@ class InstrProfCorrelator {
const char *DataEnd;
const char *NameStart;
size_t NameSize;
+ /// Resolved values for Mach-O linker fixup chains when FileKind is Binary.
+ /// The mapping is from an address relative to the start of __llvm_covdata,
+ /// to the resolved pointer value at that address.
+ llvm::DenseMap<uint64_t, uint64_t> MachOFixups;
/// True if target and host have different endian orders.
bool ShouldSwapBytes;
};
diff --git a/llvm/lib/ProfileData/InstrProfCorrelator.cpp b/llvm/lib/ProfileData/InstrProfCorrelator.cpp
index 65fd5ba1c5ad2..081029d05baa4 100644
--- a/llvm/lib/ProfileData/InstrProfCorrelator.cpp
+++ b/llvm/lib/ProfileData/InstrProfCorrelator.cpp
@@ -54,20 +54,21 @@ const char *InstrProfCorrelator::NumCountersAttributeName = "Num Counters";
llvm::Expected<std::unique_ptr<InstrProfCorrelator::Context>>
InstrProfCorrelator::Context::get(std::unique_ptr<MemoryBuffer> Buffer,
- const object::ObjectFile &Obj,
+ object::ObjectFile *Obj,
ProfCorrelatorKind FileKind) {
auto C = std::make_unique<Context>();
- auto CountersSection = getInstrProfSection(Obj, IPSK_cnts);
+ auto CountersSection = getInstrProfSection(*Obj, IPSK_cnts);
if (auto Err = CountersSection.takeError())
return std::move(Err);
+ Triple::ObjectFormatType ObjFormat = Obj->getTripleObjectFormat();
if (FileKind == InstrProfCorrelator::BINARY) {
- auto DataSection = getInstrProfSection(Obj, IPSK_covdata);
+ auto DataSection = getInstrProfSection(*Obj, IPSK_covdata);
if (auto Err = DataSection.takeError())
return std::move(Err);
auto DataOrErr = DataSection->getContents();
if (!DataOrErr)
return DataOrErr.takeError();
- auto NameSection = getInstrProfSection(Obj, IPSK_covname);
+ auto NameSection = getInstrProfSection(*Obj, IPSK_covname);
if (auto Err = NameSection.takeError())
return std::move(Err);
auto NameOrErr = NameSection->getContents();
@@ -77,16 +78,35 @@ InstrProfCorrelator::Context::get(std::unique_ptr<MemoryBuffer> Buffer,
C->DataEnd = DataOrErr->data() + DataOrErr->size();
C->NameStart = NameOrErr->data();
C->NameSize = NameOrErr->size();
+
+ if (ObjFormat == Triple::MachO) {
+ std::string FullSectionName =
+ getInstrProfSectionName(IPSK_covdata, ObjFormat);
+ SmallVector<StringRef, 3> SegmentAndSection;
+ StringRef(FullSectionName).split(SegmentAndSection, ',', 2);
+ auto *MachO = static_cast<object::MachOObjectFile *>(Obj);
+ Error Err = Error::success();
+ for (const object::MachOChainedFixupEntry &Entry :
+ MachO->fixupTable(Err)) {
+ if (Entry.isRebase() && Entry.segmentName() == SegmentAndSection[0] &&
+ Entry.sectionName() == SegmentAndSection[1]) {
+ C->MachOFixups[Entry.address() - DataSection->getAddress()] =
+ Entry.pointerValue();
+ }
+ }
+ if (Err)
+ return std::move(Err);
+ }
}
C->Buffer = std::move(Buffer);
C->CountersSectionStart = CountersSection->getAddress();
C->CountersSectionEnd = C->CountersSectionStart + CountersSection->getSize();
// In COFF object file, there's a null byte at the beginning of the counter
// section which doesn't exist in raw profile.
- if (Obj.getTripleObjectFormat() == Triple::COFF)
+ if (ObjFormat == Triple::COFF)
++C->CountersSectionStart;
- C->ShouldSwapBytes = Obj.isLittleEndian() != sys::IsLittleEndianHost;
+ C->ShouldSwapBytes = Obj->isLittleEndian() != sys::IsLittleEndianHost;
return Expected<std::unique_ptr<Context>>(std::move(C));
}
@@ -157,7 +177,7 @@ InstrProfCorrelator::get(std::unique_ptr<MemoryBuffer> Buffer,
return std::move(Err);
if (auto *Obj = dyn_cast<object::ObjectFile>(BinOrErr->get())) {
- auto CtxOrErr = Context::get(std::move(Buffer), *Obj, FileKind);
+ auto CtxOrErr = Context::get(std::move(Buffer), Obj, FileKind);
if (auto Err = CtxOrErr.takeError())
return std::move(Err);
auto T = Obj->makeTriple();
@@ -218,11 +238,11 @@ InstrProfCorrelatorImpl<IntPtrT>::get(
instrprof_error::unable_to_correlate_profile,
"unsupported debug info format (only DWARF is supported)");
}
- if (Obj.isELF() || Obj.isCOFF())
+ if (Obj.isELF() || Obj.isCOFF() || Obj.isMachO())
return std::make_unique<BinaryInstrProfCorrelator<IntPtrT>>(std::move(Ctx));
return make_error<InstrProfError>(
instrprof_error::unable_to_correlate_profile,
- "unsupported binary format (only ELF and COFF are supported)");
+ "unsupported binary format (only ELF, COFF, and Mach-O are supported)");
}
template <class IntPtrT>
@@ -478,6 +498,16 @@ void BinaryInstrProfCorrelator<IntPtrT>::correlateProfileDataImpl(
uint64_t CounterPtr = this->template maybeSwap<IntPtrT>(I->CounterPtr);
uint64_t CountersStart = this->Ctx->CountersSectionStart;
uint64_t CountersEnd = this->Ctx->CountersSectionEnd;
+ if (!this->Ctx->MachOFixups.empty()) {
+ uint64_t Offset = (uint64_t)&I->CounterPtr - (uint64_t)DataStart;
+ auto It = this->Ctx->MachOFixups.find(Offset);
+ if (It != this->Ctx->MachOFixups.end()) {
+ CounterPtr = It->second;
+ } else if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) {
+ WithColor::warning() << format(
+ "Mach-O fixup not found for covdata offset 0x%llx\n", Offset);
+ }
+ }
if (CounterPtr < CountersStart || CounterPtr >= CountersEnd) {
if (UnlimitedWarnings || ++NumSuppressedWarnings < 1) {
WithColor::warning()
More information about the llvm-commits
mailing list