Commit b9f174c8 authored by Omar Sandoval's avatar Omar Sandoval Committed by Peter Zijlstra
Browse files

x86/unwind/orc: Add ELF section with ORC version identifier

Commits ffb1b4a4 ("x86/unwind/orc: Add 'signal' field to ORC
metadata") and fb799447 ("x86,objtool: Split UNWIND_HINT_EMPTY in
two") changed the ORC format. Although ORC is internal to the kernel,
it's the only way for external tools to get reliable kernel stack traces
on x86-64. In particular, the drgn debugger [1] uses ORC for stack
unwinding, and these format changes broke it [2]. As the drgn
maintainer, I don't care how often or how much the kernel changes the
ORC format as long as I have a way to detect the change.

It suffices to store a version identifier in the vmlinux and kernel
module ELF files (to use when parsing ORC sections from ELF), and in
kernel memory (to use when parsing ORC from a core dump+symbol table).
Rather than hard-coding a version number that needs to be manually
bumped, Peterz suggested hashing the definitions from orc_types.h. If
there is a format change that isn't caught by this, the hashing script
can be updated.

This patch adds an .orc_header allocated ELF section containing the
20-byte hash to vmlinux and kernel modules, along with the corresponding
__start_orc_header and __stop_orc_header symbols in vmlinux.

1: https://github.com/osandov/drgn
2: https://github.com/osandov/drgn/issues/303



Fixes: ffb1b4a4 ("x86/unwind/orc: Add 'signal' field to ORC metadata")
Fixes: fb799447 ("x86,objtool: Split UNWIND_HINT_EMPTY in two")
Signed-off-by: default avatarOmar Sandoval <osandov@fb.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: default avatarJosh Poimboeuf <jpoimboe@kernel.org>
Link: https://lkml.kernel.org/r/aef9c8dc43915b886a8c48509a12ec1b006ca1ca.1686690801.git.osandov@osandov.com
parent 858fd168
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -305,6 +305,18 @@ ifeq ($(RETPOLINE_CFLAGS),)
endif
endif

ifdef CONFIG_UNWINDER_ORC
orc_hash_h := arch/$(SRCARCH)/include/generated/asm/orc_hash.h
orc_hash_sh := $(srctree)/scripts/orc_hash.sh
targets += $(orc_hash_h)
quiet_cmd_orc_hash = GEN     $@
      cmd_orc_hash = mkdir -p $(dir $@); \
		     $(CONFIG_SHELL) $(orc_hash_sh) < $< > $@
$(orc_hash_h): $(srctree)/arch/x86/include/asm/orc_types.h $(orc_hash_sh) FORCE
	$(call if_changed,orc_hash)
archprepare: $(orc_hash_h)
endif

archclean:
	$(Q)rm -rf $(objtree)/arch/i386
	$(Q)rm -rf $(objtree)/arch/x86_64
+1 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0


generated-y += orc_hash.h
generated-y += syscalls_32.h
generated-y += syscalls_64.h
generated-y += syscalls_x32.h
+19 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (c) Meta Platforms, Inc. and affiliates. */

#ifndef _ORC_HEADER_H
#define _ORC_HEADER_H

#include <linux/types.h>
#include <linux/compiler.h>
#include <asm/orc_hash.h>

/*
 * The header is currently a 20-byte hash of the ORC entry definition; see
 * scripts/orc_hash.sh.
 */
#define ORC_HEADER					\
	__used __section(".orc_header") __aligned(4)	\
	static const u8 orc_header[] = { ORC_HASH }

#endif /* _ORC_HEADER_H */
+3 −0
Original line number Diff line number Diff line
@@ -7,6 +7,9 @@
#include <asm/unwind.h>
#include <asm/orc_types.h>
#include <asm/orc_lookup.h>
#include <asm/orc_header.h>

ORC_HEADER;

#define orc_warn(fmt, ...) \
	printk_deferred_once(KERN_WARNING "WARNING: " fmt, ##__VA_ARGS__)
+3 −0
Original line number Diff line number Diff line
@@ -839,6 +839,9 @@

#ifdef CONFIG_UNWINDER_ORC
#define ORC_UNWIND_TABLE						\
	.orc_header : AT(ADDR(.orc_header) - LOAD_OFFSET) {		\
		BOUNDED_SECTION_BY(.orc_header, _orc_header)		\
	}								\
	. = ALIGN(4);							\
	.orc_unwind_ip : AT(ADDR(.orc_unwind_ip) - LOAD_OFFSET) {	\
		BOUNDED_SECTION_BY(.orc_unwind_ip, _orc_unwind_ip)	\
Loading