[llvm] [dsymutil] Add support for pseudo probes (PR #186877)
Henry Jiang via llvm-commits
llvm-commits at lists.llvm.org
Fri Apr 3 16:39:59 PDT 2026
https://github.com/mustartt updated https://github.com/llvm/llvm-project/pull/186877
>From 4004dac22d97919bb31d05de8f6e7b5018928b6d Mon Sep 17 00:00:00 2001
From: Henry Jiang <henry_jiang2 at apple.com>
Date: Thu, 12 Mar 2026 11:28:28 -0700
Subject: [PATCH 1/2] dsymutil support for pseudo probes
---
.../tools/dsymutil/AArch64/pseudo-probe.test | 315 ++++++++++++++++++
llvm/tools/dsymutil/MachOUtils.cpp | 71 +++-
2 files changed, 379 insertions(+), 7 deletions(-)
create mode 100644 llvm/test/tools/dsymutil/AArch64/pseudo-probe.test
diff --git a/llvm/test/tools/dsymutil/AArch64/pseudo-probe.test b/llvm/test/tools/dsymutil/AArch64/pseudo-probe.test
new file mode 100644
index 0000000000000..3d8afbfe601c7
--- /dev/null
+++ b/llvm/test/tools/dsymutil/AArch64/pseudo-probe.test
@@ -0,0 +1,315 @@
+## Test that dsymutil correctly copies pseudoprobe sections and segments
+
+# Example program
+
+# RUN: rm -rf %t && mkdir -p %t
+# RUN: yaml2obj %s -o %t/main.o
+# RUN: dsymutil %t/main.o -o %t/main.dSYM
+# RUN: obj2yaml %t/main.dSYM/Contents/Resources/DWARF/main.o -o %t/out.yaml
+# RUN: FileCheck %s < %t/out.yaml
+
+# CHECK: segname: __PSEUDO_PROBE
+# CHECK: Sections:
+# CHECK-NEXT: - sectname: __probe_descs
+# CHECK-NEXT: segname: __PSEUDO_PROBE
+# CHECK: content: ACBD18DB4CC2F85CFFFFFFFF0000000003666F6F37B51D194A7513E4FFFFFFFF000000000362617273FEFFA4B7F6BB68FFFFFFFF000002000362617AFAD58DE7366495DBFFFFFFFF00000100046D61696E
+# CHECK-NEXT: - sectname: __probes
+# CHECK-NEXT: segname: __PSEUDO_PROBE
+# CHECK: content: 37B51D194A7513E402000020A9A7418A5E3E02B5018000FAD58DE7366495DB0200002001411FAE90F5722601800073FEFFA4B7F6BB6802000020DEDF76EC3AE9F50501800037B51D194A7513E402000020DEDF76EC3AE9F505018000ACBD18DB4CC2F85C020000208774C18D504B6E28018000
+
+--- !mach-o
+FileHeader:
+ magic: 0xFEEDFACF
+ cputype: 0x100000C
+ cpusubtype: 0x0
+ filetype: 0x2
+ ncmds: 17
+ sizeofcmds: 976
+ flags: 0x200085
+ reserved: 0x0
+LoadCommands:
+ - cmd: LC_SEGMENT_64
+ cmdsize: 72
+ segname: __PAGEZERO
+ vmaddr: 0
+ vmsize: 4294967296
+ fileoff: 0
+ filesize: 0
+ maxprot: 0
+ initprot: 0
+ nsects: 0
+ flags: 0
+ - cmd: LC_SEGMENT_64
+ cmdsize: 232
+ segname: __TEXT
+ vmaddr: 4294967296
+ vmsize: 16384
+ fileoff: 0
+ filesize: 16384
+ maxprot: 5
+ initprot: 5
+ nsects: 2
+ flags: 0
+ Sections:
+ - sectname: __text
+ segname: __TEXT
+ addr: 0x100000410
+ size: 20
+ offset: 0x410
+ align: 2
+ reloff: 0x0
+ nreloc: 0
+ flags: 0x80000400
+ reserved1: 0x0
+ reserved2: 0x0
+ reserved3: 0x0
+ content: C0035FD6C0035FD6C0035FD600008052C0035FD6
+ - sectname: __unwind_info
+ segname: __TEXT
+ addr: 0x100000424
+ size: 88
+ offset: 0x424
+ align: 2
+ reloff: 0x0
+ nreloc: 0
+ flags: 0x0
+ reserved1: 0x0
+ reserved2: 0x0
+ reserved3: 0x0
+ content: 010000001C000000000000001C000000000000001C00000002000000100400004000000040000000250400000000000040000000000000000000000000000000030000000C00010010000100000000000000000200000000
+ - cmd: LC_SEGMENT_64
+ cmdsize: 232
+ segname: __PSEUDO_PROBE
+ vmaddr: 4294983680
+ vmsize: 16384
+ fileoff: 16384
+ filesize: 16384
+ maxprot: 3
+ initprot: 3
+ nsects: 2
+ flags: 0
+ Sections:
+ - sectname: __probe_descs
+ segname: __PSEUDO_PROBE
+ addr: 0x100004000
+ size: 81
+ offset: 0x4000
+ align: 0
+ reloff: 0x0
+ nreloc: 0
+ flags: 0x0
+ reserved1: 0x0
+ reserved2: 0x0
+ reserved3: 0x0
+ content: ACBD18DB4CC2F85CFFFFFFFF0000000003666F6F37B51D194A7513E4FFFFFFFF000000000362617273FEFFA4B7F6BB68FFFFFFFF000002000362617AFAD58DE7366495DBFFFFFFFF00000100046D61696E
+ - sectname: __probes
+ segname: __PSEUDO_PROBE
+ addr: 0x100004051
+ size: 115
+ offset: 0x4051
+ align: 0
+ reloff: 0x0
+ nreloc: 0
+ flags: 0x0
+ reserved1: 0x0
+ reserved2: 0x0
+ reserved3: 0x0
+ content: 37B51D194A7513E402000020A9A7418A5E3E02B5018000FAD58DE7366495DB0200002001411FAE90F5722601800073FEFFA4B7F6BB6802000020DEDF76EC3AE9F50501800037B51D194A7513E402000020DEDF76EC3AE9F505018000ACBD18DB4CC2F85C020000208774C18D504B6E28018000
+ - cmd: LC_SEGMENT_64
+ cmdsize: 72
+ segname: __LINKEDIT
+ vmaddr: 4295000064
+ vmsize: 16384
+ fileoff: 32768
+ filesize: 680
+ maxprot: 1
+ initprot: 1
+ nsects: 0
+ flags: 0
+ - cmd: LC_DYLD_CHAINED_FIXUPS
+ cmdsize: 16
+ dataoff: 32768
+ datasize: 56
+ - cmd: LC_DYLD_EXPORTS_TRIE
+ cmdsize: 16
+ dataoff: 32824
+ datasize: 80
+ - cmd: LC_SYMTAB
+ cmdsize: 24
+ symoff: 32912
+ nsyms: 5
+ stroff: 32992
+ strsize: 48
+ - cmd: LC_DYSYMTAB
+ cmdsize: 80
+ ilocalsym: 0
+ nlocalsym: 0
+ iextdefsym: 0
+ nextdefsym: 5
+ iundefsym: 5
+ nundefsym: 0
+ tocoff: 0
+ ntoc: 0
+ modtaboff: 0
+ nmodtab: 0
+ extrefsymoff: 0
+ nextrefsyms: 0
+ indirectsymoff: 0
+ nindirectsyms: 0
+ extreloff: 0
+ nextrel: 0
+ locreloff: 0
+ nlocrel: 0
+ - cmd: LC_LOAD_DYLINKER
+ cmdsize: 32
+ name: 12
+ Content: '/usr/lib/dyld'
+ ZeroPadBytes: 7
+ - cmd: LC_UUID
+ cmdsize: 24
+ uuid: B95C17E1-7B8C-362B-B402-C636C09BB979
+ - cmd: LC_BUILD_VERSION
+ cmdsize: 32
+ platform: 1
+ minos: 1703936
+ sdk: 1704448
+ ntools: 1
+ Tools:
+ - tool: 3
+ version: 86573312
+ - cmd: LC_SOURCE_VERSION
+ cmdsize: 16
+ version: 0
+ - cmd: LC_MAIN
+ cmdsize: 24
+ entryoff: 1052
+ stacksize: 0
+ - cmd: LC_LOAD_DYLIB
+ cmdsize: 56
+ dylib:
+ name: 24
+ timestamp: 2
+ current_version: 88866816
+ compatibility_version: 65536
+ Content: '/usr/lib/libSystem.B.dylib'
+ ZeroPadBytes: 6
+ - cmd: LC_FUNCTION_STARTS
+ cmdsize: 16
+ dataoff: 32904
+ datasize: 8
+ - cmd: LC_DATA_IN_CODE
+ cmdsize: 16
+ dataoff: 32912
+ datasize: 0
+ - cmd: LC_CODE_SIGNATURE
+ cmdsize: 16
+ dataoff: 33040
+ datasize: 408
+LinkEditData:
+ ExportTrie:
+ TerminalSize: 0
+ NodeOffset: 0
+ Name: ''
+ Flags: 0x0
+ Address: 0x0
+ Other: 0x0
+ ImportName: ''
+ Children:
+ - TerminalSize: 0
+ NodeOffset: 41
+ Name: _
+ Flags: 0x0
+ Address: 0x0
+ Other: 0x0
+ ImportName: ''
+ Children:
+ - TerminalSize: 2
+ NodeOffset: 9
+ Name: _mh_execute_header
+ Flags: 0x0
+ Address: 0x0
+ Other: 0x0
+ ImportName: ''
+ - TerminalSize: 0
+ NodeOffset: 23
+ Name: ba
+ Flags: 0x0
+ Address: 0x0
+ Other: 0x0
+ ImportName: ''
+ Children:
+ - TerminalSize: 3
+ NodeOffset: 13
+ Name: r
+ Flags: 0x0
+ Address: 0x414
+ Other: 0x0
+ ImportName: ''
+ - TerminalSize: 3
+ NodeOffset: 18
+ Name: z
+ Flags: 0x0
+ Address: 0x418
+ Other: 0x0
+ ImportName: ''
+ - TerminalSize: 3
+ NodeOffset: 31
+ Name: foo
+ Flags: 0x0
+ Address: 0x410
+ Other: 0x0
+ ImportName: ''
+ - TerminalSize: 3
+ NodeOffset: 36
+ Name: main
+ Flags: 0x0
+ Address: 0x41C
+ Other: 0x0
+ ImportName: ''
+ NameList:
+ - n_strx: 2
+ n_type: 0xF
+ n_sect: 1
+ n_desc: 16
+ n_value: 4294967296
+ - n_strx: 22
+ n_type: 0xF
+ n_sect: 1
+ n_desc: 0
+ n_value: 4294968340
+ - n_strx: 27
+ n_type: 0xF
+ n_sect: 1
+ n_desc: 0
+ n_value: 4294968344
+ - n_strx: 32
+ n_type: 0xF
+ n_sect: 1
+ n_desc: 0
+ n_value: 4294968336
+ - n_strx: 37
+ n_type: 0xF
+ n_sect: 1
+ n_desc: 0
+ n_value: 4294968348
+ StringTable:
+ - ' '
+ - __mh_execute_header
+ - _bar
+ - _baz
+ - _foo
+ - _main
+ - ''
+ - ''
+ - ''
+ - ''
+ - ''
+ FunctionStarts: [ 0x410, 0x414, 0x418, 0x41C ]
+ ChainedFixups: [ 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x34, 0x0,
+ 0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
+...
+
diff --git a/llvm/tools/dsymutil/MachOUtils.cpp b/llvm/tools/dsymutil/MachOUtils.cpp
index 9b143157ac773..d8b745cf73dbe 100644
--- a/llvm/tools/dsymutil/MachOUtils.cpp
+++ b/llvm/tools/dsymutil/MachOUtils.cpp
@@ -269,13 +269,18 @@ getSection(const object::MachOObjectFile &Obj,
//
// When the eh_frame section is transferred, its offset and size are set resp.
// to \a EHFrameOffset and \a EHFrameSize.
+//
+// When the __PSEUDO_PROBE segment is transferred, its sections' offsets are
+// updated using \a PseudoProbeOffset and \a PseudoProbeSize for __probes, and
+// \a PseudoProbeDescsOffset and \a PseudoProbeDescsSize for __probe_descs
template <typename SegmentTy>
static void transferSegmentAndSections(
const object::MachOObjectFile::LoadCommandInfo &LCI, SegmentTy Segment,
const object::MachOObjectFile &Obj, MachObjectWriter &Writer,
uint64_t LinkeditOffset, uint64_t LinkeditSize, uint64_t EHFrameOffset,
- uint64_t EHFrameSize, uint64_t DwarfSegmentSize, uint64_t &GapForDwarf,
- uint64_t &EndAddress) {
+ uint64_t EHFrameSize, uint64_t PseudoProbeOffset, uint64_t PseudoProbeSize,
+ uint64_t PseudoProbeProbesOffset, uint64_t PseudoProbeDescsOffset,
+ uint64_t DwarfSegmentSize, uint64_t &GapForDwarf, uint64_t &EndAddress) {
if (StringRef("__DWARF") == Segment.segname)
return;
@@ -287,6 +292,10 @@ static void transferSegmentAndSections(
Segment.filesize = LinkeditSize;
// Resize vmsize by rounding to the page size.
Segment.vmsize = alignTo(LinkeditSize, 0x1000);
+ } else if (StringRef("__PSEUDO_PROBE") == Segment.segname &&
+ PseudoProbeSize > 0) {
+ Segment.fileoff = PseudoProbeOffset;
+ Segment.filesize = PseudoProbeSize;
} else {
Segment.fileoff = Segment.filesize = 0;
}
@@ -312,6 +321,14 @@ static void transferSegmentAndSections(
if (StringRef("__eh_frame") == Sect.sectname) {
Sect.offset = EHFrameOffset;
Sect.reloff = Sect.nreloc = 0;
+ } else if (StringRef("__probes") == Sect.sectname &&
+ PseudoProbeProbesOffset > 0) {
+ Sect.offset = PseudoProbeProbesOffset;
+ Sect.reloff = Sect.nreloc = 0;
+ } else if (StringRef("__probe_descs") == Sect.sectname &&
+ PseudoProbeDescsOffset > 0) {
+ Sect.offset = PseudoProbeDescsOffset;
+ Sect.reloff = Sect.nreloc = 0;
} else {
Sect.offset = Sect.reloff = Sect.nreloc = 0;
}
@@ -463,6 +480,10 @@ bool generateDsymCompanion(
// If we have a valid eh_frame to copy, do it.
uint64_t EHFrameSize = 0;
StringRef EHFrameData;
+ StringRef PseudoProbeProbesData;
+ uint64_t PseudoProbeProbesSize = 0;
+ StringRef PseudoProbeDescsData;
+ uint64_t PseudoProbeDescsSize = 0;
for (const object::SectionRef &Section : InputBinary.sections()) {
Expected<StringRef> NameOrErr = Section.getName();
if (!NameOrErr) {
@@ -478,8 +499,23 @@ bool generateDsymCompanion(
} else {
consumeError(ContentsOrErr.takeError());
}
+ } else if (SectionName == "probes") {
+ if (Expected<StringRef> ContentsOrErr = Section.getContents()) {
+ PseudoProbeProbesData = *ContentsOrErr;
+ PseudoProbeProbesSize = Section.getSize();
+ } else {
+ consumeError(ContentsOrErr.takeError());
+ }
+ } else if (SectionName == "probe_descs") {
+ if (Expected<StringRef> ContentsOrErr = Section.getContents()) {
+ PseudoProbeDescsData = *ContentsOrErr;
+ PseudoProbeDescsSize = Section.getSize();
+ } else {
+ consumeError(ContentsOrErr.takeError());
+ }
}
}
+ uint64_t PseudoProbeSize = PseudoProbeProbesSize + PseudoProbeDescsSize;
unsigned HeaderSize =
Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
@@ -561,8 +597,16 @@ bool generateDsymCompanion(
uint64_t EHFrameStart = StringStart + NewStringsSize;
EHFrameStart = alignTo(EHFrameStart, 0x1000);
- uint64_t DwarfSegmentStart = EHFrameStart + EHFrameSize;
- DwarfSegmentStart = alignTo(DwarfSegmentStart, 0x1000);
+ // Place pseudo probe data after the EH frame
+ uint64_t PseudoProbeStart = PseudoProbeSize > 0
+ ? alignTo(EHFrameStart + EHFrameSize, 0x1000)
+ : EHFrameStart + EHFrameSize;
+
+ uint64_t PseudoProbeProbesStart = PseudoProbeStart;
+ uint64_t PseudoProbeDescsStart = PseudoProbeStart + PseudoProbeProbesSize;
+
+ uint64_t DwarfSegmentStart =
+ alignTo(PseudoProbeStart + PseudoProbeSize, 0x1000);
// Write the load commands for the segments and sections we 'import' from
// the original binary.
@@ -573,12 +617,16 @@ bool generateDsymCompanion(
transferSegmentAndSections(
LCI, InputBinary.getSegmentLoadCommand(LCI), InputBinary, Writer,
SymtabStart, StringStart + NewStringsSize - SymtabStart, EHFrameStart,
- EHFrameSize, DwarfSegmentSize, GapForDwarf, EndAddress);
+ EHFrameSize, PseudoProbeStart, PseudoProbeSize,
+ PseudoProbeProbesStart, PseudoProbeDescsStart, DwarfSegmentSize,
+ GapForDwarf, EndAddress);
else if (LCI.C.cmd == MachO::LC_SEGMENT_64)
transferSegmentAndSections(
LCI, InputBinary.getSegment64LoadCommand(LCI), InputBinary, Writer,
SymtabStart, StringStart + NewStringsSize - SymtabStart, EHFrameStart,
- EHFrameSize, DwarfSegmentSize, GapForDwarf, EndAddress);
+ EHFrameSize, PseudoProbeStart, PseudoProbeSize,
+ PseudoProbeProbesStart, PseudoProbeDescsStart, DwarfSegmentSize,
+ GapForDwarf, EndAddress);
}
uint64_t DwarfVMAddr = alignTo(EndAddress, 0x1000);
@@ -632,8 +680,17 @@ bool generateDsymCompanion(
OutFile << EHFrameData;
assert(OutFile.tell() == EHFrameStart + EHFrameSize);
+ // Transfer pseudo probe.
+ if (PseudoProbeSize > 0) {
+ OutFile.write_zeros(PseudoProbeStart - (EHFrameStart + EHFrameSize));
+ assert(OutFile.tell() == PseudoProbeStart);
+ OutFile << PseudoProbeProbesData;
+ OutFile << PseudoProbeDescsData;
+ assert(OutFile.tell() == PseudoProbeStart + PseudoProbeSize);
+ }
+
// Pad till the Dwarf segment start.
- OutFile.write_zeros(DwarfSegmentStart - (EHFrameStart + EHFrameSize));
+ OutFile.write_zeros(DwarfSegmentStart - OutFile.tell());
assert(OutFile.tell() == DwarfSegmentStart);
// Emit the Dwarf sections contents.
>From ad3df6d6c3668874d2c4691664038cffe9a766d5 Mon Sep 17 00:00:00 2001
From: Henry Jiang <henry_jiang2 at apple.com>
Date: Fri, 3 Apr 2026 16:39:40 -0700
Subject: [PATCH 2/2] updated comments
---
llvm/tools/dsymutil/MachOUtils.cpp | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/llvm/tools/dsymutil/MachOUtils.cpp b/llvm/tools/dsymutil/MachOUtils.cpp
index d8b745cf73dbe..a6dd3afa223e7 100644
--- a/llvm/tools/dsymutil/MachOUtils.cpp
+++ b/llvm/tools/dsymutil/MachOUtils.cpp
@@ -270,9 +270,10 @@ getSection(const object::MachOObjectFile &Obj,
// When the eh_frame section is transferred, its offset and size are set resp.
// to \a EHFrameOffset and \a EHFrameSize.
//
-// When the __PSEUDO_PROBE segment is transferred, its sections' offsets are
-// updated using \a PseudoProbeOffset and \a PseudoProbeSize for __probes, and
-// \a PseudoProbeDescsOffset and \a PseudoProbeDescsSize for __probe_descs
+// When the __PSEUDO_PROBE segment is transferred, its offset and size are set
+// resp. to \a PseudoProbeOffset and \a PseudoProbeSize, and its sections'
+// offsets are updated using \a PseudoProbeProbesOffset for __probes and
+// \a PseudoProbeDescsOffset for __probe_descs.
template <typename SegmentTy>
static void transferSegmentAndSections(
const object::MachOObjectFile::LoadCommandInfo &LCI, SegmentTy Segment,
@@ -597,7 +598,7 @@ bool generateDsymCompanion(
uint64_t EHFrameStart = StringStart + NewStringsSize;
EHFrameStart = alignTo(EHFrameStart, 0x1000);
- // Place pseudo probe data after the EH frame
+ // Place pseudo probe data after the EH frame.
uint64_t PseudoProbeStart = PseudoProbeSize > 0
? alignTo(EHFrameStart + EHFrameSize, 0x1000)
: EHFrameStart + EHFrameSize;
More information about the llvm-commits
mailing list