new file mode 100644
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Author: Madhavan T. Venkataraman (madvenka@linux.microsoft.com)
+ *
+ * Copyright (C) 2022 Microsoft Corporation
+ */
+
+#ifndef _ORC_TYPES_H
+#define _ORC_TYPES_H
+
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/orc_entry.h>
+
+/*
+ * The ORC_REG_* registers are base registers which are used to find other
+ * registers on the stack.
+ *
+ * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the
+ * address of the previous frame: the caller's SP before it called the current
+ * function.
+ *
+ * ORC_REG_UNDEFINED means the corresponding register's value didn't change in
+ * the current frame.
+ *
+ * We only use base registers SP and FP -- which the previous SP is based on --
+ * and PREV_SP and UNDEFINED -- which the previous FP is based on.
+ */
+#define ORC_REG_UNDEFINED 0
+#define ORC_REG_PREV_SP 1
+#define ORC_REG_SP 2
+#define ORC_REG_FP 3
+#define ORC_REG_MAX 4
+
+#endif /* _ORC_TYPES_H */
new file mode 100644
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Author: Madhavan T. Venkataraman (madvenka@linux.microsoft.com)
+ *
+ * Copyright (C) 2022 Microsoft Corporation
+ */
+
+#ifndef _ORC_TYPES_H
+#define _ORC_TYPES_H
+
+#include <linux/types.h>
+#include <linux/compiler.h>
+#include <linux/orc_entry.h>
+
+/*
+ * The ORC_REG_* registers are base registers which are used to find other
+ * registers on the stack.
+ *
+ * ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the
+ * address of the previous frame: the caller's SP before it called the current
+ * function.
+ *
+ * ORC_REG_UNDEFINED means the corresponding register's value didn't change in
+ * the current frame.
+ *
+ * We only use base registers SP and FP -- which the previous SP is based on --
+ * and PREV_SP and UNDEFINED -- which the previous FP is based on.
+ */
+#define ORC_REG_UNDEFINED 0
+#define ORC_REG_PREV_SP 1
+#define ORC_REG_SP 2
+#define ORC_REG_FP 3
+#define ORC_REG_MAX 4
+
+#endif /* _ORC_TYPES_H */
@@ -47,6 +47,7 @@ ifeq ($(SRCARCH),x86)
endif
ifeq ($(SRCARCH),arm64)
+ BUILD_ORC := y
DYNAMIC_CHECK := y
endif
@@ -1 +1,2 @@
objtool-y += decode.o
+objtool-y += orc.o
new file mode 100644
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+
+#ifndef _OBJTOOL_ARCH_ELF
+#define _OBJTOOL_ARCH_ELF
+
+#define R_NONE R_AARCH64_NONE
+#define R_PCREL R_AARCH64_PREL32
+
+#endif /* _OBJTOOL_ARCH_ELF */
new file mode 100644
@@ -0,0 +1,86 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Author: Madhavan T. Venkataraman (madvenka@linux.microsoft.com)
+ *
+ * Copyright (C) 2022 Microsoft Corporation
+ */
+#include <string.h>
+
+#include <linux/objtool.h>
+
+#include <objtool/insn.h>
+#include <objtool/orc.h>
+
+int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi,
+ struct instruction *insn)
+{
+ struct cfi_reg *fp = &cfi->regs[CFI_FP];
+
+ memset(orc, 0, sizeof(*orc));
+
+ orc->sp_reg = ORC_REG_SP;
+ orc->fp_reg = ORC_REG_PREV_SP;
+
+ if (!cfi || cfi->cfa.base == CFI_UNDEFINED ||
+ (cfi->type == UNWIND_HINT_TYPE_CALL && !fp->offset)) {
+ /*
+ * The frame pointer has not been set up. This instruction is
+ * unreliable from an unwind perspective.
+ */
+ return 0;
+ }
+
+ orc->sp_offset = cfi->cfa.offset;
+ orc->fp_offset = fp->offset;
+ orc->type = cfi->type;
+ orc->end = cfi->end;
+
+ return 0;
+}
+
+static const char *reg_name(unsigned int reg)
+{
+ switch (reg) {
+ case ORC_REG_PREV_SP:
+ return "cfa";
+ case ORC_REG_FP:
+ return "x29";
+ case ORC_REG_SP:
+ return "sp";
+ default:
+ return "?";
+ }
+}
+
+const char *orc_type_name(unsigned int type)
+{
+ switch (type) {
+ case UNWIND_HINT_TYPE_CALL:
+ return "call";
+ default:
+ return "?";
+ }
+}
+
+void orc_print_reg(unsigned int reg, int offset)
+{
+ if (reg == ORC_REG_UNDEFINED)
+ printf("(und)");
+ else
+ printf("%s%+d", reg_name(reg), offset);
+}
+
+void orc_print_sp(void)
+{
+ printf(" cfa:");
+}
+
+void orc_print_fp(void)
+{
+ printf(" x29:");
+}
+
+bool orc_ignore_section(struct section *sec)
+{
+ return !strcmp(sec->name, ".head.text");
+}
@@ -349,5 +349,8 @@ int check(struct objtool_file *file)
walk_sections(file);
- return 0;
+ if (opts.orc)
+ ret = orc_create(file);
+
+ return ret;
}
@@ -84,6 +84,7 @@ struct instruction *next_insn_same_sec(struct objtool_file *file,
struct instruction *next_insn_same_func(struct objtool_file *file,
struct instruction *insn);
struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn);
+bool insn_can_reloc(struct instruction *insn);
bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2,
bool print);
bool same_function(struct instruction *insn1, struct instruction *insn2);
@@ -46,5 +46,6 @@ void objtool_pv_add(struct objtool_file *file, int idx, struct symbol *func);
int check(struct objtool_file *file);
int orc_dump(const char *objname);
int orc_create(struct objtool_file *file);
+bool orc_ignore_section(struct section *sec);
#endif /* _OBJTOOL_H */
@@ -193,3 +193,23 @@ bool insn_cfi_match(struct instruction *insn, struct cfi_state *cfi2,
return false;
}
+
+/*
+ * This is a hack for Clang. Clang is aggressive about removing section
+ * symbols and then some. If we cannot find something to relocate an
+ * instruction against, we must not generate CFI for it or the ORC
+ * generation will fail later.
+ */
+bool insn_can_reloc(struct instruction *insn)
+{
+ struct section *insn_sec = insn->sec;
+ unsigned long insn_off = insn->offset;
+
+ if (insn_sec->sym ||
+ find_symbol_containing(insn_sec, insn_off) ||
+ find_symbol_containing(insn_sec, insn_off - 1)) {
+ /* See elf_add_reloc_to_insn(). */
+ return true;
+ }
+ return false;
+}
@@ -14,6 +14,11 @@
#include <objtool/warn.h>
#include <objtool/endianness.h>
+bool __weak orc_ignore_section(struct section *sec)
+{
+ return false;
+}
+
static int write_orc_entry(struct elf *elf, struct section *orc_sec,
struct section *ip_sec, unsigned int idx,
struct section *insn_sec, unsigned long insn_off,
@@ -87,13 +92,16 @@ int orc_create(struct objtool_file *file)
struct instruction *insn;
bool empty = true;
- if (!sec->text)
+ if (!sec->text || orc_ignore_section(sec))
continue;
sec_for_each_insn(file, sec, insn) {
struct alt_group *alt_group = insn->alt_group;
int i;
+ if (!insn_can_reloc(insn))
+ continue;
+
if (!alt_group) {
if (init_orc_entry(&orc, insn->cfi, insn))
return -1;
@@ -137,7 +145,7 @@ int orc_create(struct objtool_file *file)
}
/* Add a section terminator */
- if (!empty) {
+ if (!empty && sec->sym) {
orc_list_add(&orc_list, &null, sec, sec->sh.sh_size);
nr++;
}
@@ -29,6 +29,13 @@ arch/x86/lib/insn.c
'
fi
+if [ "$SRCARCH" = "arm64" ]; then
+FILES="$FILES
+arch/arm64/include/asm/orc_types.h
+include/linux/orc_entry.h
+"
+fi
+
check_2 () {
file1=$1
file2=$2