背景
Android模拟器运行于virtualbox中,而virtualbox运行于x86架构的pc端,所以android及其Linux内核都编译成x86架构。当virtualbox的vt未开启的情况下android系统会出现各种问题,如arm库游戏不能运行,桌面平凡挂死重启。通过查看日志,都奔溃在了#00 pc 000183c6 /system/lib/libc.so (__get_thread+6)这个点。
问题
在android内核为3.10时选择CONFIG_CC_STACKPROTECTOR=y(开启内核栈保护功能),在x86架构下能正常编译解决vt下桌面重复挂死、arm库游戏不能玩的问题。但是如果升级android内核为3.18,内核开启栈保护功能时,交叉编译x86架构下的linux内核就会出现编译错误。具体看如下开启内核配置和出错日志:
开启3.18栈保护的内核配置选项如下:
@@ -41,7 +41,6 @@ CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y
CONFIG_HAVE_INTEL_TXT=y
CONFIG_X86_32_SMP=y
CONFIG_X86_HT=y
-CONFIG_X86_32_LAZY_GS=y
CONFIG_ARCH_HWEIGHT_CFLAGS="-fcall-saved-ecx -fcall-saved-edx"
CONFIG_ARCH_SUPPORTS_UPROBES=y
CONFIG_FIX_EARLYCON_MEM=y
@@ -249,10 +248,10 @@ CONFIG_HAVE_CMPXCHG_DOUBLE=y
CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
CONFIG_HAVE_CC_STACKPROTECTOR=y
-# CONFIG_CC_STACKPROTECTOR is not set
-CONFIG_CC_STACKPROTECTOR_NONE=y
+CONFIG_CC_STACKPROTECTOR=y
+# CONFIG_CC_STACKPROTECTOR_NONE is not set
# CONFIG_CC_STACKPROTECTOR_REGULAR is not set
-# CONFIG_CC_STACKPROTECTOR_STRONG is not set
+CONFIG_CC_STACKPROTECTOR_STRONG=y
CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES=y
CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y
开启3.18内核栈保护配置后内核编译x86架构错误如下:
CHK include/generated/compile.h
UPD include/generated/compile.h
CC init/version.o
LD init/built-in.o
android-4.4.4/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.6/bin/i686-linux-android-ld: init/built-in.o: in function do_one_initcall:init_task.c(.text+0x7f): error: undefined reference to '__stack_chk_guard'
android-4.4.4/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.6/bin/i686-linux-android-ld: init/built-in.o: in function do_one_initcall:init_task.c(.text+0x1c6): error: undefined reference to '__stack_chk_guard'
android-4.4.4/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.6/bin/i686-linux-android-ld: init/built-in.o: in function name_to_dev_t:init_task.c(.text+0x261): error: undefined reference to '__stack_chk_guard'
android-4.4.4/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.6/bin/i686-linux-android-ld: init/built-in.o: in function name_to_dev_t:init_task.c(.text+0x517): error: undefined reference to '__stack_chk_guard'
make: *** [vmlinux] Error 1
arch/x86/Makefile:116: stack-protector enabled but compiler support broken
分析解决
上述error: undefined reference to '__stack_chk_guard'错误通过各种google也没有找到正解,有的说是gcc需要4.9及以上,然而用交叉编译工具4.9也不行。还查看了android源码关于stack protector的相关修复,都没有啥卵用。最后通过查看__stack_chk_guard字段发现,x86架构没有定义此字段,而sh,arm,mips等架构确定义了。在穷途末路时只能自己动手依葫芦画瓢,期待有所进展。如下patch为本人添加,不仅能解决编译错误,还确实解决了vt未开启时,virtualbox下运行android镜像出现的各种问题。
Linux编译x86架构时__stack_chk_guard未定义错误的修复patch
diff --git a/arch/x86/include/asm/stackprotector.h b/arch/x86/include/asm/stackprotector.h
index 6a99859..3e2d812 100644
--- a/arch/x86/include/asm/stackprotector.h
+++ b/arch/x86/include/asm/stackprotector.h
@@ -41,6 +41,10 @@
#include <asm/desc.h>
#include <linux/random.h>
+#if defined(CONFIG_CC_STACKPROTECTOR)
+extern unsigned long __stack_chk_guard;
+#endif
+
/*
* 24 byte read-only segment initializer for stack canary. Linker
* can't handle the address bit shifting. Address will be set in
@@ -79,6 +83,10 @@ static __always_inline void boot_init_stack_canary(void)
#else
this_cpu_write(stack_canary.canary, canary);
#endif
+
+#if defined(CONFIG_CC_STACKPROTECTOR)
+ __stack_chk_guard = current->stack_canary;
+#endif
}
static inline void setup_stack_canary_segment(int cpu)
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index 4eb204c..5ad8ab2 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -29,6 +29,12 @@
#include <asm/debugreg.h>
#include <asm/nmi.h>
+
+#ifdef CONFIG_CC_STACKPROTECTOR
+unsigned long __stack_chk_guard __read_mostly;
+EXPORT_SYMBOL(__stack_chk_guard);
+#endif
+
/*
* per-CPU TSS segments. Threads are completely 'soft' on Linux,
* no more per-task TSS's. The TSS size is kept cacheline-aligned
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
index 8f3ebfe..f027d25 100644
--- a/arch/x86/kernel/process_32.c
+++ b/arch/x86/kernel/process_32.c
@@ -39,6 +39,11 @@
#include <asm/pgtable.h>
#include <asm/ldt.h>
#include <asm/processor.h>
+
+#ifdef CONFIG_CC_STACKPROTECTOR
+#include <linux/stackprotector.h>
+#endif
+
#include <asm/i387.h>
#include <asm/fpu-internal.h>
#include <asm/desc.h>
@@ -249,6 +254,11 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
*next = &next_p->thread;
int cpu = smp_processor_id();
struct tss_struct *tss = &per_cpu(init_tss, cpu);
+
+ #if defined(CONFIG_CC_STACKPROTECTOR)
+ __stack_chk_guard = next_p->stack_canary;
+ #endif
+
fpu_switch_t fpu;
/* never put a printk in __switch_to... printk() calls wake_up*() indirectly */
为啥内核能正常选择打开栈保护功能但却无法编译通过呢,后面想了想,可能有如下原因:
一是godlfish内核主要对移动手机设备使用的内核,手机一般使用的是arm芯片,对于x86架构官方关注的也许并不多。
二是默认此栈保护功能是关闭的,只有对运行稳定性有特殊需求的产品,如航天,太空类高稳定产品才需要考虑打开,打开后会稍微降低性能。
此patch应该是可以作为官网的patch来提交修复,福泽大众的,但本人从来没有提交过,限于水平有限,暂且记录,以备后忘。