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.

Reply via email to