as: How to determine the section of symbols

Peter Zijlstra peterz@infradead.org
Mon Jul 15 14:49:00 GMT 2019


Hi all,

I'm trying to 'optimize' the Linux Kernel x86 jump_label support. That
is; jump_label or static_branch() is a Linux Kernel construct build upon
GCC asm goto and provides for self-modifying code based branches.
Regular execution will only see unconditional branches or nops.

Currently, on x86, we patch between jmp.d32 or nop5 and this works well.

The quest is to also allow usage of jmp.d8 (and the matching nop2) where
the displacement allows for this.

The below patch is the last of a series that implements this and
contains all the relevant bits to this discussion, and is subtly broken.

The problem is that the labels GCC hands to the asm goto () can be in
different sections (namely .text and .text.unlikely), and the GAS manual
sayeth:

 [ https://sourceware.org/binutils/docs-2.27/as/Infix-Ops.html#Infix-Ops ]

 - Subtraction. If the right argument is absolute, the result has the
   section of the left argument. If both arguments are in the same
   section, the result is absolute. You may not subtract arguments from
   different sections.

Funnily this does not result in a compile/assemble time error :-(, it
seems to emit a MOP5 but then at runtime explodes because the actual
displacement (after linking etc..) ends up fitting in a d8 and then the
actual code and the expected code don't match up at code patching time
and we BUG.

If I were to be able to reliably detect this section mismatch I could
encode it in the JUMP_TABLE_ENTRY (__jump_table section).

Any clues on how I can (best) fix this; even if it involves writing a
GAS patch that'd be fine, we can have this functionality depend on a
binutils version.

Thanks!

  ~ Peter Zijlstra


Full patch series here:

  https://git.kernel.org/pub/scm/linux/kernel/git/peterz/queue.git/log/?h=locking/jump_label


---
Subject: jump_label, x86: Enable JMP8/NOP2 support
From: Peter Zijlstra <peterz@infradead.org>
Date: Fri Jun 28 10:20:55 CEST 2019

Enable and emit short JMP/NOP jump_label entries.

Much thanks to Josh for (re)discovering the .skip trick to
conditionally emit variable length text.

Due to how early we enable jump_labels on x86, if any of this comes
apart, the machine is completely dead. Qemu+GDB saved the day this
time.

Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
---
 arch/x86/include/asm/jump_label.h |   37 +++++++++++++++++++++++++++++++------
 arch/x86/kernel/jump_label.c      |    5 ++++-
 2 files changed, 35 insertions(+), 7 deletions(-)

--- a/arch/x86/include/asm/jump_label.h
+++ b/arch/x86/include/asm/jump_label.h
@@ -31,7 +31,34 @@
 static __always_inline bool arch_static_branch(struct static_key *key, bool branch)
 {
 	asm_volatile_goto("1:"
-		".byte " __stringify(STATIC_KEY_INIT_NOP) "\n\t"
+
+		".set disp, %l[l_yes] - (1b + 2) \n\t"
+		".set res, (disp >> 31) == (disp >> 7) \n\t"
+		".set is_byte, -res \n\t"
+		".set is_long, -(~res) \n\t"
+
+#ifdef CONFIG_X86_64
+		".skip is_byte, 0x66 \n\t"
+		".skip is_byte, 0x90 \n\t"
+#else
+		".skip is_byte, 0x89 \n\t"
+		".skip is_byte, 0xf6 \n\t"
+#endif
+
+#ifdef CONFIG_X86_64
+		".skip is_long, 0x0f \n\t"
+		".skip is_long, 0x1f \n\t"
+		".skip is_long, 0x44 \n\t"
+		".skip is_long, 0x00 \n\t"
+		".skip is_long, 0x00 \n\t"
+#else
+		".skip is_long, 0x3e \n\t"
+		".skip is_long, 0x8d \n\t"
+		".skip is_long, 0x74 \n\t"
+		".skip is_long, 0x26 \n\t"
+		".skip is_long, 0x00 \n\t"
+#endif
+
 		JUMP_TABLE_ENTRY
 		: :  "i" (key), "i" (branch) : : l_yes);
 
@@ -43,8 +71,7 @@ static __always_inline bool arch_static_
 static __always_inline bool arch_static_branch_jump(struct static_key *key, bool branch)
 {
 	asm_volatile_goto("1:"
-		".byte 0xe9 \n\t"
-		".long %l[l_yes] - (. + 4) \n\t"
+		"jmp %l[l_yes] \n\t"
 		JUMP_TABLE_ENTRY
 		: :  "i" (key), "i" (branch) : : l_yes);
 
@@ -59,9 +86,7 @@ extern int arch_jump_entry_size(struct j
 
 .macro STATIC_BRANCH_FALSE_LIKELY target, key
 .Lstatic_jump_\@:
-	/* Equivalent to "jmp.d32 \target" */
-	.byte		0xe9
-	.long		\target - (. + 4)
+	jmp \target
 
 	.pushsection __jump_table, "aw"
 	_ASM_ALIGN
--- a/arch/x86/kernel/jump_label.c
+++ b/arch/x86/kernel/jump_label.c
@@ -29,7 +29,8 @@ union jump_code_union {
 
 static inline bool __jump_disp_is_byte(s32 disp)
 {
-	return false;
+	disp -= JMP8_INSN_SIZE;
+	return (disp >> 31) == (disp >> 7);
 }
 
 int arch_jump_entry_size(struct jump_entry *entry)



More information about the Binutils mailing list