1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
// SPDX-License-Identifier: LGPL-2.1-or-later
/**
* @file
*
* Call frame information
*
* See @ref CallFrameInformation.
*/
#ifndef DRGN_CFI_H
#define DRGN_CFI_H
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
/**
* @ingroup Internals
*
* @defgroup CallFrameInformation Call frame information
*
* Call frame information for stack unwinding.
*
* This defines a generic representation for Call Frame Information (CFI), which
* describes how to determine the Canonical Frame Address (CFA) and previous
* register values while unwinding a stack trace.
*
* @{
*/
/**
* Numeric identifier for a register.
*
* These are only unique within an architecture, and they are not necessarily
* the same as the register numbers used by DWARF.
*/
typedef uint16_t drgn_register_number;
/** Maximum valid register number. */
#define DRGN_MAX_REGISTER_NUMBER ((drgn_register_number)-3)
/** Placeholder number for unknown register. */
#define DRGN_REGISTER_NUMBER_UNKNOWN ((drgn_register_number)-1)
/** Kinds of CFI rules. */
enum drgn_cfi_rule_kind {
/** Register value in the caller is not known. */
DRGN_CFI_RULE_UNDEFINED,
/**
* Register value in the caller is stored at the CFA in the current
* frame plus an offset: `*(cfa + offset)`.
*/
DRGN_CFI_RULE_AT_CFA_PLUS_OFFSET,
/**
* Register value in the caller is the CFA in the current frame plus an
* offset: `cfa + offset`.
*/
DRGN_CFI_RULE_CFA_PLUS_OFFSET,
/**
* Register value in the caller is stored at the value of a register in
* the current frame plus an offset: `*(reg + offset)`.
*/
DRGN_CFI_RULE_AT_REGISTER_PLUS_OFFSET,
/**
* Register value in the caller is an offset plus the value stored at
* the value of a register in the current frame: `(*reg) + offset`.
*/
DRGN_CFI_RULE_AT_REGISTER_ADD_OFFSET,
/**
* Register value in the caller is the value of a register in the
* current frame plus an offset: `reg + offset`.
*
* Note that this can also be used to represent DWARF's "same value"
* rule by using the same register with an offset of 0.
*/
DRGN_CFI_RULE_REGISTER_PLUS_OFFSET,
/**
* Register value in the caller is stored at the address given by a
* DWARF expression.
*/
DRGN_CFI_RULE_AT_DWARF_EXPRESSION,
/** Register value in the caller is given by a DWARF expression. */
DRGN_CFI_RULE_DWARF_EXPRESSION,
/** Register value in the caller has a constant value. */
DRGN_CFI_RULE_CONSTANT,
} __attribute__((__packed__));
/** Rule for determining a single register value or CFA. */
struct drgn_cfi_rule {
/** Rule kind. */
enum drgn_cfi_rule_kind kind;
/**
* Whether to push the CFA before evaluating the DWARF
* expression for @ref DRGN_CFI_RULE_AT_DWARF_EXPRESSION or @ref
* DRGN_CFI_RULE_DWARF_EXPRESSION.
*/
bool push_cfa;
/** Register number for @ref DRGN_CFI_RULE_REGISTER_PLUS_OFFSET. */
drgn_register_number regno;
union {
/**
* Offset for @ref DRGN_CFI_RULE_AT_CFA_PLUS_OFFSET, @ref
* DRGN_CFI_RULE_CFA_PLUS_OFFSET, @ref
* DRGN_CFI_RULE_AT_REGISTER_PLUS_OFFSET, @ref
* DRGN_CFI_RULE_AT_REGISTER_ADD_OFFSET, and @ref
* DRGN_CFI_RULE_REGISTER_PLUS_OFFSET.
*/
int64_t offset;
/** Constant for @ref DRGN_CFI_RULE_CONSTANT. */
uint64_t constant;
/**
* DWARF expression for @ref DRGN_CFI_RULE_AT_DWARF_EXPRESSION
* and @ref DRGN_CFI_RULE_DWARF_EXPRESSION.
*/
struct {
/** Pointer to expression data. */
const char *expr;
/** Size of @ref drgn_cfi_rule::expr. */
size_t expr_size;
};
};
};
/**
* "Row" of call frame information, i.e., how to get the CFA and the previous
* value of each register at a single location in the program.
*
* A row may be allocated statically or on the heap. Static rows are created
* with @ref DRGN_CFI_ROW(). The first time a static row would be modified (with
* @ref drgn_cfi_row_copy(), @ref drgn_cfi_row_set_cfa(), or @ref
* drgn_cfi_row_set_register()), it is first copied to the heap. Subsequent
* modifications reuse the heap allocation, growing it if necessary. The
* allocation must be freed with @ref drgn_cfi_row_destroy().
*/
struct drgn_cfi_row {
/**
* Number of rules allocated, including the CFA rule.
*
* If the row is statically allocated, then this is zero, even if
* `num_regs` is non-zero. Otherwise, it is at least `num_regs + 1`.
*/
uint16_t allocated_rules;
/** Number of initialized elements in `reg_rules`. */
uint16_t num_regs;
/** Canonical Frame Address rule. */
struct drgn_cfi_rule cfa_rule;
/** Register rules. */
struct drgn_cfi_rule reg_rules[];
};
/**
* Initializer for a static @ref drgn_cfi_row given initializers for @ref
* drgn_cfi_row::reg_rules.
*/
#define DRGN_CFI_ROW(...) { \
.num_regs = (sizeof((struct drgn_cfi_rule []){ __VA_ARGS__ }) \
/ sizeof(struct drgn_cfi_rule)), \
.reg_rules = { __VA_ARGS__ }, \
}
/**
* Initializer for a rule in @ref drgn_cfi_row::reg_rules specifying that the
* register with the given number has the same value in the caller.
*/
#define DRGN_CFI_SAME_VALUE_INIT(number) \
[(number)] = { \
.kind = DRGN_CFI_RULE_REGISTER_PLUS_OFFSET, \
.regno = (number), \
}
extern const struct drgn_cfi_row drgn_empty_cfi_row_impl;
/**
* Static @ref drgn_cfi_row with all rules set to @ref DRGN_CFI_RULE_UNDEFINED.
*/
#define drgn_empty_cfi_row ((struct drgn_cfi_row *)&drgn_empty_cfi_row_impl)
/** Free a @ref drgn_cfi_row. */
static inline void drgn_cfi_row_destroy(struct drgn_cfi_row *row)
{
if (row->allocated_rules > 0)
free(row);
}
/** Copy the rules from one @ref drgn_cfi_row to another. */
bool drgn_cfi_row_copy(struct drgn_cfi_row **dst,
const struct drgn_cfi_row *src);
/**
* Get the rule for the Canonical Frame Address in a @ref drgn_cfi_row.
*
* @param[out] ret Returned rule.
*/
static inline void drgn_cfi_row_get_cfa(const struct drgn_cfi_row *row,
struct drgn_cfi_rule *ret)
{
*ret = row->cfa_rule;
}
/**
* Set the rule for the Canonical Frame Address in a @ref drgn_cfi_row.
*
* @param[in] rule Rule to set to.
* @return @c true on success, @c false on failure to allocate memory.
*/
bool drgn_cfi_row_set_cfa(struct drgn_cfi_row **row,
const struct drgn_cfi_rule *rule);
/**
* Get the rule for a register in a @ref drgn_cfi_row.
*
* @param[in] regno Register number.
*/
void drgn_cfi_row_get_register(const struct drgn_cfi_row *row,
drgn_register_number regno,
struct drgn_cfi_rule *ret);
/**
* Set the rule for a register in a @ref drgn_cfi_row.
*
* @param[in] regno Register number.
* @param[in] rule Rule to set to.
* @return @c true on success, @c false on failure to allocate memory.
*/
bool drgn_cfi_row_set_register(struct drgn_cfi_row **row,
drgn_register_number regno,
const struct drgn_cfi_rule *rule);
/** @} */
#endif /* DRGN_CFI_H */
|