boot: Bring back bootloader builds

This adds back sd-boot builds by using meson compile targets directly.
We can do this now, because userspace binaries use the special
dependency that allows us to easily separate flags, so that we don't
pass anything to EFI builds that shouldn't be passed.

Additionally, we pass a bunch of flags to hopefully disable/override any
distro provided flags that should not be used for EFI binaries.

Fixes: #12275
This commit is contained in:
Jan Janssen
2023-02-27 16:54:48 +01:00
parent dfca5587cf
commit 2afeaf1675
10 changed files with 843 additions and 18 deletions

1
README
View File

@@ -216,6 +216,7 @@ REQUIREMENTS:
awk, sed, grep, and similar tools
clang >= 10.0, llvm >= 10.0 (optional, required to build BPF programs
from source code in C)
pyelftools (optional, required for systemd-boot)
During runtime, you need the following additional
dependencies:

View File

@@ -1972,11 +1972,28 @@ if get_option('bootloader') != 'false' and efi_arch != ''
elif get_option('bootloader') == 'true' and efi_arch == ''
error('EFI not supported for this arch.')
endif
efi_arch_alt = ''
if efi_arch == 'x64' and cc.links('''
#include <limits.h>
int main(int argc, char *argv[]) {
return __builtin_popcount(argc - CHAR_MAX);
}''', args : ['-m32', '-march=i686'], name : '32bit build possible')
efi_arch_alt = 'ia32'
endif
have_pyelftools = pymod.find_installation('python3', required : false, modules : ['elftools']).found()
if get_option('bootloader') == 'true' and (not python_39 or not have_pyelftools)
error('EFI bootloader support requires Python >= 3.9 and pyelftools.')
endif
conf.set10(
'ENABLE_BOOTLOADER',
get_option('efi') and
get_option('bootloader') in ['auto', 'true'] and
efi_arch != '',
efi_arch != '' and
python_39 and
have_pyelftools,
)
if get_option('ukify') == 'auto'
@@ -1990,18 +2007,20 @@ conf.set10('ENABLE_UKIFY', want_ukify)
############################################################
#
elf2efi_lds = project_source_root / 'tools/elf2efi.lds'
elf2efi_py = find_program('tools/elf2efi.py')
export_dbus_interfaces_py = find_program('tools/dbus_exporter.py')
generate_gperfs = find_program('tools/generate-gperfs.py')
make_autosuspend_rules_py = find_program('tools/make-autosuspend-rules.py')
make_directive_index_py = find_program('tools/make-directive-index.py')
make_man_index_py = find_program('tools/make-man-index.py')
meson_render_jinja2 = find_program('tools/meson-render-jinja2.py')
update_dbus_docs_py = find_program('tools/update-dbus-docs.py')
update_man_rules_py = find_program('tools/update-man-rules.py')
update_hwdb_sh = find_program('tools/update-hwdb.sh')
update_hwdb_autosuspend_sh = find_program('tools/update-hwdb-autosuspend.sh')
update_hwdb_sh = find_program('tools/update-hwdb.sh')
update_man_rules_py = find_program('tools/update-man-rules.py')
update_syscall_tables_sh = find_program('tools/update-syscall-tables.sh')
xml_helper_py = find_program('tools/xml_helper.py')
export_dbus_interfaces_py = find_program('tools/dbus_exporter.py')
############################################################

View File

@@ -21,6 +21,7 @@
#include "shim.h"
#include "ticks.h"
#include "util.h"
#include "version.h"
#include "vmm.h"
/* Magic string for recognizing our own binaries */

View File

@@ -126,7 +126,7 @@ _gnu_printf_(2, 0) _warn_unused_result_ char16_t *xvasprintf_status(EFI_STATUS s
/* inttypes.h is provided by libc instead of the compiler and is not supposed to be used in freestanding
* environments. We could use clang __*_FMT*__ constants for this, bug gcc does not have them. :( */
# if defined(__ILP32__) || defined(__arm__)
# if defined(__ILP32__) || defined(__arm__) || defined(__i386__)
# define PRI64_PREFIX "ll"
# elif defined(__LP64__)
# define PRI64_PREFIX "l"

View File

@@ -57,7 +57,7 @@ elif get_option('sbat-distro') != ''
endif
endif
summary({'UEFI architecture' : efi_arch},
summary({'UEFI architectures' : efi_arch + (efi_arch_alt == '' ? '' : ', ' + efi_arch_alt)},
section : 'UEFI')
if efi_conf.get('SBAT_DISTRO', '') != ''
@@ -76,6 +76,97 @@ configure_file(
############################################################
efi_includes = [fundamental_include, include_directories('.')]
efi_c_args = [
'-DSD_BOOT=1',
'-ffreestanding',
'-fno-strict-aliasing',
'-fshort-wchar',
'-include', 'efi_config.h',
]
efi_c_args += cc.get_supported_arguments(
'-fwide-exec-charset=UCS2',
# gcc docs says this is required for ms_abi to work correctly.
'-maccumulate-outgoing-args',
)
efi_c_ld_args = [
# We only support bfd. gold is going away, lld has issues with LTO on x86
# and mold does not support linker scripts.
'-fuse-ld=bfd',
'-lgcc',
'-nostdlib',
'-static-pie',
'-Wl,--entry=efi_main',
'-Wl,--fatal-warnings',
# These flags should be passed by -static-pie, but seem to be missing sometimes.
'-Wl,--no-dynamic-linker',
'-z', 'text',
# EFI has 4KiB pages.
'-z', 'common-page-size=4096',
'-z', 'max-page-size=4096',
'-z', 'noexecstack',
'-z', 'norelro',
'-T' + elf2efi_lds,
]
# efi_c_args is explicitly passed to targets so that they can override distro-provided flags
# that should not be used for EFI binaries.
efi_disabled_c_args = cc.get_supported_arguments(
'-fcf-protection=none',
'-fno-asynchronous-unwind-tables',
'-fno-exceptions',
'-fno-trapv',
'-fno-sanitize=all',
'-fno-stack-clash-protection',
'-fno-stack-protector',
'-fno-unwind-tables',
)
efi_c_args += efi_disabled_c_args
efi_c_ld_args += efi_disabled_c_args
efi_override_options = [
'b_coverage=false',
'b_pgo=off',
'b_sanitize=none',
]
if cc.get_id() == 'clang'
# clang is too picky sometimes.
efi_c_args += '-Wno-unused-command-line-argument'
efi_c_ld_args += '-Wno-unused-command-line-argument'
endif
if host_machine.cpu_family() == 'arm'
# libgcc is not compiled with -fshort-wchar, but it does not use it anyways,
# so it's fine to link against it.
efi_c_ld_args += '-Wl,--no-wchar-size-warning'
endif
efi_c_args_primary = [efi_c_args, '-DEFI_MACHINE_TYPE_NAME="' + efi_arch + '"']
efi_c_args_alt = [efi_c_args, '-DEFI_MACHINE_TYPE_NAME="' + efi_arch_alt + '"']
efi_c_ld_args_primary = efi_c_ld_args
efi_c_ld_args_alt = efi_c_ld_args
efi_c_args_primary += {
'aarch64' : ['-mgeneral-regs-only'],
'arm' : ['-mgeneral-regs-only'],
'x86_64' : ['-march=x86-64', '-mno-red-zone', '-mgeneral-regs-only'],
'x86' : ['-march=i686', '-mgeneral-regs-only'],
}.get(host_machine.cpu_family(), [])
if efi_arch_alt == 'ia32'
efi_c_args_alt += ['-m32', '-march=i686', '-mgeneral-regs-only']
efi_c_ld_args_alt += '-m32'
endif
############################################################
libefi_sources = files(
'console.c',
'device-path-util.c',
@@ -155,3 +246,86 @@ if host_machine.cpu_family() in ['aarch64', 'arm', 'x86_64', 'x86']
},
]
endif
boot_targets = []
efi_elf_binaries = []
efi_archspecs = [
{
'arch' : efi_arch,
'c_args' : efi_c_args_primary,
'link_args' : efi_c_ld_args_primary,
},
]
if efi_arch_alt != ''
efi_archspecs += {
'arch' : efi_arch_alt,
'c_args' : efi_c_args_alt,
'link_args' : efi_c_ld_args_alt,
}
endif
foreach archspec : efi_archspecs
libefi = static_library(
'efi' + archspec['arch'],
fundamental_sources,
libefi_sources,
include_directories : efi_includes,
c_args : archspec['c_args'],
dependencies : versiondep,
gnu_symbol_visibility : 'hidden',
override_options : efi_override_options,
pic : true)
efi_elf_binaries += executable(
'systemd-boot' + archspec['arch'],
systemd_boot_sources,
include_directories : efi_includes,
c_args : archspec['c_args'],
link_args : archspec['link_args'],
link_with : libefi,
link_depends : elf2efi_lds,
dependencies : versiondep,
gnu_symbol_visibility : 'hidden',
override_options : efi_override_options,
name_suffix : 'elf',
pie : true)
efi_elf_binaries += executable(
'linux' + archspec['arch'],
stub_sources,
include_directories : efi_includes,
c_args : archspec['c_args'],
link_args : archspec['link_args'],
link_with : libefi,
link_depends : elf2efi_lds,
dependencies : versiondep,
gnu_symbol_visibility : 'hidden',
override_options : efi_override_options,
name_suffix : 'elf.stub',
pie : true)
endforeach
foreach efi_elf_binary : efi_elf_binaries
# FIXME: Use build_tgt.name() with meson >= 0.54.0
name = fs.name(efi_elf_binary.full_path()).split('.')[0]
name += name.startswith('linux') ? '.efi.stub' : '.efi'
boot_targets += custom_target(
name,
output : name,
input : efi_elf_binary,
install : true,
install_dir : bootlibdir,
install_tag : 'systemd-boot',
command : [
elf2efi_py,
'--version-major=' + meson.project_version(),
'--version-minor=0',
'--efi-major=1',
'--efi-minor=1',
'--subsystem=10',
'@INPUT@',
'@OUTPUT@',
])
endforeach
alias_target('systemd-boot', boot_targets)

View File

@@ -14,6 +14,7 @@
#include "splash.h"
#include "tpm-pcr.h"
#include "util.h"
#include "version.h"
#include "vmm.h"
/* magic string to find in the binary image */

View File

@@ -168,18 +168,18 @@ void hexdump(const char16_t *prefix, const void *data, size_t size);
# define notify_debugger(i, w)
#endif
#define DEFINE_EFI_MAIN_FUNCTION(func, identity, wait_for_debugger) \
EFI_SYSTEM_TABLE *ST; \
EFI_BOOT_SERVICES *BS; \
EFI_RUNTIME_SERVICES *RT; \
EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *system_table) { \
ST = system_table; \
BS = system_table->BootServices; \
RT = system_table->RuntimeServices; \
notify_debugger((identity), (wait_for_debugger)); \
EFI_STATUS err = func(image); \
log_wait(); \
return err; \
#define DEFINE_EFI_MAIN_FUNCTION(func, identity, wait_for_debugger) \
EFI_SYSTEM_TABLE *ST; \
EFI_BOOT_SERVICES *BS; \
EFI_RUNTIME_SERVICES *RT; \
EFIAPI EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *system_table) { \
ST = system_table; \
BS = system_table->BootServices; \
RT = system_table->RuntimeServices; \
notify_debugger((identity), (wait_for_debugger)); \
EFI_STATUS err = func(image); \
log_wait(); \
return err; \
}
#if defined(__i386__) || defined(__x86_64__)

View File

@@ -1,5 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "version.h"
#ifdef SBAT_DISTRO
# define SBAT_SECTION_TEXT \
"sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md\n" \

49
tools/elf2efi.lds Normal file
View File

@@ -0,0 +1,49 @@
SECTIONS {
/* We skip the first page because the space will be occupied by the PE headers after conversion. */
. = CONSTANT(MAXPAGESIZE);
.text ALIGN(CONSTANT(MAXPAGESIZE)) : {
*(.text .text.*)
}
.rodata ALIGN(CONSTANT(MAXPAGESIZE)) : {
*(.rodata .rodata.*)
*(.srodata .srodata.*)
}
.data ALIGN(CONSTANT(MAXPAGESIZE)) : {
*(.data .data.*)
*(.sdata .sdata.*)
*(.got .got.*)
*(.got.plt .got.plt.*)
/* EDK2 says some firmware cannot handle BSS sections properly. */
*(.bss .bss.*)
*(.sbss .sbss.*)
*(COMMON)
}
.sdmagic ALIGN(CONSTANT(MAXPAGESIZE)) : { *(.sdmagic) }
.osrel ALIGN(CONSTANT(MAXPAGESIZE)) : { *(.osrel) }
.sbat ALIGN(CONSTANT(MAXPAGESIZE)) : { *(.sbat) }
/* These are used for PE conversion and then discarded. */
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.dynamic : { *(.dynamic) }
.rel.dyn : { *(.rel.dyn) }
.rela.dyn : { *(.rela.dyn) }
/* These aren't needed and could be discarded. Just in case that they're useful to the debugger
* we keep them, but move them out of the way to keep the PE binary more compact. */
.ARM.exidx : { *(.ARM.exidx) }
.eh_frame : { *(.eh_frame) }
.eh_frame_hdr : { *(.eh_frame_hdr) }
.gnu.hash : { *(.gnu.hash) }
.hash : { *(.hash) }
.note.gnu.build-id : { *(.note.gnu.build-id ) }
/DISCARD/ : {
*(.ARM.attributes)
*(.comment)
*(.note.*)
*(.riscv.attributes)
}
}

578
tools/elf2efi.py Executable file

File diff suppressed because it is too large Load Diff