-
Notifications
You must be signed in to change notification settings - Fork 11
Parse __eh_frame DWARF info on macOS for per-PC frame descriptions #430
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
2a6e628
0bc1dbc
9aba288
7a1c101
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,5 +1,6 @@ | ||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||
| * Copyright 2021 Andrei Pangin | ||||||||||||||||||||||||||
| * Copyright 2026, Datadog, Inc. | ||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||||||||||||||||||||||||||
| * you may not use this file except in compliance with the License. | ||||||||||||||||||||||||||
|
|
@@ -92,8 +93,7 @@ FrameDesc FrameDesc::default_frame = {0, DW_REG_FP | LINKED_FRAME_SIZE << 8, | |||||||||||||||||||||||||
| FrameDesc FrameDesc::default_clang_frame = {0, DW_REG_FP | LINKED_FRAME_CLANG_SIZE << 8, -LINKED_FRAME_CLANG_SIZE, -LINKED_FRAME_CLANG_SIZE + DW_STACK_SLOT}; | ||||||||||||||||||||||||||
| FrameDesc FrameDesc::no_dwarf_frame = {0, DW_REG_INVALID, DW_REG_INVALID, DW_REG_INVALID}; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| DwarfParser::DwarfParser(const char *name, const char *image_base, | ||||||||||||||||||||||||||
| const char *eh_frame_hdr) { | ||||||||||||||||||||||||||
| void DwarfParser::init(const char *name, const char *image_base) { | ||||||||||||||||||||||||||
| _name = name; | ||||||||||||||||||||||||||
| _image_base = image_base; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
@@ -105,10 +105,21 @@ DwarfParser::DwarfParser(const char *name, const char *image_base, | |||||||||||||||||||||||||
| _code_align = sizeof(instruction_t); | ||||||||||||||||||||||||||
| _data_align = -(int)sizeof(void *); | ||||||||||||||||||||||||||
| _linked_frame_size = -1; | ||||||||||||||||||||||||||
| _has_z_augmentation = false; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| DwarfParser::DwarfParser(const char *name, const char *image_base, | ||||||||||||||||||||||||||
| const char *eh_frame_hdr) { | ||||||||||||||||||||||||||
| init(name, image_base); | ||||||||||||||||||||||||||
| parse(eh_frame_hdr); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| DwarfParser::DwarfParser(const char *name, const char *image_base, | ||||||||||||||||||||||||||
| const char *eh_frame, size_t eh_frame_size) { | ||||||||||||||||||||||||||
| init(name, image_base); | ||||||||||||||||||||||||||
| parseEhFrame(eh_frame, eh_frame_size); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| static constexpr u8 omit_sign_bit(u8 value) { | ||||||||||||||||||||||||||
| // each signed flag = unsigned equivalent | 0x80 | ||||||||||||||||||||||||||
| return value & 0xf7; | ||||||||||||||||||||||||||
|
|
@@ -144,6 +155,93 @@ void DwarfParser::parse(const char *eh_frame_hdr) { | |||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Parse raw .eh_frame (or __eh_frame on macOS) without a binary-search index. | ||||||||||||||||||||||||||
| // Records are CIE/FDE sequences laid out linearly; terminated by a 4-byte zero or EOF. | ||||||||||||||||||||||||||
| void DwarfParser::parseEhFrame(const char *eh_frame, size_t size) { | ||||||||||||||||||||||||||
| if (eh_frame == NULL || size < 4) { | ||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| const char *section_end = eh_frame + size; | ||||||||||||||||||||||||||
| _ptr = eh_frame; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| while (_ptr + 4 <= section_end) { | ||||||||||||||||||||||||||
| const char *record_start = _ptr; | ||||||||||||||||||||||||||
| u32 length = get32(); | ||||||||||||||||||||||||||
| if (length == 0) { | ||||||||||||||||||||||||||
| break; // terminator | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| if (length == 0xffffffff) { | ||||||||||||||||||||||||||
| break; // 64-bit DWARF not supported | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (length > (size_t)(section_end - record_start) - 4) { | ||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| const char *record_end = record_start + 4 + length; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| u32 cie_id = get32(); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (cie_id == 0) { | ||||||||||||||||||||||||||
| // CIE: update code and data alignment factors. | ||||||||||||||||||||||||||
| // Layout after cie_id: [1-byte version][augmentation string \0][code_align LEB][data_align SLEB] | ||||||||||||||||||||||||||
| // [return_address_register][augmentation data (if 'z')]... | ||||||||||||||||||||||||||
| // return_address_register and everything after data_align are not consumed; _ptr = record_end | ||||||||||||||||||||||||||
| // at the bottom of the loop skips them. | ||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||
| // _has_z_augmentation is overwritten by every CIE encountered. The DWARF spec allows | ||||||||||||||||||||||||||
| // multiple CIEs with different augmentation strings in a single .eh_frame section, so | ||||||||||||||||||||||||||
| // strictly speaking each FDE should resolve its own CIE via the backward cie_id offset. | ||||||||||||||||||||||||||
| // We intentionally skip that: macOS binaries compiled by clang typically emit a single CIE | ||||||||||||||||||||||||||
| // per module, and this parser is only called for macOS __eh_frame sections. Multi-CIE | ||||||||||||||||||||||||||
| // binaries are not produced by the toolchains we target here. | ||||||||||||||||||||||||||
| if (_ptr >= record_end) { | ||||||||||||||||||||||||||
| _ptr = record_end; | ||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| _ptr++; // skip version | ||||||||||||||||||||||||||
|
Comment on lines
+196
to
+201
|
||||||||||||||||||||||||||
| // binaries are not produced by the toolchains we target here. | |
| _ptr++; // skip version | |
| // binaries are not produced by the toolchains we target here. | |
| if (_ptr >= record_end) { | |
| _ptr = record_end; | |
| continue; | |
| } | |
| _ptr++; // skip version | |
| if (_ptr >= record_end) { | |
| _ptr = record_end; | |
| continue; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Added if (_ptr >= record_end) { _ptr = record_end; continue; } guards before the version skip and before the augmentation string read.
Copilot
AI
Apr 7, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the FDE path, _ptr += getLeb() assumes the augmentation-data-length LEB is fully readable. If the record ends mid-LEB, getLeb() can read past record_end (same issue as in CIE parsing). Use a bounded LEB decoder (or at least check _ptr < record_end per-byte) and fail the record if the LEB doesn’t complete within record_end.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. The FDE augmentation-data-length read now uses getLeb(record_end) (the bounded overload).
Uh oh!
There was an error while loading. Please reload this page.