[U-Boot] [PATCH v3] x86: Wrap small helper functions from libgcc to avoid an ABI mismatch

Gabe Black gabeblack at chromium.org
Thu Nov 17 10:13:50 CET 2011


On Thu, Nov 17, 2011 at 1:01 AM, Gabe Black <gabeblack at chromium.org> wrote:

> 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 can be built with a
> non-standard ABI when libgcc isn't. More specifically, u-boot uses
> -mregparm. When the u-boot and libgcc 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 borrows (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.
>
> To prevent symbols which are provided by libgcc but not currently wrapped
> (or even known about) from being silently linked against by code generated
> by libgcc, a new copy of libgcc is created where all the symbols are
> prefixed with __normal_. Without being purposefully wrapped, these symbols
> will cause linker errors instead of silently introducing very subtle,
> confusing bugs.
>
> Another approach would be to whitelist symbols from libgcc and strip out
> all the others. The problem with this approach is that it requires the
> white listed symbols to be specified three times, once for objcopy, once so
> the linker inserts the wrapped, and once to generate the wrapper itself,
> while this implementation needs it to be listed only twice. There isn't
> much tangible difference in what each approach produces, so this one was
> preferred.
>
> Signed-off-by: Gabe Black <gabeblack at chromium.org>
> ---
> Changes in v2:
> - Change the [x86] tag to x86:
> - Mention -mregparm in the commit message.
> - Get rid of a stray line which snuck in during a rebase.
>
> Changes in v3:
> - Prevent symbols from libgcc which aren't wrapped from getting silently
> picked up by the linker.
>
>  arch/x86/config.mk    |    7 +++++++
>  arch/x86/lib/Makefile |    6 ++++++
>  arch/x86/lib/gcc.c    |   38 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 51 insertions(+), 0 deletions(-)
>  create mode 100644 arch/x86/lib/gcc.c
>
> diff --git a/arch/x86/config.mk b/arch/x86/config.mk
> index fe9083f..23cacff 100644
> --- a/arch/x86/config.mk
> +++ b/arch/x86/config.mk
> @@ -41,3 +41,10 @@ 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
> +
> +NORMAL_LIBGCC = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
> +PREFIXED_LIBGCC = $(OBJTREE)/arch/$(ARCH)/lib/$(shell basename
> $(NORMAL_LIBGCC))
> +
> +export USE_PRIVATE_LIBGCC=$(shell dirname $(PREFIXED_LIBGCC))
> diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile
> index eb5fa10..16db73f 100644
> --- a/arch/x86/lib/Makefile
> +++ b/arch/x86/lib/Makefile
> @@ -32,6 +32,7 @@ SOBJS-$(CONFIG_SYS_X86_REALMODE)      +=
> realmode_switch.o
>  COBJS-$(CONFIG_SYS_PC_BIOS)    += 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
> @@ -49,6 +50,11 @@ OBJS := $(addprefix $(obj),$(SOBJS-y) $(COBJS-y))
>  $(LIB):        $(obj).depend $(OBJS)
>        $(call cmd_link_o_target, $(OBJS))
>
> +$(PREFIXED_LIBGCC): $(NORMAL_LIBGCC)
> +       $(OBJCOPY) $< $@ --prefix-symbols=__normal_
> +
> +$(LIB): $(PREFIXED_LIBGCC)
> +
>  #########################################################################
>
>  # defines $(obj).depend target
> diff --git a/arch/x86/lib/gcc.c b/arch/x86/lib/gcc.c
> new file mode 100644
> index 0000000..4043431
> --- /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 __normal_##name(type a, type b) __attribute__((regparm(0))); \
> +       type __wrap_##name(type a, type b); \
> +       type __wrap_##name(type a, type b) { return __normal_##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
> --
> 1.7.3.1
>
>

The patch I just sent out is not 100% checkpatch clean (1 error, 1
warning), but the error is because it misidentifies the macro in gcc.c as a
collection of statements instead of function prototypes/definitions and
insists they're wrapped in a do {} while loop. That wouldn't be possible,
so I'm sending this out as is. There may also be small bits of review
feedback I haven't implemented (the name of gcc.c?) because I wasn't able
to find the email the feedback was in. Please send them again and I'll spin
another version as necessary.

This has been tested in our tree for our build target and upstream for the
eNET target, both in our environment. It wouldn't be a bad idea to test it
again in a different environment to be sure I'm not just getting lucky
somehow.

Gabe


More information about the U-Boot mailing list