drgn/libdrgn/cfi.h
Omar Sandoval 87b7292aa5 Relicense drgn from GPLv3+ to LGPLv2.1+
drgn is currently licensed as GPLv3+. Part of the long term vision for
drgn is that other projects can use it as a library providing
programmatic interfaces for debugger functionality. A more permissive
license is better suited to this goal. We decided on LGPLv2.1+ as a good
balance between software freedom and permissiveness.

All contributors not employed by Meta were contacted via email and
consented to the license change. The only exception was the author of
commit c4fbf7e589 ("libdrgn: fix for compilation error"), who did not
respond. That commit reverted a single line of code to one originally
written by me in commit 640b1c011d ("libdrgn: embed DWARF index in
DWARF info cache").

Signed-off-by: Omar Sandoval <osandov@osandov.com>
2022-11-01 17:05:16 -07:00

232 lines
6.7 KiB
C

// 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 */