summaryrefslogtreecommitdiff
path: root/arch
diff options
context:
space:
mode:
authorGabe Black <gabeblack@chromium.org>2011-08-08 16:48:05 -0700
committerSimon Glass <sjg@chromium.org>2011-08-29 10:59:30 -0700
commitdc900f1de50a5e0d329433cb3957d40c689062b7 (patch)
tree1fef6aa3fef7257596e1df5902045ccda8c1559c /arch
parent8c056507ce8e3e53a6459b447685f20fb483b090 (diff)
Wrap small helper functions from libgcc to avoid an ABI mismatch
When gcc compiles some 64 bit operations on a 32 bit machine, it generates calls to small functions instead of instructions which do the job directly. Those functions are defined in libgcc and transparently provide whatever functionality was necessary. Unfortunately, u-boot and vboot are both built with a non-standard ABI and libgcc isn't. When the two are linked together, very confusing bugs can crop up, for instance seemingly normal integer division or modulus getting the wrong answer or even raising a spurious divide by zero exception. This change barrows (steals) a technique and some code from coreboot which solves this problem by creating wrappers which translate the calling convention when calling the functions in libgcc. Unfortunately that means that these instructions which had already been turned into functions have even more overhead, but more importantly it makes them work properly. To find all of the functions that needed wrapping, u-boot was compiled without linking in libgcc. All the symbols the linker complained were undefined were presumed to be the symbols that are needed from libgcc. These were a subset of the symbols covered by the coreboot code, so it was used unmodified. It wouldn't be a bad idea to revisit vboot and try to either get rid of these slow operations, or make them faster by enabling appropriate instructions in gcc. Currently the vboot Makefile sets -march=i386 which is a very conservative setting. Something more aggressive might help. BUG=chrome-os-partner:4552 TEST=Built and ran vboot_twostop. Saw previous incorrect behavior go away. Signed-off-by: Gabe Black <gabeblack@google.com> Change-Id: I45439fd8e967754b9168700f152fbab09521c99f Reviewed-on: http://gerrit.chromium.org/gerrit/5543 Reviewed-by: Gabe Black <gabeblack@chromium.org> Tested-by: Gabe Black <gabeblack@chromium.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/config.mk3
-rw-r--r--arch/x86/lib/Makefile1
-rw-r--r--arch/x86/lib/gcc.c38
3 files changed, 42 insertions, 0 deletions
diff --git a/arch/x86/config.mk b/arch/x86/config.mk
index ee23c9f87d2..ab8e83ccdf8 100644
--- a/arch/x86/config.mk
+++ b/arch/x86/config.mk
@@ -39,3 +39,6 @@ PLATFORM_RELFLAGS += -ffunction-sections -fvisibility=hidden
PLATFORM_LDFLAGS += --emit-relocs -Bsymbolic -Bsymbolic-functions
LDFLAGS_FINAL += --gc-sections -pie
+LDFLAGS_FINAL += --wrap=__divdi3 --wrap=__udivdi3
+LDFLAGS_FINAL += --wrap=__moddi3 --wrap=__umoddi3
+LDSCRIPT := $(SRCTREE)/$(CPUDIR)/u-boot.lds
diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
index 3a0527160a0..8b0ac74dca6 100644
--- a/arch/x86/lib/Makefile
+++ b/arch/x86/lib/Makefile
@@ -32,6 +32,7 @@ SOBJS-y += realmode_switch.o
COBJS-y += bios_setup.o
COBJS-y += board.o
COBJS-y += bootm.o
+COBJS-y += gcc.o
COBJS-y += interrupts.o
COBJS-$(CONFIG_SYS_PCAT_INTERRUPTS) += pcat_interrupts.o
COBJS-$(CONFIG_SYS_GENERIC_TIMER) += pcat_timer.o
diff --git a/arch/x86/lib/gcc.c b/arch/x86/lib/gcc.c
new file mode 100644
index 00000000000..b11ea5f2c7f
--- /dev/null
+++ b/arch/x86/lib/gcc.c
@@ -0,0 +1,38 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2009 coresystems GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 or later of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA, 02110-1301 USA
+ */
+
+#ifdef __GNUC__
+
+/*
+ * GCC's libgcc handling is quite broken. While the libgcc functions
+ * are always regparm(0) the code that calls them uses whatever the
+ * compiler call specifies. Therefore we need a wrapper around those
+ * functions. See gcc bug PR41055 for more information.
+ */
+#define WRAP_LIBGCC_CALL(type, name) \
+ type __real_##name(type a, type b) __attribute__((regparm(0))); \
+ type __wrap_##name(type a, type b); \
+ type __wrap_##name(type a, type b) { return __real_##name(a, b); }
+
+WRAP_LIBGCC_CALL(long long, __divdi3)
+WRAP_LIBGCC_CALL(unsigned long long, __udivdi3)
+WRAP_LIBGCC_CALL(long long, __moddi3)
+WRAP_LIBGCC_CALL(unsigned long long, __umoddi3)
+
+#endif