diff --git a/meson_options.txt b/meson_options.txt index 0d3491a56c..ed51fae43a 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -413,7 +413,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/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); diff --git a/src/boot/efi/meson.build b/src/boot/efi/meson.build index e32664ca89..c095512bfb 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], @@ -252,34 +244,67 @@ 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 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', + '-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'] + 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 += ['--defsym=EFI_SUBSYSTEM=0xa'] + 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 +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 @@ -296,7 +321,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]) @@ -308,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_ld, '-o', '@OUTPUT@', efi_ldflags, tuple[2], '-lefi', '-lgnuefi', libgcc_file_name], + command : [efi_cc, '-o', '@OUTPUT@', efi_ldflags, efi_cflags, tuple[2], '-lefi', '-lgnuefi', '-lgcc'], install : tuple[3], install_dir : bootlibdir) @@ -317,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',