Skip to content

Commit 50024e0

Browse files
jbachorikclaudekaahos
authored
Detect per-library aarch64 frame layout from DWARF (#426)
* Detect per-library aarch64 frame layout from DWARF Wire up default_clang_frame which was defined but never used. DwarfParser now detects the linked frame size by observing the first FP-based CFA entry with a non-zero offset during DWARF parsing (both GCC and Clang emit cfa_off=LINKED_FRAME_CLANG_SIZE in function bodies on aarch64). CodeCache stores the detected default per library and uses it when DWARF doesn't cover a PC. For unknown libraries (cc==NULL) on Apple/aarch64, fall back to default_clang_frame since Apple ships Clang-only code. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> * Update ddprof-lib/src/main/cpp/dwarf.h Co-authored-by: Paul Fournillon <112829622+kaahos@users.noreply.github.com> * Set clang frame layout as default for macOS aarch64 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Paul Fournillon <112829622+kaahos@users.noreply.github.com>
1 parent 93a3208 commit 50024e0

7 files changed

Lines changed: 52 additions & 11 deletions

File tree

ddprof-lib/src/main/cpp/codeCache.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ CodeCache::CodeCache(const char *name, short lib_index,
6363

6464
_dwarf_table = NULL;
6565
_dwarf_table_length = 0;
66+
_default_frame = &FrameDesc::default_frame;
6667

6768
_capacity = INITIAL_CODE_CACHE_CAPACITY;
6869
_count = 0;
@@ -101,6 +102,7 @@ void CodeCache::copyFrom(const CodeCache& other) {
101102
_dwarf_table = new FrameDesc[_dwarf_table_length];
102103
memcpy(_dwarf_table, other._dwarf_table,
103104
_dwarf_table_length * sizeof(FrameDesc));
105+
_default_frame = other._default_frame;
104106

105107
_capacity = other._capacity;
106108
_count = other._count;
@@ -388,16 +390,15 @@ void CodeCache::makeImportsPatchable() {
388390
}
389391
}
390392

391-
void CodeCache::setDwarfTable(FrameDesc *table, int length) {
393+
void CodeCache::setDwarfTable(FrameDesc *table, int length, const FrameDesc &default_frame) {
392394
_dwarf_table = table;
393395
_dwarf_table_length = length;
396+
_default_frame = &default_frame;
394397
}
395398

396399
FrameDesc CodeCache::findFrameDesc(const void *pc) {
397400
if (_dwarf_table == NULL || _dwarf_table_length == 0) {
398-
// No DWARF data available - use default frame pointer unwinding
399-
// This handles OpenJ9 and other VMs that don't provide DWARF info
400-
return FrameDesc::default_frame;
401+
return *_default_frame;
401402
}
402403

403404
u32 target_loc = (const char *)pc - _text_base;
@@ -420,7 +421,7 @@ FrameDesc CodeCache::findFrameDesc(const void *pc) {
420421
} else if (target_loc - _plt_offset < _plt_size) {
421422
return FrameDesc::empty_frame;
422423
} else {
423-
return FrameDesc::default_frame;
424+
return *_default_frame;
424425
}
425426
}
426427

ddprof-lib/src/main/cpp/codeCache.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
#include "common.h"
1010
#include "counters.h"
11+
#include "dwarf.h"
1112
#include "utils.h"
1213

1314
#include <jvmti.h>
@@ -116,8 +117,6 @@ class CodeBlob {
116117
}
117118
};
118119

119-
class FrameDesc;
120-
121120
class CodeCache {
122121
private:
123122
char *_name;
@@ -141,6 +140,7 @@ class CodeCache {
141140

142141
FrameDesc *_dwarf_table;
143142
int _dwarf_table_length;
143+
const FrameDesc *_default_frame;
144144

145145
int _capacity;
146146
int _count;
@@ -241,7 +241,7 @@ class CodeCache {
241241
void findSymbolsByPrefix(std::vector<const char *> &prefixes,
242242
std::vector<const void *> &symbols);
243243

244-
void setDwarfTable(FrameDesc *table, int length);
244+
void setDwarfTable(FrameDesc *table, int length, const FrameDesc &default_frame = FrameDesc::default_frame);
245245
FrameDesc findFrameDesc(const void *pc);
246246

247247
long long memoryUsage() {

ddprof-lib/src/main/cpp/dwarf.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ DwarfParser::DwarfParser(const char *name, const char *image_base,
104104

105105
_code_align = sizeof(instruction_t);
106106
_data_align = -(int)sizeof(void *);
107+
_linked_frame_size = -1;
107108

108109
parse(eh_frame_hdr);
109110
}
@@ -411,6 +412,14 @@ void DwarfParser::addRecord(u32 loc, u32 cfa_reg, int cfa_off, int fp_off,
411412
// cfa_reg and cfa_off can be encoded to a single 32 bit value, considering the existing and supported systems
412413
u32 cfa = static_cast<u32>(cfa_off) << 8 | static_cast<u32>(cfa_reg & 0xff);
413414

415+
// Detect the linked frame size from the first FP-based entry with a non-zero offset.
416+
// Both GCC and Clang emit DW_REG_FP with cfa_off = LINKED_FRAME_CLANG_SIZE in function
417+
// bodies after the prologue completes. Terminal records use cfa_off = 0 (LINKED_FRAME_SIZE
418+
// on aarch64) and do not influence detection.
419+
if (_linked_frame_size < 0 && cfa_reg == DW_REG_FP && cfa_off > 0) {
420+
_linked_frame_size = cfa_off;
421+
}
422+
414423
if (_prev == NULL || (_prev->loc == loc && --_count >= 0) ||
415424
_prev->cfa != cfa || _prev->fp_off != fp_off || _prev->pc_off != pc_off) {
416425
_prev = addRecordRaw(loc, cfa, fp_off, pc_off);

ddprof-lib/src/main/cpp/dwarf.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,17 @@ struct FrameDesc {
8383
static FrameDesc default_clang_frame;
8484
static FrameDesc no_dwarf_frame;
8585

86+
// Best-guess fallback frame layout when a PC doesn't map to any known library.
87+
// Per-library detection overrides this: on macOS via __eh_frame section presence,
88+
// on Linux via DwarfParser::detectedDefaultFrame().
89+
static const FrameDesc& fallback_default_frame() {
90+
#if defined(__APPLE__) && defined(__aarch64__)
91+
return default_clang_frame;
92+
#else
93+
return default_frame;
94+
#endif
95+
}
96+
8697
static int comparator(const void* p1, const void* p2) {
8798
FrameDesc* fd1 = (FrameDesc*)p1;
8899
FrameDesc* fd2 = (FrameDesc*)p2;
@@ -104,6 +115,7 @@ class DwarfParser {
104115

105116
u32 _code_align;
106117
int _data_align;
118+
int _linked_frame_size; // detected from FP-based DWARF entries; -1 = undetected
107119

108120
const char* add(size_t size) {
109121
const char* ptr = _ptr;
@@ -185,6 +197,13 @@ class DwarfParser {
185197
int count() const {
186198
return _count;
187199
}
200+
201+
const FrameDesc& detectedDefaultFrame() const {
202+
if (_linked_frame_size == LINKED_FRAME_CLANG_SIZE && LINKED_FRAME_CLANG_SIZE != LINKED_FRAME_SIZE) {
203+
return FrameDesc::default_clang_frame;
204+
}
205+
return FrameDesc::default_frame;
206+
}
188207
};
189208

190209
#endif // _DWARF_H

ddprof-lib/src/main/cpp/stackWalker.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ int StackWalker::walkDwarf(void* ucontext, const void** callchain, int max_depth
158158

159159
uintptr_t prev_sp = sp;
160160
CodeCache* cc = profiler->findLibraryByAddress(pc);
161-
FrameDesc f = cc != NULL ? cc->findFrameDesc(pc) : FrameDesc::default_frame;
161+
FrameDesc f = cc != NULL ? cc->findFrameDesc(pc) : FrameDesc::fallback_default_frame();
162162

163163
u8 cfa_reg = (u8)f.cfa;
164164
int cfa_off = f.cfa >> 8;
@@ -694,7 +694,7 @@ __attribute__((no_sanitize("address"))) int StackWalker::walkVM(void* ucontext,
694694
dwarf_unwind:
695695
uintptr_t prev_sp = sp;
696696
CodeCache* cc = profiler->findLibraryByAddress(pc);
697-
FrameDesc f = cc != NULL ? cc->findFrameDesc(pc) : FrameDesc::default_frame;
697+
FrameDesc f = cc != NULL ? cc->findFrameDesc(pc) : FrameDesc::fallback_default_frame();
698698

699699
u8 cfa_reg = (u8)f.cfa;
700700
int cfa_off = f.cfa >> 8;

ddprof-lib/src/main/cpp/symbols_linux.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -604,8 +604,11 @@ void ElfParser::parseDwarfInfo() {
604604
ElfProgramHeader* eh_frame_hdr = findProgramHeader(PT_GNU_EH_FRAME);
605605
if (eh_frame_hdr != NULL) {
606606
if (eh_frame_hdr->p_vaddr != 0) {
607+
// Parse per-PC frame descriptions and detect per-library default frame layout.
608+
// On aarch64 this distinguishes GCC (LINKED_FRAME_SIZE=0) from clang
609+
// (LINKED_FRAME_CLANG_SIZE=16) conventions for each shared library.
607610
DwarfParser dwarf(_cc->name(), _base, at(eh_frame_hdr));
608-
_cc->setDwarfTable(dwarf.table(), dwarf.count());
611+
_cc->setDwarfTable(dwarf.table(), dwarf.count(), dwarf.detectedDefaultFrame());
609612
} else if (strcmp(_cc->name(), "[vdso]") == 0) {
610613
FrameDesc* table = (FrameDesc*)malloc(sizeof(FrameDesc));
611614
*table = FrameDesc::empty_frame;

ddprof-lib/src/main/cpp/symbols_macos.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <mach-o/loader.h>
1313
#include <mach-o/nlist.h>
1414
#include "symbols.h"
15+
#include "dwarf.h"
1516
#include "log.h"
1617

1718
UnloadProtection::UnloadProtection(const CodeCache *cc) {
@@ -138,13 +139,15 @@ class MachOParser {
138139
const symtab_command* symtab = NULL;
139140
const dysymtab_command* dysymtab = NULL;
140141
const section_64* stubs_section = NULL;
142+
bool has_eh_frame = false;
141143

142144
for (uint32_t i = 0; i < header->ncmds; i++) {
143145
if (lc->cmd == LC_SEGMENT_64) {
144146
const segment_command_64* sc = (const segment_command_64*)lc;
145147
if (strcmp(sc->segname, "__TEXT") == 0) {
146148
_cc->updateBounds(_image_base, add(_image_base, sc->vmsize));
147149
stubs_section = findSection(sc, "__stubs");
150+
has_eh_frame = findSection(sc, "__eh_frame") != NULL;
148151
} else if (strcmp(sc->segname, "__LINKEDIT") == 0) {
149152
link_base = _vmaddr_slide + sc->vmaddr - sc->fileoff;
150153
} else if (strcmp(sc->segname, "__DATA") == 0 || strcmp(sc->segname, "__DATA_CONST") == 0) {
@@ -168,6 +171,12 @@ class MachOParser {
168171
}
169172
}
170173

174+
// GCC emits __eh_frame (DWARF CFI); clang emits __unwind_info (compact unwind).
175+
// On aarch64, GCC and clang use different frame layouts, so detecting the
176+
// compiler matters. On x86_64 both use the same layout (no-op distinction).
177+
const FrameDesc& frame = has_eh_frame ? FrameDesc::default_frame : FrameDesc::fallback_default_frame();
178+
_cc->setDwarfTable(NULL, 0, frame);
179+
171180
return true;
172181
}
173182
};

0 commit comments

Comments
 (0)