new file mode 100644
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * dwarf.h - DWARF data structures used by the unwinder.
+ *
+ * Author: Madhavan T. Venkataraman (madvenka@linux.microsoft.com)
+ *
+ * Copyright (c) 2022 Microsoft Corporation
+ */
+
+#ifndef _LINUX_DWARF_H
+#define _LINUX_DWARF_H
+
+#include <linux/types.h>
+
+/*
+ * objtool generates two special sections that contain DWARF information that
+ * will be used by the reliable unwinder to validate the frame pointer in every
+ * frame:
+ *
+ * .dwarf_rules:
+ * This contains an array of struct dwarf_rule. Each rule contains the
+ * size of a code range. In addition, a rule contains the offsets that
+ * must be used to compute the frame pointer at any of the instructions
+ * within the code range. The computation is:
+ *
+ * CFA = %sp + sp_offset
+ * FP = CFA + fp_offset
+ *
+ * where %sp is the stack pointer at the instruction address and FP is
+ * the frame pointer.
+ *
+ * .dwarf_pcs:
+ * This contains an array of starting PCs, one for each rule.
+ */
+struct dwarf_rule {
+ unsigned int size:30;
+ unsigned int sp_saved:1;
+ unsigned int fp_saved:1;
+ short sp_offset;
+ short fp_offset;
+};
+
+#endif /* _LINUX_DWARF_H */
new file mode 100644
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * dwarf.h - DWARF data structures used by the unwinder.
+ *
+ * Author: Madhavan T. Venkataraman (madvenka@linux.microsoft.com)
+ *
+ * Copyright (c) 2022 Microsoft Corporation
+ */
+
+#ifndef _LINUX_DWARF_H
+#define _LINUX_DWARF_H
+
+#include <linux/types.h>
+
+/*
+ * objtool generates two special sections that contain DWARF information that
+ * will be used by the reliable unwinder to validate the frame pointer in every
+ * frame:
+ *
+ * .dwarf_rules:
+ * This contains an array of struct dwarf_rule. Each rule contains the
+ * size of a code range. In addition, a rule contains the offsets that
+ * must be used to compute the frame pointer at any of the instructions
+ * within the code range. The computation is:
+ *
+ * CFA = %sp + sp_offset
+ * FP = CFA + fp_offset
+ *
+ * where %sp is the stack pointer at the instruction address and FP is
+ * the frame pointer.
+ *
+ * .dwarf_pcs:
+ * This contains an array of starting PCs, one for each rule.
+ */
+struct dwarf_rule {
+ unsigned int size:30;
+ unsigned int sp_saved:1;
+ unsigned int fp_saved:1;
+ short sp_offset;
+ short fp_offset;
+};
+
+#endif /* _LINUX_DWARF_H */
@@ -25,6 +25,10 @@ static const char * const dwarf_usage[] = {
* information.
*/
"objtool dwarf generate file",
+ /*
+ * Dump DWARF rules for debugging purposes.
+ */
+ "objtool dwarf dump file",
NULL,
};
@@ -37,6 +41,7 @@ int cmd_dwarf(int argc, const char **argv)
{
const char *object;
struct objtool_file *file;
+ int ret;
argc--; argv++;
if (argc != 2)
@@ -48,8 +53,21 @@ int cmd_dwarf(int argc, const char **argv)
if (!file)
return 1;
- if (!strncmp(argv[0], "gen", 3))
- return dwarf_parse(file);
+ if (!strncmp(argv[0], "gen", 3)) {
+ ret = dwarf_parse(file);
+ if (!ret)
+ ret = dwarf_write(file);
+ if (!ret && file->elf->changed)
+ ret = elf_write(file->elf);
+ return ret;
+ }
+
+ if (!strcmp(argv[0], "dump")) {
+ ret = dwarf_parse(file);
+ if (!ret)
+ dwarf_dump();
+ return ret;
+ }
usage_with_options(dwarf_usage, dwarf_options);
@@ -13,25 +13,192 @@
#include <objtool/dwarf_def.h>
#include <linux/compiler.h>
-/*
- * The following are stubs for now. Later, they will be filled to create
- * DWARF rules that the kernel can use to compute the frame pointer at
- * a given instruction address.
- */
+struct section *dwarf_rules_sec;
+struct section *dwarf_pcs_sec;
+
+static struct fde_entry *cur_entry;
+static int nentries;
+
+static int dwarf_rule_insert(struct fde *fde, unsigned long addr,
+ struct rule *sp_rule, struct rule *fp_rule);
+
void dwarf_rule_start(struct fde *fde)
{
+ fde->head = NULL;
+ fde->tail = NULL;
+ cur_entry = NULL;
}
int dwarf_rule_add(struct fde *fde, unsigned long addr,
- struct rule *sp_rule, struct rule *fp_rule)
+ struct rule *sp_rule, struct rule *fp_rule)
{
- return 0;
+ if (cur_entry) {
+ struct rule *esp_rule = &cur_entry->sp_rule;
+ struct rule *efp_rule = &cur_entry->fp_rule;
+
+ /*
+ * If the rules have not changed, there is nothing to do.
+ */
+ if (esp_rule->offset == sp_rule->offset &&
+ efp_rule->offset == fp_rule->offset &&
+ esp_rule->saved == sp_rule->saved &&
+ efp_rule->saved == fp_rule->saved) {
+ return 0;
+ }
+ /* Close out the current range. */
+ cur_entry->size = addr - cur_entry->addr;
+ }
+ return dwarf_rule_insert(fde, addr, sp_rule, fp_rule);
}
void dwarf_rule_next(struct fde *fde, unsigned long addr)
{
+ if (cur_entry) {
+ /* Close out the current range. */
+ cur_entry->size = addr - cur_entry->addr;
+ cur_entry = NULL;
+ }
}
void dwarf_rule_reset(struct fde *fde)
{
+ struct fde_entry *entry;
+
+ while (fde->head) {
+ entry = fde->head;
+ fde->head = entry->next;
+ free(entry);
+ nentries--;
+ }
+ fde->tail = NULL;
+ cur_entry = NULL;
+}
+
+static int dwarf_rule_insert(struct fde *fde, unsigned long addr,
+ struct rule *sp_rule, struct rule *fp_rule)
+{
+ struct fde_entry *entry;
+
+ entry = dwarf_alloc(sizeof(*entry));
+ if (!entry)
+ return -1;
+
+ /* Add the entry to the FDE list. */
+ if (fde->tail)
+ fde->tail->next = entry;
+ else
+ fde->head = entry;
+ fde->tail = entry;
+ entry->next = NULL;
+
+ /*
+ * Record the starting address of the code range here. The size of
+ * the range will be known only when the next rule comes in. At that
+ * time, we will close out this range.
+ */
+ entry->addr = addr;
+
+ /* Copy the rules. */
+ entry->sp_rule = *sp_rule;
+ entry->fp_rule = *fp_rule;
+
+ cur_entry = entry;
+ nentries++;
+ return 0;
+}
+
+static int dwarf_rule_write(struct elf *elf, struct fde *fde,
+ struct fde_entry *entry, unsigned int index)
+{
+ struct dwarf_rule rule, *drule;
+
+ /*
+ * Encode the SP and FP rules from the entry into a single dwarf_rule
+ * for the kernel's benefit. Copy it into .dwarf_rules.
+ */
+ rule.size = entry->size;
+ rule.sp_saved = entry->sp_rule.saved;
+ rule.fp_saved = entry->fp_rule.saved;
+ rule.sp_offset = entry->sp_rule.offset;
+ rule.fp_offset = entry->fp_rule.offset;
+
+ drule = (struct dwarf_rule *) dwarf_rules_sec->data->d_buf + index;
+ memcpy(drule, &rule, sizeof(rule));
+
+ /* Add relocation information for the code range. */
+ if (elf_add_reloc_to_insn(elf, dwarf_pcs_sec,
+ index * sizeof(unsigned long),
+ R_AARCH64_ABS64,
+ fde->section, entry->addr)) {
+ return -1;
+ }
+ return 0;
+}
+
+int dwarf_write(struct objtool_file *file)
+{
+ struct elf *elf = file->elf;
+ struct fde *fde;
+ struct fde_entry *entry;
+ int index;
+
+ /*
+ * Check if .dwarf_rules already exists. If it doesn't, we will
+ * assume that .dwarf_pcs doesn't exist either.
+ */
+ if (find_section_by_name(elf, ".dwarf_rules")) {
+ WARN("file already has .dwarf_rules section");
+ return -1;
+ }
+
+ /* Create .dwarf_rules. */
+ dwarf_rules_sec = elf_create_section(elf, ".dwarf_rules", 0,
+ sizeof(struct dwarf_rule),
+ nentries);
+ if (!dwarf_rules_sec) {
+ WARN("Unable to create .dwarf_rules");
+ return -1;
+ }
+
+ /* Create .dwarf_pcs. */
+ dwarf_pcs_sec = elf_create_section(elf, ".dwarf_pcs", 0,
+ sizeof(unsigned long), nentries);
+ if (!dwarf_pcs_sec) {
+ WARN("Unable to create .dwarf_pcs");
+ return -1;
+ }
+
+ /* Write DWARF rules to sections. */
+ index = 0;
+ for (fde = fdes; fde != NULL; fde = fde->next) {
+ for (entry = fde->head; entry != NULL; entry = entry->next) {
+ if (dwarf_rule_write(elf, fde, entry, index))
+ return -1;
+ index++;
+ }
+ }
+
+ return 0;
+}
+
+void dwarf_dump(void)
+{
+ struct fde *fde;
+ struct fde_entry *entry;
+ struct rule *sp_rule, *fp_rule;
+ int index = 0;
+
+ for (fde = fdes; fde != NULL; fde = fde->next) {
+ for (entry = fde->head; entry != NULL; entry = entry->next) {
+ sp_rule = &entry->sp_rule;
+ fp_rule = &entry->fp_rule;
+
+ printf("addr=%lx size=%lx:",
+ entry->addr, entry->size);
+ printf("\tsp=%ld sp_saved=%d fp=%ld fp_saved=%d\n",
+ sp_rule->offset, sp_rule->saved,
+ fp_rule->offset, fp_rule->saved);
+ index++;
+ }
+ }
}
@@ -10,6 +10,8 @@
#ifndef _OBJTOOL_DWARF_DEF_H
#define _OBJTOOL_DWARF_DEF_H
+#include <linux/dwarf.h>
+
/*
* The DWARF Call Frame Information (CFI) is encoded in a self-contained
* section called .debug_frame.
@@ -228,6 +230,14 @@ struct cie {
bool unusable;
};
+struct fde_entry {
+ struct fde_entry *next;
+ unsigned long addr;
+ size_t size;
+ struct rule sp_rule;
+ struct rule fp_rule;
+};
+
/*
* Frame Description Entry (FDE):
*
@@ -290,6 +300,8 @@ struct fde {
struct section *section;
unsigned long offset;
unsigned long sp_offset;
+ struct fde_entry *head;
+ struct fde_entry *tail;
};
/*
@@ -42,5 +42,7 @@ int check(struct objtool_file *file);
int orc_dump(const char *objname);
int orc_create(struct objtool_file *file);
int dwarf_parse(struct objtool_file *file);
+void dwarf_dump(void);
+int dwarf_write(struct objtool_file *file);
#endif /* _OBJTOOL_H */
@@ -27,6 +27,12 @@ arch/x86/lib/insn.c
'
fi
+if [ "$SRCARCH" = "arm64" ]; then
+FILES="$FILES
+include/linux/dwarf.h
+"
+fi
+
check_2 () {
file1=$1
file2=$2
@@ -38,6 +38,17 @@ int __weak dwarf_parse(struct objtool_file *file)
return -EOPNOTSUPP;
}
+int __weak dwarf_write(struct objtool_file *file)
+{
+ fprintf(stderr, "error: objtool: %s not implemented\n", __func__);
+ return -1;
+}
+
+void __weak dwarf_dump(void)
+{
+ fprintf(stderr, "error: objtool: %s not implemented\n", __func__);
+}
+
int __weak arch_dwarf_fde_reloc(struct fde *fde)
{
fprintf(stderr, "error: objtool: %s not implemented\n", __func__);