[PATCH 00/15] ulib: Provide test programs and documentation

From: Simon Glass <sjg@chromium.org> This series completes the implementation of a basic U-Boot library: - ensure that no output is written during init - provide a static library to match the existing shared library - add test programs for both, to ensure they continue to build correctly - provide documentation to describe how to use the libraries This is still very early in the implementation, but this series provides enough for some initial experimentation. Simon Glass (15): sandbox: Add a function to read a line from a file ulib: Disable environment setup ulib: Disable console messages ulib: Disable network setup ulib: Disable the main loop ulib: Move struct bd_info into its own header ulib: sandbox: Move the linker-script name into config.mk ulib: Add static-library build support ulib: Provide an init function for sandbox ulib: Provide a generic init function ulib: Provide a test program for the static library ulib: Drop unnecessary pieces in test/ulib/Makefile ulib: Provide a bit more info in the library-test program ulib: Allow building of the libraries to be disabled doc: Add ulib documentation for shared and static libraries Makefile | 38 +++- arch/sandbox/config.mk | 4 + arch/sandbox/cpu/os.c | 32 +++ arch/sandbox/cpu/start.c | 30 ++- arch/sandbox/cpu/ulib-test-static.lds | 19 ++ arch/sandbox/include/asm/u-boot-sandbox.h | 9 + common/board_r.c | 14 ++ common/console.c | 2 +- doc/develop/index.rst | 1 + doc/develop/ulib.rst | 253 ++++++++++++++++++++++ include/asm-generic/u-boot.h | 52 +---- include/bd.h | 70 ++++++ include/init.h | 4 + include/os.h | 14 ++ include/u-boot-lib.h | 34 +++ lib/Makefile | 2 + lib/ulib.c | 36 +++ test/ulib/Makefile | 7 - test/ulib/ulib_test.c | 53 +++-- 19 files changed, 593 insertions(+), 81 deletions(-) create mode 100644 arch/sandbox/cpu/ulib-test-static.lds create mode 100644 doc/develop/ulib.rst create mode 100644 include/bd.h create mode 100644 include/u-boot-lib.h create mode 100644 lib/ulib.c -- 2.43.0 base-commit: d1e8f71cb5e8648328b8043b8f1a3ea780b4e50d branch: ulid

From: Simon Glass <sjg@chromium.org> Provide a version of fgets() which works on file descriptors. This can be used to read lines from a file, as will be needed for the ulib test-program. Signed-off-by: Simon Glass <sjg@chromium.org> --- arch/sandbox/cpu/os.c | 32 ++++++++++++++++++++++++++++++++ include/os.h | 14 ++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/arch/sandbox/cpu/os.c b/arch/sandbox/cpu/os.c index 1c4e23cb4eb..891dfd9c9b8 100644 --- a/arch/sandbox/cpu/os.c +++ b/arch/sandbox/cpu/os.c @@ -150,6 +150,38 @@ int os_unlink(const char *pathname) return unlink(pathname); } +char *os_fgets(char *str, int size, int fd) +{ + char *s = str; + int n = size - 1; + int i; + + if (n <= 0 || !str) + return NULL; + + for (i = 0; i < n; i++) { + ssize_t ret; + char c; + + ret = read(fd, &c, 1); + if (ret <= 0) { + /* EOF or error */ + if (!i) + return NULL; + break; + } + + *s++ = c; + + if (c == '\n') + break; + } + + *s = '\0'; + + return str; +} + void os_exit(int exit_code) { exit(exit_code); diff --git a/include/os.h b/include/os.h index 3393acb435a..e5c524c57a4 100644 --- a/include/os.h +++ b/include/os.h @@ -106,6 +106,20 @@ int os_isatty(int fd); */ int os_unlink(const char *pathname); +/** + * os_fgets() - read a string from a file stream + * + * Reads at most @size - 1 characters from the stream and stores them in str. + * Reading stops after an EOF or a newline. If a newline is read, it is + * stored in str. A terminating nul byte is appended. + * + * @str: Buffer to store the string + * @size: Maximum number of characters to read (including null terminator) + * @fd: File descriptor to read from + * Return: str on success, NULL on error, or EOF with no characters read + */ +char *os_fgets(char *str, int size, int fd); + /** os_persistent_fname() - Find the path to a test file * * @buf: Buffer to hold path -- 2.43.0

From: Simon Glass <sjg@chromium.org> The application may not wish to have an environment, so skip this init with the library. Signed-off-by: Simon Glass <sjg@chromium.org> --- common/board_r.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/board_r.c b/common/board_r.c index 1bb0eb37e9b..22506effae6 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -417,6 +417,9 @@ static int should_load_env(void) static int initr_env(void) { + if (gd_ulib()) + return 0; + /* initialize environment */ if (should_load_env()) env_relocate(); -- 2.43.0

From: Simon Glass <sjg@chromium.org> By default U-Boot shows the console settings on startup, e.g.: In: serial,vidconsole Out: serial,vidconsole Err: serial,vidconsole These messages are useful for the user but not for an application using U-Boot as a library. Disable them. Signed-off-by: Simon Glass <sjg@chromium.org> --- common/console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/console.c b/common/console.c index 2073a9878a5..77f70588488 100644 --- a/common/console.c +++ b/common/console.c @@ -1301,7 +1301,7 @@ int console_init_r(void) done: - if (!IS_ENABLED(CONFIG_SYS_CONSOLE_INFO_QUIET)) + if (!IS_ENABLED(CONFIG_SYS_CONSOLE_INFO_QUIET) && !gd_ulib()) stdio_print_current_devices(); #ifdef CONFIG_VIDCONSOLE_AS_LCD -- 2.43.0

From: Simon Glass <sjg@chromium.org> The application may not wish to use networking, so avoid setting it up and the associated console output. Signed-off-by: Simon Glass <sjg@chromium.org> --- common/board_r.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/common/board_r.c b/common/board_r.c index 22506effae6..a19c3138962 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -458,6 +458,9 @@ static int initr_malloc_bootparams(void) #if CONFIG_IS_ENABLED(NET) || CONFIG_IS_ENABLED(NET_LWIP) static int initr_net(void) { + if (gd_ulib()) + return 0; + puts("Net: "); eth_initialize(); #if defined(CONFIG_RESET_PHY_R) -- 2.43.0

From: Simon Glass <sjg@chromium.org> The application wants control once the U-Boot library is ready, so skip the main loop and just return. Signed-off-by: Simon Glass <sjg@chromium.org> --- common/board_r.c | 8 ++++++++ include/init.h | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/common/board_r.c b/common/board_r.c index a19c3138962..d38c2304e87 100644 --- a/common/board_r.c +++ b/common/board_r.c @@ -539,6 +539,9 @@ static int dm_announce(void) static int run_main_loop(void) { + if (gd_ulib()) + return 0; + #ifdef CONFIG_SANDBOX sandbox_main_loop_init(); #endif @@ -768,6 +771,11 @@ void board_init_r(gd_t *new_gd, ulong dest_addr) initcall_run_r(); +#ifdef CONFIG_ULIB + if (gd_ulib()) + return; +#endif + /* NOTREACHED - run_main_loop() does not return */ hang(); } diff --git a/include/init.h b/include/init.h index 2c37cef5fc0..e4b07ea48a0 100644 --- a/include/init.h +++ b/include/init.h @@ -277,8 +277,12 @@ struct global_data; void arch_setup_gd(struct global_data *gd_ptr); /* common/board_r.c */ +#ifdef CONFIG_ULIB +void board_init_r(struct global_data *id, ulong dest_addr); +#else void board_init_r(struct global_data *id, ulong dest_addr) __attribute__ ((noreturn)); +#endif int cpu_init_r(void); int mac_read_from_eeprom(void); -- 2.43.0

From: Simon Glass <sjg@chromium.org> This is currently in asm-generic/u-boot.h but it is no-longer the interface to U-Boot in any real sense. It also doesn't include assembler so having it in asm-generic seems wrong. Move it into its own header. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/asm-generic/u-boot.h | 52 +-------------------------- include/bd.h | 70 ++++++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+), 51 deletions(-) create mode 100644 include/bd.h diff --git a/include/asm-generic/u-boot.h b/include/asm-generic/u-boot.h index 8c619c1b74a..1792165c6fa 100644 --- a/include/asm-generic/u-boot.h +++ b/include/asm-generic/u-boot.h @@ -15,56 +15,6 @@ #ifndef __ASM_GENERIC_U_BOOT_H__ #define __ASM_GENERIC_U_BOOT_H__ -/* - * Board information passed to Linux kernel from U-Boot - * - * include/asm-ppc/u-boot.h - */ - -#ifndef __ASSEMBLY__ - -#include <asm/types.h> -#include <linux/types.h> - -struct bd_info { - unsigned long bi_flashstart; /* start of FLASH memory */ - unsigned long bi_flashsize; /* size of FLASH memory */ - unsigned long bi_flashoffset; /* reserved area for startup monitor */ -#ifdef CONFIG_ARM - unsigned long bi_arm_freq; /* arm frequency */ - unsigned long bi_dsp_freq; /* dsp core frequency */ - unsigned long bi_ddr_freq; /* ddr frequency */ -#endif -#if defined(CONFIG_MPC8xx) || defined(CONFIG_E500) || defined(CONFIG_MPC86xx) - unsigned long bi_immr_base; /* base of IMMR register */ -#endif -#if defined(CONFIG_M68K) - unsigned long bi_mbar_base; /* base of internal registers */ -#endif -#if defined(CONFIG_MPC83xx) - unsigned long bi_immrbar; -#endif - unsigned long bi_bootflags; /* boot / reboot flag (Unused) */ - unsigned long bi_ip_addr; /* IP Address */ - unsigned short bi_ethspeed; /* Ethernet speed in Mbps */ - unsigned long bi_intfreq; /* Internal Freq, in MHz */ - unsigned long bi_busfreq; /* Bus Freq, in MHz */ -#if defined(CONFIG_M68K) - unsigned long bi_pcifreq; /* PCI Bus Freq, in MHz */ -#endif -#if defined(CONFIG_EXTRA_CLOCK) - unsigned long bi_inpfreq; /* input Freq in MHz */ - unsigned long bi_vcofreq; /* vco Freq in MHz */ - unsigned long bi_flbfreq; /* Flexbus Freq in MHz */ -#endif - ulong bi_arch_number; /* unique id for this board */ - ulong bi_boot_params; /* where this board expects params */ - struct { /* RAM configuration */ - phys_addr_t start; - phys_size_t size; - } bi_dram[CONFIG_NR_DRAM_BANKS]; -}; - -#endif /* __ASSEMBLY__ */ +#include <bd.h> #endif /* __ASM_GENERIC_U_BOOT_H__ */ diff --git a/include/bd.h b/include/bd.h new file mode 100644 index 00000000000..5fb52e26c31 --- /dev/null +++ b/include/bd.h @@ -0,0 +1,70 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2011 The Chromium OS Authors. + * + * (C) Copyright 2000 - 2002 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + ******************************************************************** + * NOTE: This header file defines an interface to U-Boot. Including + * this (unmodified) header file in another file is considered normal + * use of U-Boot, and does *not* fall under the heading of "derived + * work". + ******************************************************************** + */ + +#ifndef __ASM_GENERIC_BD_H__ +#define __ASM_GENERIC_BD_H__ + +/* + * Board information that used to be passed to Linux kernel from U-Boot + * + * include/asm-ppc/u-boot.h + */ + +#ifndef __ASSEMBLY__ + +#include <asm/types.h> +#include <linux/types.h> + +struct bd_info { + unsigned long bi_flashstart; /* start of FLASH memory */ + unsigned long bi_flashsize; /* size of FLASH memory */ + unsigned long bi_flashoffset; /* reserved area for startup monitor */ +#ifdef CONFIG_ARM + unsigned long bi_arm_freq; /* arm frequency */ + unsigned long bi_dsp_freq; /* dsp core frequency */ + unsigned long bi_ddr_freq; /* ddr frequency */ +#endif +#if defined(CONFIG_MPC8xx) || defined(CONFIG_E500) || defined(CONFIG_MPC86xx) + unsigned long bi_immr_base; /* base of IMMR register */ +#endif +#if defined(CONFIG_M68K) + unsigned long bi_mbar_base; /* base of internal registers */ +#endif +#if defined(CONFIG_MPC83xx) + unsigned long bi_immrbar; +#endif + unsigned long bi_bootflags; /* boot / reboot flag (Unused) */ + unsigned long bi_ip_addr; /* IP Address */ + unsigned short bi_ethspeed; /* Ethernet speed in Mbps */ + unsigned long bi_intfreq; /* Internal Freq, in MHz */ + unsigned long bi_busfreq; /* Bus Freq, in MHz */ +#if defined(CONFIG_M68K) + unsigned long bi_pcifreq; /* PCI Bus Freq, in MHz */ +#endif +#if defined(CONFIG_EXTRA_CLOCK) + unsigned long bi_inpfreq; /* input Freq in MHz */ + unsigned long bi_vcofreq; /* vco Freq in MHz */ + unsigned long bi_flbfreq; /* Flexbus Freq in MHz */ +#endif + ulong bi_arch_number; /* unique id for this board */ + ulong bi_boot_params; /* where this board expects params */ + struct { /* RAM configuration */ + phys_addr_t start; + phys_size_t size; + } bi_dram[CONFIG_NR_DRAM_BANKS]; +}; + +#endif /* __ASSEMBLY__ */ + +#endif /* __ASM_GENERIC_BD_H__ */ -- 2.43.0

From: Simon Glass <sjg@chromium.org> Each architecture will have its own version of the linker script, so use a Makefile variable for it. Signed-off-by: Simon Glass <sjg@chromium.org> --- Makefile | 5 +++-- arch/sandbox/config.mk | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 7011535bb2f..1a378ebbee3 100644 --- a/Makefile +++ b/Makefile @@ -1856,7 +1856,7 @@ endif # Build U-Boot as a shared library quiet_cmd_libu-boot.so = LD $@ cmd_libu-boot.so = $(CC) -shared -o $@ -Wl,--build-id=none \ - -Wl,-T,$(srctree)/arch/sandbox/cpu/u-boot-lib.lds \ + -Wl,-T,$(LIB_LDS) \ $(u-boot-init) \ $(KBUILD_LDFLAGS:%=-Wl,%) $(SANITIZERS) $(LTO_FINAL_LDFLAGS) \ -Wl,--whole-archive \ @@ -1865,7 +1865,8 @@ quiet_cmd_libu-boot.so = LD $@ -Wl,--no-whole-archive \ $(PLATFORM_LIBS) -Wl,-Map -Wl,libu-boot.map -libu-boot.so: $(u-boot-init) $(u-boot-main) $(u-boot-keep-syms-lto) FORCE +libu-boot.so: $(u-boot-init) $(u-boot-main) $(u-boot-keep-syms-lto) \ + $(LIB_LDS) FORCE $(call if_changed,libu-boot.so) # Build ulib_test that links with shared library diff --git a/arch/sandbox/config.mk b/arch/sandbox/config.mk index 405843800e9..566f5b417ae 100644 --- a/arch/sandbox/config.mk +++ b/arch/sandbox/config.mk @@ -69,3 +69,6 @@ EFI_LDS := ${SRCDIR}/../../../arch/riscv/lib/elf_riscv64_efi.lds endif EFI_CRT0 := crt0_sandbox_efi.o EFI_RELOC := reloc_sandbox_efi.o + +# U-Boot Library +LIB_LDS := $(srctree)/arch/sandbox/cpu/u-boot-lib.lds -- 2.43.0

From: Simon Glass <sjg@chromium.org> Sometimes it is more convenient to link an application directly with a static library. Add build rules to create libu-boot.a as a static library alongside the existing shared library (libu-boot.so). The static library is a fat archive containing all U-Boot object files except for arch/sandbox/cpu/main.o since it contains main(). A temporary thin archive is used to collect all the objects. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- Makefile | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Makefile b/Makefile index 1a378ebbee3..bf571de9f06 100644 --- a/Makefile +++ b/Makefile @@ -1046,6 +1046,7 @@ INPUTS-$(CONFIG_X86) += u-boot-x86-start16.bin u-boot-x86-reset16.bin \ ifdef CONFIG_CMDLINE ifneq ($(cc-name),clang) INPUTS-$(CONFIG_ULIB) += libu-boot.so test/ulib/ulib_test +INPUTS-$(CONFIG_ULIB) += libu-boot.a endif endif @@ -1869,6 +1870,20 @@ libu-boot.so: $(u-boot-init) $(u-boot-main) $(u-boot-keep-syms-lto) \ $(LIB_LDS) FORCE $(call if_changed,libu-boot.so) +# Build U-Boot as a static library +# Create a fat archive with all object files (except arch/sandbox/cpu/main.o) +# Avoid partial linking so as to preserve the linker-list sections +quiet_cmd_libu-boot.a = AR $@ + cmd_libu-boot.a = rm -f $@ $@.tmp $@.objlist; \ + $(AR) rcT $@.tmp $(u-boot-init) $(u-boot-main) \ + $(u-boot-keep-syms-lto); \ + $(AR) t $@.tmp | grep -v "arch/sandbox/cpu/main\.o$$" > $@.objlist; \ + cat $@.objlist | xargs $(AR) rcs $@; \ + rm -f $@.tmp $@.objlist + +libu-boot.a: $(u-boot-init) $(u-boot-main) $(u-boot-keep-syms-lto) FORCE + $(call if_changed,libu-boot.a) + # Build ulib_test that links with shared library quiet_cmd_ulib_test = HOSTCC $@ cmd_ulib_test = $(HOSTCC) $(HOSTCFLAGS) \ -- 2.43.0

From: Simon Glass <sjg@chromium.org> Provide a simple function to set up the library, to avoid having to worry about the details. This requires the caller to know about global_data Signed-off-by: Simon Glass <sjg@chromium.org> --- arch/sandbox/cpu/start.c | 30 +++++++++++++++++++---- arch/sandbox/include/asm/u-boot-sandbox.h | 9 +++++++ test/ulib/ulib_test.c | 12 +++------ 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/arch/sandbox/cpu/start.c b/arch/sandbox/cpu/start.c index 59682b039e5..556d1985ccc 100644 --- a/arch/sandbox/cpu/start.c +++ b/arch/sandbox/cpu/start.c @@ -693,13 +693,14 @@ int sandbox_init(int argc, char *argv[], struct global_data *data) return 0; } -int sandbox_main(int argc, char *argv[]) +static int sandbox_flow(int argc, char *argv[], struct global_data *data, + uint flags) { - gd_t data; int ret; - memset(&data, '\0', sizeof(data)); - ret = sandbox_init(argc, argv, &data); + memset(data, '\0', sizeof(struct global_data)); + data->flags = flags; + ret = sandbox_init(argc, argv, data); if (ret) goto err; @@ -708,10 +709,29 @@ int sandbox_main(int argc, char *argv[]) board_init_r(gd->new_gd, 0); - /* NOTREACHED - board_init_r() does not return */ return 0; err: printf("Error %d\n", ret); return 1; } + +int sandbox_main(int argc, char *argv[]) +{ + gd_t data; + + sandbox_flow(argc, argv, &data, 0); + + /* NOTREACHED - board_init_r() does not return */ + return 1; +} + +int ulib_init_with_data(char *progname, struct global_data *data) +{ + char *argv[] = {progname, NULL}; + int ret; + + ret = sandbox_flow(1, argv, data, GD_FLG_ULIB); + + return ret; +} diff --git a/arch/sandbox/include/asm/u-boot-sandbox.h b/arch/sandbox/include/asm/u-boot-sandbox.h index 941f35f9e69..859c2f08b4c 100644 --- a/arch/sandbox/include/asm/u-boot-sandbox.h +++ b/arch/sandbox/include/asm/u-boot-sandbox.h @@ -75,4 +75,13 @@ int sandbox_init(int argc, char *argv[], struct global_data *data); */ int sandbox_main(int argc, char *argv[]); +/** + * ulib_init_with_data() - set up the U-Boot library + * + * @progname: Program name to use, typically argv[0] + * @data: Global data (must remain valid until the program exits) + * Return: 0 if OK, -ve error code on error + */ +int ulib_init_with_data(char *progname, struct global_data *data); + #endif /* _U_BOOT_SANDBOX_H_ */ diff --git a/test/ulib/ulib_test.c b/test/ulib/ulib_test.c index 621bcd49be9..e1e863712a8 100644 --- a/test/ulib/ulib_test.c +++ b/test/ulib/ulib_test.c @@ -23,15 +23,9 @@ int main(int argc, char *argv[]) printf("Calling U-Boot initialization via shared library...\n"); - /* init global data */ - memset(&data, '\0', sizeof(data)); - data.flags = GD_FLG_ULIB; - - ret = sandbox_init(argc, argv, &data); - - /* Do pre- and post-relocation init */ - board_init_f(gd->flags); - board_init_r(data.new_gd, 0); + ret = ulib_init_with_data(argv[0], &data); + if (ret) + return 1; return ret; } -- 2.43.0

From: Simon Glass <sjg@chromium.org> Provide an arch-neutral init function that can be used by including a single header file. This declares a static global_data which is used for the initial startup process. Once board_init_r() is called, the global_data is moved into a new place and the static version is not needed. Signed-off-by: Simon Glass <sjg@chromium.org> --- include/u-boot-lib.h | 34 ++++++++++++++++++++++++++++++++++ lib/Makefile | 2 ++ lib/ulib.c | 36 ++++++++++++++++++++++++++++++++++++ test/ulib/ulib_test.c | 8 ++------ 4 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 include/u-boot-lib.h create mode 100644 lib/ulib.c diff --git a/include/u-boot-lib.h b/include/u-boot-lib.h new file mode 100644 index 00000000000..7157ef6ba60 --- /dev/null +++ b/include/u-boot-lib.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * U-Boot library interface + * + * This provides basic access to setup of the U-Boot library. + * + * Library functions must be individually accessed via their respective headers. + * + * Copyright 2025 Canonical + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#ifndef __U_BOOT_LIB_H +#define __U_BOOT_LIB_H + +struct global_data; + +/** + * ulib_init() - set up the U-Boot library + * + * @progname: Program name to use (must be a writeable string, typically argv[0]) + * @data: Global data (must remain valid until the program exits) + * Return: 0 if OK, -ve error code on error + */ +int ulib_init(char *progname); + +/** + * ulib_uninit() shut down the U-Boot librrary + * + * Call this when your program has finished using the library, before it exits + */ +void ulib_uninit(void); + +#endif diff --git a/lib/Makefile b/lib/Makefile index 1db1e3c9000..eb6da6d63c9 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -166,6 +166,8 @@ obj-$(CONFIG_LIB_ELF) += elf.o obj-$(CONFIG_$(PHASE_)SEMIHOSTING) += semihosting.o +obj-$(CONFIG_ULIB) += ulib.o + # # Build a fast OID lookup registry from include/linux/oid_registry.h # diff --git a/lib/ulib.c b/lib/ulib.c new file mode 100644 index 00000000000..03acbe93fd1 --- /dev/null +++ b/lib/ulib.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Simplified U-Boot library interface implementation + * + * Copyright 2025 Canonical + * Written by Simon Glass <simon.glass@canonical.com> + */ + +#include <string.h> +#include <version.h> +#include <asm/global_data.h> +#include <u-boot-lib.h> + +/* Static storage for global data when using simplified API */ +static struct global_data static_gd; + +int ulib_init(char *progname) +{ + int ret; + + /* Initialize the U-Boot library with our static global data */ + ret = ulib_init_with_data(progname, &static_gd); + if (ret) + return ret; + + return 0; +} + +void ulib_uninit(void) +{ +} + +const char *ulib_get_version(void) +{ + return PLAIN_VERSION; +} diff --git a/test/ulib/ulib_test.c b/test/ulib/ulib_test.c index e1e863712a8..a68290ff484 100644 --- a/test/ulib/ulib_test.c +++ b/test/ulib/ulib_test.c @@ -11,19 +11,15 @@ /* Use system headers, not U-Boot headers */ #include <stdio.h> #include <string.h> -#include <init.h> -#include <asm/global_data.h> - -DECLARE_GLOBAL_DATA_PTR; +#include <u-boot-lib.h> int main(int argc, char *argv[]) { - struct global_data data; int ret; printf("Calling U-Boot initialization via shared library...\n"); - ret = ulib_init_with_data(argv[0], &data); + ret = ulib_init(argv[0]); if (ret) return 1; -- 2.43.0

From: Simon Glass <sjg@chromium.org> Create a static version of the library test. This requires a linker script, since the linker lists much be correctly placed within the final executable. Provide a linker script for the sandbox version. Signed-off-by: Simon Glass <sjg@chromium.org> --- Makefile | 18 +++++++++++++++++- arch/sandbox/config.mk | 1 + arch/sandbox/cpu/ulib-test-static.lds | 19 +++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 arch/sandbox/cpu/ulib-test-static.lds diff --git a/Makefile b/Makefile index bf571de9f06..3ece9a02b6c 100644 --- a/Makefile +++ b/Makefile @@ -1046,7 +1046,7 @@ INPUTS-$(CONFIG_X86) += u-boot-x86-start16.bin u-boot-x86-reset16.bin \ ifdef CONFIG_CMDLINE ifneq ($(cc-name),clang) INPUTS-$(CONFIG_ULIB) += libu-boot.so test/ulib/ulib_test -INPUTS-$(CONFIG_ULIB) += libu-boot.a +INPUTS-$(CONFIG_ULIB) += libu-boot.a test/ulib/ulib_test_static endif endif @@ -1893,6 +1893,22 @@ quiet_cmd_ulib_test = HOSTCC $@ test/ulib/ulib_test: test/ulib/ulib_test.o libu-boot.so FORCE $(call if_changed,ulib_test) +# Build ulib_test_static to test linking with the static library +# main.o is excluded from the static library since the main program is provided +# by the user +# Use --whole-archive to include all linker lists +# Use a linker script to ensure proper alignment of linker-lists +quiet_cmd_ulib_test_static = HOSTCC $@ + cmd_ulib_test_static = $(HOSTCC) $(HOSTCFLAGS) \ + -I$(srctree)/arch/sandbox/include -o $@ $< \ + -Wl,-T,$(LIB_STATIC_LDS) \ + -Wl,--whole-archive $(obj)/libu-boot.a -Wl,--no-whole-archive \ + -lpthread -ldl -lSDL2 -lrt -Wl,-z,noexecstack + +test/ulib/ulib_test_static: test/ulib/ulib_test.o libu-boot.a \ + $(LIB_STATIC_LDS) FORCE + $(call if_changed,ulib_test_static) + quiet_cmd_sym ?= SYM $@ cmd_sym ?= $(OBJDUMP) -t $< > $@ u-boot.sym: u-boot FORCE diff --git a/arch/sandbox/config.mk b/arch/sandbox/config.mk index 566f5b417ae..f80e2ef369f 100644 --- a/arch/sandbox/config.mk +++ b/arch/sandbox/config.mk @@ -72,3 +72,4 @@ EFI_RELOC := reloc_sandbox_efi.o # U-Boot Library LIB_LDS := $(srctree)/arch/sandbox/cpu/u-boot-lib.lds +LIB_STATIC_LDS := $(srctree)/arch/sandbox/cpu/ulib-test-static.lds diff --git a/arch/sandbox/cpu/ulib-test-static.lds b/arch/sandbox/cpu/ulib-test-static.lds new file mode 100644 index 00000000000..c400fba4f2b --- /dev/null +++ b/arch/sandbox/cpu/ulib-test-static.lds @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Linker script for ulib_test_static binary + * + * This ensures proper alignment for linker-lists when linking with libu-boot.a + */ + +SECTIONS +{ + /* Ensure proper alignment for linker lists */ + . = ALIGN(32); + __u_boot_list : { + __u_boot_list_start = .; + KEEP(*(SORT(__u_boot_list*))); + __u_boot_list_end = .; + } +} + +INSERT AFTER .rodata; -- 2.43.0

From: Simon Glass <sjg@chromium.org> We now have rules in the main Makefile to ensure that the library is built before the ulib test-binaries. Drop the unnecessary dependency. Also the link is done entirely in the main Makefile, so drop the flags as well. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/ulib/Makefile | 7 ------- 1 file changed, 7 deletions(-) diff --git a/test/ulib/Makefile b/test/ulib/Makefile index ae58e01fccd..b6c3df38c9b 100644 --- a/test/ulib/Makefile +++ b/test/ulib/Makefile @@ -3,10 +3,3 @@ # Copyright 2025 Simon Glass <sjg@chromium.org> obj-y += ulib_test.o - -# Link with the shared library -HOSTCFLAGS_ulib_test.o += -I$(srctree)/arch/sandbox/include -HOSTLDLIBS_ulib_test += -L$(obj)/../.. -lu-boot -Wl,-rpath,$(obj)/../.. - -# Ensure shared library is built first -$(obj)/ulib_test: $(obj)/../../libu-boot.so -- 2.43.0

From: Simon Glass <sjg@chromium.org> When the test is run, indicate which library the test was linked against, static or dynamic. Drop the printf() at the start of main(). Since all printf() calls go to U-Boot, this message is not shown anyway, since the global-data pointer gd is NULL at this point and console_puts() just returns immediately. Signed-off-by: Simon Glass <sjg@chromium.org> --- test/ulib/ulib_test.c | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/test/ulib/ulib_test.c b/test/ulib/ulib_test.c index a68290ff484..51b1c62e6b2 100644 --- a/test/ulib/ulib_test.c +++ b/test/ulib/ulib_test.c @@ -2,7 +2,7 @@ /* * Test application for U-Boot shared library * - * This demonstrates linking against libu-boot.so + * This demonstrates linking against libu-boot.so and libu-boot.a * * Copyright 2025 Canonical * Written by Simon Glass <simon.glass@canonical.com> @@ -11,17 +11,50 @@ /* Use system headers, not U-Boot headers */ #include <stdio.h> #include <string.h> + +#include <os.h> #include <u-boot-lib.h> +/* Runtime detection of link type using /proc/self/maps */ +static const char *detect_link_type(void) +{ + char line[512]; + int fd; + int found_libuboot = 0; + + /* Open /proc/self/maps to check loaded libraries */ + fd = os_open("/proc/self/maps", 0); + if (fd < 0) + return "unable to detect linkage"; + + /* Read line by line to avoid boundary issues */ + while (os_fgets(line, sizeof(line), fd)) { + if (strstr(line, "libu-boot.so")) { + found_libuboot = 1; + break; + } + } + + os_close(fd); + + /* Return appropriate message based on what we found */ + if (found_libuboot) + return "dynamically linked (uses libu-boot.so)"; + else + return "statically linked (uses libu-boot.a)"; +} + int main(int argc, char *argv[]) { int ret; - printf("Calling U-Boot initialization via shared library...\n"); - ret = ulib_init(argv[0]); if (ret) return 1; + printf("Hello, world\n"); + printf("\n- U-Boot\n"); + printf("\nPS: This program is %s\n", detect_link_type()); + return ret; } -- 2.43.0

From: Simon Glass <sjg@chromium.org> Provide a NO_LIB option which disables building the libraries. This saves a little time. Signed-off-by: Simon Glass <sjg@chromium.org> --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 3ece9a02b6c..2c4685d161c 100644 --- a/Makefile +++ b/Makefile @@ -1045,10 +1045,12 @@ INPUTS-$(CONFIG_X86) += u-boot-x86-start16.bin u-boot-x86-reset16.bin \ ifdef CONFIG_CMDLINE ifneq ($(cc-name),clang) +ifeq ($(NO_LIBS),) INPUTS-$(CONFIG_ULIB) += libu-boot.so test/ulib/ulib_test INPUTS-$(CONFIG_ULIB) += libu-boot.a test/ulib/ulib_test_static endif endif +endif LDFLAGS_u-boot += $(LDFLAGS_FINAL) -- 2.43.0

From: Simon Glass <sjg@chromium.org> Add documentation for the U-Boot Library (ulib) feature, explaining how to build and use both shared (.so) and static (.a) libraries. Co-developed-by: Claude <noreply@anthropic.com> Signed-off-by: Simon Glass <sjg@chromium.org> --- doc/develop/index.rst | 1 + doc/develop/ulib.rst | 253 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 doc/develop/ulib.rst diff --git a/doc/develop/index.rst b/doc/develop/index.rst index 592736331f2..12a71b5b23a 100644 --- a/doc/develop/index.rst +++ b/doc/develop/index.rst @@ -56,6 +56,7 @@ Implementation falcon std_passage uefi/index + ulib vbe version diff --git a/doc/develop/ulib.rst b/doc/develop/ulib.rst new file mode 100644 index 00000000000..559f170ce42 --- /dev/null +++ b/doc/develop/ulib.rst @@ -0,0 +1,253 @@ +.. SPDX-License-Identifier: GPL-2.0+ + +U-Boot Library (ulib) +===================== + +The U-Boot Library (ulib) allows U-Boot to be built as a shared or static +library that can be used by external programs. This enables reuse of U-Boot +functionality in test programs and other applications without needing to +build that functionality directly into U-Boot image. + +Please read `License Implications`_ below. + +Building the Libraries +---------------------- + +For now the library is only available for :doc:`/arch/sandbox/sandbox`, i.e. for +running natively on a host machine. Further work will extend that to other +architectures supported by U-Boot. + +To build U-Boot as a library (libu-boot.so):: + + make sandbox_defconfig + make [-s] -j$(nproc) + +Use ``-s`` if you just want to see warnings / errors. This produces two files: + +``libu-boot.so`` + This is the shared library, sometimes called a dynamically linked library. + Programs which need it can be dynamically linked with this library at + runtime. This provides: + + - Smaller executable size for linked programs + - Runtime dependency on the .so file + - Must set LD_LIBRARY_PATH or use rpath for runtime linking + - Suitable for development and testing + +``libu-boot.a`` + This is a static library, meaning that it is directly linked with your + program and the resulting executable includes the U-Boot code. This + provides: + + - Larger executable size (includes all code) + - No runtime dependencies + - Self-contained executables + - Suitable for distribution and embedded systems + +To disable creation of the library (e.g. to speed up the build), provide +NO_LIBS=1 in the environment:: + + NO_LIBS=1 make -j$(nproc) + +Simple test programs are provided for each library. + +For dynamic linking:: + + $ LD_LIBRARY_PATH=. ./test/ulib/ulib_test + Hello, world + + - U-Boot + + PS: This program is dynamically linked (uses libu-boot.so) + +For static linking:: + + $ ./test/ulib/ulib_test_static + Hello, world + + - U-Boot + + PS: This program is statically linked (uses libu-boot.a) + + +Architecture Notes +------------------ + +Both libraries exclude ``arch/sandbox/cpu/main.o`` which contains the +``main()`` function. This allows the linking program to provide its own +main entry point while still using U-Boot functionality. + +The libraries preserve U-Boot's linker lists, which are essential for +driver registration and other U-Boot subsystems. + +Building outside the U-Boot tree +-------------------------------- + +This is possible, but as soon as you want to call a function that is not in +u-boot-lib.h you will have problems, as described in the following sections. + +This will be addressed with future work. + +Including U-Boot header files from outside +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +U-Boot has many header files, some of which are arch-specific. These are +typically including via:: + + #include <asm/...> + +and are located in the ```arch/<arch>/include/asm/...`` directory within the +U-Boot source tree. You will need to ensure that this directory is present in +the include path. + + +Kconfig options +~~~~~~~~~~~~~~~ + +There is currently no mechanism to make use of the Kconfig options used to +build the U-Boot library. It is possible to add `-include kconfig.h` to your +build, but for this to work more generally, the header file containing the +CONFIG settings would need to be exported from the build and packaged with the +library. + + +Test Programs +------------- + +U-Boot includes test programs that demonstrate library usage: + +* ``test/ulib/ulib_test`` - Uses the shared library +* ``test/ulib/ulib_test_static`` - Uses the static library + +These are built by default with the sandbox build. + +Run the shared library version:: + + ./test/ulib/ulib_test + +Run the static library version:: + + ./test/ulib/ulib_test_static + +Linking and the Linker Script +----------------------------- + +For the static library, a custom linker script is needed to ensure proper +section alignment, particularly for U-Boot linker lists. See +``arch/sandbox/cpu/ulib-test-static.lds`` for an example. + +The linker script ensures: + +* Proper alignment of the ``__u_boot_list`` section (32-byte alignment) +* Correct ordering of sandbox-specific sections +* Preservation of linker list entries with ``KEEP()`` directives + +For this to work, the library must be linked using a 'whole archive' approach, +for example:: + + -Wl,--whole-archive $(obj)/libu-boot.a -Wl,--no-whole-archive + +Failure to do either of these will result in strange errors, such as running +out of memory or simple crashes during library init. + +Dependencies +------------ + +When linking with the U-Boot library for sanbod, you may need these system +libraries: + +* ``pthread`` - POSIX threads +* ``dl`` - Dynamic linking support +* ``SDL2`` - For sandbox display emulation (optional) +* ``rt`` - Real-time extensions + +Troubleshooting +--------------- + +Missing Symbols +~~~~~~~~~~~~~~~ + +If you encounter undefined symbol errors when linking: + +1. For static library, ensure you're using ``--whole-archive`` +2. Check that required system libraries are linked +3. Some symbols may need to be defined with ``--defsym``: + + - ``__stack_chk_guard`` - Stack protection guard + - ``sandbox_sdl_sync`` - SDL synchronization (can be set to 0 if unused) + +Linker List Issues +~~~~~~~~~~~~~~~~~~ + +If U-Boot subsystems don't initialize properly: + +1. Check linker list alignment with:: + + scripts/check_linker_lists.py /path/to/executable + +2. For static linking, use ``--whole-archive`` to include all sections +3. Use a custom linker script for proper section organization + +Runtime Errors +~~~~~~~~~~~~~~ + +For shared library programs: + +1. Ensure ``LD_LIBRARY_PATH`` includes the directory with ``libu-boot.so`` +2. Or use ``-Wl,-rpath`` when linking to embed the library path +3. Check library dependencies with ``ldd myapp`` + + +License Implications +-------------------- + +U-Boot is licensed under GPL-2.0+ (GNU General Public License version 2 or later). +This has important implications when linking against the U-Boot library: + +* **Static Linking (libu-boot.a)**: + + - Your program becomes a derivative work of U-Boot + - The entire combined work must be distributed under GPL-2.0+ terms + - You must provide source code for your entire application + - All code linked with the static library must be GPL-compatible + +* **Dynamic Linking (libu-boot.so)**: + + - The GPL interpretation for dynamic linking is legally complex + - Conservative interpretation: still creates a derivative work requiring GPL + - Some jurisdictions may treat dynamic linking differently + - Consult legal counsel for commercial applications + +* **Compliance Requirements**: + + - Provide a GPL-2.0+ license notice + - Make source code available (including your modifications) + - Include copyright notices from U-Boot + - Provide build instructions to reproduce the binary + +* **Alternative Approaches**: + + - Consider using U-Boot functionality via separate processes (IPC) + - Implement a clean-room alternative for needed functionality + - Request dual-licensing from all U-Boot contributors (impractical) + +**Note**: This is not legal advice. Always consult with legal professionals +when using GPL-licensed code in your products, especially for commercial use. + +Limitations +----------- + +* Currently only supported for sandbox architecture +* Network-subsystem init is disabled in library mode +* Main-loop functionality is disabled to prevent interference with the host + program +* EFI runtime-services and relocation are disabled + +Future Work +----------- + +* Support for other architectures beyond sandbox +* Selective inclusion of U-Boot subsystems +* API versioning and stability guarantees +* pkg-config support for easier integration +* Support for calling functions in any U-Boot header -- 2.43.0
participants (1)
-
Simon Glass