From fe330f02dfebbaa462e0f4590de1049e47da54b9 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Mon, 8 Nov 2021 13:43:55 +0100 Subject: [PATCH 1/4] sd-boot: Let the compiler invoke the linker for us For LTO to work, the linker has to be called with some magic sauce arguments. And the easiest way to get those is to just let the compiler to the job for us. --- meson_options.txt | 4 ++- src/boot/efi/meson.build | 60 ++++++++++++++++++++++++++-------------- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/meson_options.txt b/meson_options.txt index 1e91bf1fd2..e4b85c73ee 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -411,7 +411,9 @@ option('gnu-efi', type : 'combo', choices : ['auto', 'true', 'false'], description : 'gnu-efi support for sd-boot') option('efi-cc', type : 'array', description : 'the compiler to use for EFI modules') -option('efi-ld', type : 'string', value : 'ld', +# Note that LLD does not support PE/COFF relocations +# https://lists.llvm.org/pipermail/llvm-dev/2021-March/149234.html +option('efi-ld', type : 'combo', choices : ['bfd', 'gold'], description : 'the linker to use for EFI modules') option('efi-libdir', type : 'string', description : 'path to the EFI lib directory') diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 17407281bb..b8001d1c65 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -48,14 +48,6 @@ if efi_cc.length() == 0 efi_cc = cc.cmd_array() endif -efi_ld = find_program(get_option('efi-ld')) -efi_ld_name = efi_ld.path().split('/')[-1] -if efi_ld_name == 'lld' or efi_ld_name == 'ld.lld' - # LLVM/LLD does not support PE/COFF relocations - # https://lists.llvm.org/pipermail/llvm-dev/2021-March/149234.html - error('LLVM/lld does not support PE/COFF relocations. Use different linker for EFI image.') -endif - efi_libdir = '' foreach dir : [get_option('efi-libdir'), '/usr/lib/gnuefi' / efi_arch[0], @@ -260,26 +252,52 @@ foreach arg : get_option('c_args') endif endforeach -efi_ldflags = ['-T', efi_lds, - '-shared', - '-Bsymbolic', - '-nostdlib', - '--no-undefined', - '--warn-common', - '--fatal-warnings', - '-znocombreloc', - '--build-id=sha1', - '-L', efi_libdir, - efi_crt0] +efi_ldflags = [ + '-fuse-ld=' + get_option('efi-ld'), + '-L', efi_libdir, + '-nostdlib', + '-shared', + '-T', efi_lds, + '-Wl,--build-id=sha1', + '-Wl,--fatal-warnings', + '-Wl,--no-undefined', + '-Wl,--warn-common', + '-Wl,-Bsymbolic', + '-z', 'nocombreloc', + efi_crt0, +] if efi_arch[1] in ['aarch64', 'arm', 'riscv64'] # Aarch64, ARM32 and 64bit RISC-V don't have an EFI capable objcopy. # Use 'binary' instead, and add required symbols manually. - efi_ldflags += ['--defsym=EFI_SUBSYSTEM=0xa'] + efi_ldflags += ['-Wl,--defsym=EFI_SUBSYSTEM=0xa'] efi_format = ['-O', 'binary'] else efi_format = ['--target=efi-app-@0@'.format(efi_arch[1])] endif +if run_command('grep', '-q', '__CTOR_LIST__', efi_lds).returncode() == 0 + # fedora has a patched gnu-efi that adds support for ELF constructors. + # If ld is called by gcc something about these symbols breaks, resulting + # in sd-boot freezing when gnu-efi runs the constructors. Force defining + # them seems to work around this. + efi_ldflags += [ + '-Wl,--defsym=_init_array=0', + '-Wl,--defsym=_init_array_end=0', + '-Wl,--defsym=_fini_array=0', + '-Wl,--defsym=_fini_array_end=0', + '-Wl,--defsym=__CTOR_LIST__=0', + '-Wl,--defsym=__CTOR_END__=0', + '-Wl,--defsym=__DTOR_LIST__=0', + '-Wl,--defsym=__DTOR_END__=0', + ] +endif + +efi_cc_version = run_command(efi_cc, '--version').stdout().split('\n')[0] +if efi_cc_version.contains('clang') and efi_cc_version.split('.')[0].split(' ')[-1].to_int() <= 10 + # clang <= 10 doesn't pass -T to the linker and then even complains about it being unused + efi_ldflags += ['-Wl,-T,' + efi_lds, '-Wno-unused-command-line-argument'] +endif + systemd_boot_objects = [] stub_objects = [] foreach file : fundamental_source_paths + common_sources + systemd_boot_sources + stub_sources @@ -308,7 +326,7 @@ foreach tuple : [['systemd_boot.so', systemd_boot_efi_name, systemd_boot_objects tuple[0], input : tuple[2], output : tuple[0], - command : [efi_ld, '-o', '@OUTPUT@', efi_ldflags, tuple[2], '-lefi', '-lgnuefi', libgcc_file_name], + command : [efi_cc, '-o', '@OUTPUT@', efi_ldflags, tuple[2], '-lefi', '-lgnuefi', libgcc_file_name], install : tuple[3], install_dir : bootlibdir) From 0e3bcb02d2b20a9f3ed4fac6b337815807d616c4 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Mon, 29 Nov 2021 14:27:48 +0100 Subject: [PATCH 2/4] sd-boot: Let compiler figure out libgcc location Since we now let the compiler call the linker for us, we can just rely on it to find the right (static) libgcc to use. --- src/boot/efi/meson.build | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index b8001d1c65..4460af61f8 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -314,7 +314,6 @@ foreach file : fundamental_source_paths + common_sources + systemd_boot_sources endif endforeach -libgcc_file_name = run_command(efi_cc + ['-print-libgcc-file-name']).stdout().strip() systemd_boot_efi_name = 'systemd-boot@0@.efi'.format(efi_arch[0]) stub_elf_name = 'linux@0@.elf.stub'.format(efi_arch[0]) stub_efi_name = 'linux@0@.efi.stub'.format(efi_arch[0]) @@ -326,7 +325,7 @@ foreach tuple : [['systemd_boot.so', systemd_boot_efi_name, systemd_boot_objects tuple[0], input : tuple[2], output : tuple[0], - command : [efi_cc, '-o', '@OUTPUT@', efi_ldflags, tuple[2], '-lefi', '-lgnuefi', libgcc_file_name], + command : [efi_cc, '-o', '@OUTPUT@', efi_ldflags, tuple[2], '-lefi', '-lgnuefi', '-lgcc'], install : tuple[3], install_dir : bootlibdir) From b848b7e166e476d7113e4ac836212e23b0ae4525 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Wed, 10 Nov 2021 13:30:23 +0100 Subject: [PATCH 3/4] sd-boot: Add LTO support --- src/boot/efi/meson.build | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index 4460af61f8..9281e146ea 100644 --- a/src/boot/efi/meson.build +++ b/src/boot/efi/meson.build @@ -244,9 +244,12 @@ if get_option('b_ndebug') == 'true' or ( get_option('b_ndebug') == 'if-release' and get_option('buildtype') in ['plain', 'release']) efi_cflags += ['-DNDEBUG'] endif +if get_option('b_lto') + efi_cflags += ['-flto'] +endif foreach arg : get_option('c_args') - if arg in ['-Werror', '-g', '-ggdb', '-O1', '-O2', '-O3', '-Og', '-Os', '-DNDEBUG'] + if arg in ['-Werror', '-g', '-ggdb', '-O1', '-O2', '-O3', '-Og', '-Os', '-DNDEBUG', '-flto', '-fno-lto'] message('Using "@0@" from c_args for EFI compiler'.format(arg)) efi_cflags += arg endif @@ -256,7 +259,6 @@ efi_ldflags = [ '-fuse-ld=' + get_option('efi-ld'), '-L', efi_libdir, '-nostdlib', - '-shared', '-T', efi_lds, '-Wl,--build-id=sha1', '-Wl,--fatal-warnings', @@ -267,11 +269,16 @@ efi_ldflags = [ efi_crt0, ] if efi_arch[1] in ['aarch64', 'arm', 'riscv64'] + efi_ldflags += ['-shared', '-fwhole-program'] # Aarch64, ARM32 and 64bit RISC-V don't have an EFI capable objcopy. # Use 'binary' instead, and add required symbols manually. efi_ldflags += ['-Wl,--defsym=EFI_SUBSYSTEM=0xa'] efi_format = ['-O', 'binary'] else + efi_ldflags += ['-pie'] + if get_option('efi-ld') == 'bfd' + efi_ldflags += '-Wl,--no-dynamic-linker' + endif efi_format = ['--target=efi-app-@0@'.format(efi_arch[1])] endif @@ -325,7 +332,7 @@ foreach tuple : [['systemd_boot.so', systemd_boot_efi_name, systemd_boot_objects tuple[0], input : tuple[2], output : tuple[0], - command : [efi_cc, '-o', '@OUTPUT@', efi_ldflags, tuple[2], '-lefi', '-lgnuefi', '-lgcc'], + command : [efi_cc, '-o', '@OUTPUT@', efi_ldflags, efi_cflags, tuple[2], '-lefi', '-lgnuefi', '-lgcc'], install : tuple[3], install_dir : bootlibdir) @@ -334,6 +341,7 @@ foreach tuple : [['systemd_boot.so', systemd_boot_efi_name, systemd_boot_objects input : so, output : tuple[1], command : [objcopy, + '-j', '.bss*', '-j', '.data', '-j', '.dynamic', '-j', '.dynsym', From fd86250c1e088f68181d231448088ebc33971b33 Mon Sep 17 00:00:00 2001 From: Jan Janssen Date: Mon, 8 Nov 2021 16:27:21 +0100 Subject: [PATCH 4/4] sd-boot: Always add TextInputEx to wait queue if available --- src/boot/efi/console.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/boot/efi/console.c b/src/boot/efi/console.c index 6b71e1e02c..6e76745cc6 100644 --- a/src/boot/efi/console.c +++ b/src/boot/efi/console.c @@ -52,12 +52,12 @@ EFI_STATUS console_key_read(UINT64 *key, UINT64 timeout_usec) { /* If WaitForKeyEx fails here, the firmware pretends it talks this * protocol, but it really doesn't. */ TextInputEx = NULL; - else - events[n_events++] = TextInputEx->WaitForKeyEx; - checked = TRUE; } + if (TextInputEx) + events[n_events++] = TextInputEx->WaitForKeyEx; + err = BS->CreateEvent(EVT_TIMER, 0, NULL, NULL, &timer); if (EFI_ERROR(err)) return log_error_status_stall(err, L"Error creating timer event: %r", err);