Re: [RFC, PATCHv1 15/28] x86: detect 5-level paging support

From: Borislav Petkov
Date: Thu Dec 15 2016 - 09:39:53 EST


On Wed, Dec 14, 2016 at 12:07:54AM +0100, Boris Petkov wrote:
> Thus I was thinking of adding a build-time check for the gcc version
> but that might turn out to be more code in the end than those ugly
> ifnc clauses.

IOW, something like this. I did this just to try to see whether it is
doable. And it does work - gcc 4.8 and 4.9 -m32 cannot preserve the PIC
register - actually the inline asm fails building due to impossible
constraints.

However, so many lines changed just to save the ifnc, meh, I dunno...

---
arch/x86/boot/compressed/Makefile | 8 ++++++
arch/x86/boot/cpuflags.c | 14 ++++++++--
scripts/gcc-clobber-pic.sh | 58 +++++++++++++++++++++++++++++++++++++++
3 files changed, 77 insertions(+), 3 deletions(-)
create mode 100755 scripts/gcc-clobber-pic.sh

diff --git a/arch/x86/boot/compressed/Makefile b/arch/x86/boot/compressed/Makefile
index 34d9e15857c3..705fc2ab3fd6 100644
--- a/arch/x86/boot/compressed/Makefile
+++ b/arch/x86/boot/compressed/Makefile
@@ -35,6 +35,14 @@ KBUILD_CFLAGS += -mno-mmx -mno-sse
KBUILD_CFLAGS += $(call cc-option,-ffreestanding)
KBUILD_CFLAGS += $(call cc-option,-fno-stack-protector)

+# check whether inline asm clobbers the PIC register
+ifeq ($(CONFIG_X86_32),y)
+ifeq ($(shell $(CONFIG_SHELL) $(srctree)/scripts/gcc-clobber-pic.sh $(CC) -m32),n)
+ KBUILD_CFLAGS += -DCC_PRESERVES_PIC
+ KBUILD_AFLAGS += -DCC_PRESERVES_PIC
+endif
+endif
+
KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
GCOV_PROFILE := n
UBSAN_SANITIZE :=n
diff --git a/arch/x86/boot/cpuflags.c b/arch/x86/boot/cpuflags.c
index 6687ab953257..913c3f5ab3a0 100644
--- a/arch/x86/boot/cpuflags.c
+++ b/arch/x86/boot/cpuflags.c
@@ -70,11 +70,19 @@ int has_eflag(unsigned long mask)
# define EBX_REG "=b"
#endif

+#if defined(__i386__) && defined(__PIC__) && !defined(CC_PRESERVES_PIC)
+# define SAVE_PIC ".ifnc %%ebx, %3; movl %%ebx, %3; .endif\n\t"
+# define SWAP_PIC ".ifnc %%ebx, %3; xchgl %%ebx, %3; .endif\n\t"
+#else
+# define SAVE_PIC
+# define SWAP_PIC
+#endif
+
static inline void cpuid(u32 id, u32 *a, u32 *b, u32 *c, u32 *d)
{
- asm volatile(".ifnc %%ebx,%3 ; movl %%ebx,%3 ; .endif \n\t"
- "cpuid \n\t"
- ".ifnc %%ebx,%3 ; xchgl %%ebx,%3 ; .endif \n\t"
+ asm volatile(SAVE_PIC
+ "cpuid\n\t"
+ SWAP_PIC
: "=a" (*a), "=c" (*c), "=d" (*d), EBX_REG (*b)
: "a" (id)
);
diff --git a/scripts/gcc-clobber-pic.sh b/scripts/gcc-clobber-pic.sh
new file mode 100755
index 000000000000..7ff10edf9b08
--- /dev/null
+++ b/scripts/gcc-clobber-pic.sh
@@ -0,0 +1,58 @@
+#!/bin/bash -x
+err=0
+O=$(mktemp)
+cat << "END" | $@ -fPIC -x c - -o $O >/dev/null 2>&1 || err=1
+int some_global_var, some_other_global_var;
+
+typedef unsigned int u32;
+
+void __attribute__((noinline)) foo(void)
+{
+ asm volatile("# some crap just so that we don't get optimized away");
+
+ some_other_global_var = 43;
+}
+
+static inline void cpuid(u32 id, u32 *a, u32 *b, u32 *c, u32 *d)
+{
+ asm volatile("cpuid"
+ : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d)
+ : "a" (id), "2" (*c)
+ : "si", "di"
+ );
+
+ some_global_var = 42;
+ foo();
+}
+
+int main(void)
+{
+ u32 a, b, c = 0, d;
+
+ cpuid(0x1, &a, &b, &c, &d);
+
+ /*
+ * Make sure foo() gets actually called and not optimized away due to
+ * miscompilation.
+ */
+ if (some_global_var == 42 && some_other_global_var == 43)
+ return 0;
+ else
+ return 1;
+}
+END
+
+if (( $err ));
+then
+ exit 1
+fi
+
+chmod u+x $O
+$O
+
+if ! (( $? ));
+then
+ echo "n"
+fi
+
+rm -f $O
--
2.11.0

--
Regards/Gruss,
Boris.

Good mailing practices for 400: avoid top-posting and trim the reply.