| # SPDX-License-Identifier: LGPL-2.1-or-later |
| |
| conf.set10('ENABLE_EFI', get_option('efi')) |
| conf.set10('HAVE_GNU_EFI', false) |
| |
| efi_config_h_dir = meson.current_build_dir() |
| |
| if not get_option('efi') or get_option('gnu-efi') == 'false' |
| if get_option('gnu-efi') == 'true' |
| error('gnu-efi support requested, but general efi support is disabled') |
| endif |
| subdir_done() |
| endif |
| |
| efi_arch = host_machine.cpu_family() |
| if efi_arch == 'x86' and '-m64' in get_option('efi-cflags') |
| efi_arch = 'x86_64' |
| elif efi_arch == 'x86_64' and '-m32' in get_option('efi-cflags') |
| efi_arch = 'x86' |
| endif |
| efi_arch = { |
| # host_cc_arch: [efi_arch (see Table 3-2 in UEFI spec), obsolete gnu_efi_inc_arch] |
| 'x86': ['ia32', 'ia32'], |
| 'x86_64': ['x64', 'x86_64'], |
| 'arm': ['arm', 'arm'], |
| 'aarch64': ['aa64', 'aarch64'], |
| 'riscv64': ['riscv64', 'riscv64'], |
| }.get(efi_arch, []) |
| |
| efi_incdir = get_option('efi-includedir') |
| found = false |
| foreach efi_arch_candidate : efi_arch |
| efi_archdir = efi_incdir / efi_arch_candidate |
| if cc.has_header(efi_archdir / 'efibind.h', |
| args: get_option('efi-cflags')) |
| found = true |
| break |
| endif |
| endforeach |
| |
| if not found |
| if get_option('gnu-efi') == 'true' |
| error('gnu-efi support requested, but headers not found or efi arch is unknown') |
| endif |
| warning('gnu-efi headers not found or efi arch is unknown, disabling gnu-efi support') |
| subdir_done() |
| endif |
| |
| if not cc.has_header_symbol('efi.h', 'EFI_IMAGE_MACHINE_X64', |
| args: ['-nostdlib', '-ffreestanding', '-fshort-wchar'] + get_option('efi-cflags'), |
| include_directories: include_directories(efi_incdir, |
| efi_archdir)) |
| |
| if get_option('gnu-efi') == 'true' |
| error('gnu-efi support requested, but found headers are too old (3.0.5+ required)') |
| endif |
| warning('gnu-efi headers are too old (3.0.5+ required), disabling gnu-efi support') |
| subdir_done() |
| endif |
| |
| objcopy = run_command(cc.cmd_array(), '-print-prog-name=objcopy', check: true).stdout().strip() |
| objcopy_2_38 = find_program('objcopy', version: '>=2.38', required: false) |
| |
| efi_ld = get_option('efi-ld') |
| if efi_ld == 'auto' |
| efi_ld = cc.get_linker_id().split('.')[1] |
| if efi_ld not in ['bfd', 'gold'] |
| message('Not using @0@ as efi-ld, falling back to bfd'.format(efi_ld)) |
| efi_ld = 'bfd' |
| endif |
| endif |
| |
| efi_multilib = run_command( |
| cc.cmd_array(), '-print-multi-os-directory', get_option('efi-cflags'), |
| check: false |
| ).stdout().strip() |
| efi_multilib = run_command( |
| 'realpath', '-e', '/usr/lib' / efi_multilib, |
| check: false |
| ).stdout().strip() |
| |
| efi_libdir = '' |
| foreach dir : [get_option('efi-libdir'), |
| '/usr/lib/gnuefi' / efi_arch[0], |
| efi_multilib] |
| if dir != '' and fs.is_dir(dir) |
| efi_libdir = dir |
| break |
| endif |
| endforeach |
| if efi_libdir == '' |
| if get_option('gnu-efi') == 'true' |
| error('gnu-efi support requested, but efi-libdir was not found') |
| endif |
| warning('efi-libdir was not found, disabling gnu-efi support') |
| subdir_done() |
| endif |
| |
| efi_lds = '' |
| foreach location : [ # New locations first introduced with gnu-efi 3.0.11 |
| [efi_libdir / 'efi.lds', |
| efi_libdir / 'crt0.o'], |
| # Older locations... |
| [efi_libdir / 'gnuefi' / 'elf_@0@_efi.lds'.format(efi_arch[1]), |
| efi_libdir / 'gnuefi' / 'crt0-efi-@0@.o'.format(efi_arch[1])], |
| [efi_libdir / 'elf_@0@_efi.lds'.format(efi_arch[1]), |
| efi_libdir / 'crt0-efi-@0@.o'.format(efi_arch[1])]] |
| if fs.is_file(location[0]) and fs.is_file(location[1]) |
| efi_lds = location[0] |
| efi_crt0 = location[1] |
| break |
| endif |
| endforeach |
| if efi_lds == '' |
| if get_option('gnu-efi') == 'true' |
| error('gnu-efi support requested, but cannot find efi.lds') |
| endif |
| warning('efi.lds was not found, disabling gnu-efi support') |
| subdir_done() |
| endif |
| |
| conf.set10('HAVE_GNU_EFI', true) |
| conf.set_quoted('EFI_MACHINE_TYPE_NAME', efi_arch[0]) |
| |
| efi_conf = configuration_data() |
| efi_conf.set_quoted('EFI_MACHINE_TYPE_NAME', efi_arch[0]) |
| efi_conf.set10('ENABLE_TPM', get_option('tpm')) |
| |
| foreach ctype : ['color-normal', 'color-entry', 'color-highlight', 'color-edit'] |
| c = get_option('efi-' + ctype).split(',') |
| efi_conf.set(ctype.underscorify().to_upper(), 'EFI_TEXT_ATTR(@0@, @1@)'.format( |
| 'EFI_' + c[0].strip().underscorify().to_upper(), |
| 'EFI_' + c[1].strip().underscorify().to_upper())) |
| endforeach |
| |
| if meson.is_cross_build() and get_option('sbat-distro') == 'auto' |
| warning('Auto detection of SBAT information not supported when cross-building, disabling SBAT.') |
| elif get_option('sbat-distro') != '' |
| efi_conf.set_quoted('SBAT_PROJECT', meson.project_name()) |
| efi_conf.set_quoted('PROJECT_VERSION', meson.project_version()) |
| efi_conf.set('PROJECT_URL', conf.get('PROJECT_URL')) |
| if get_option('sbat-distro-generation') < 1 |
| error('SBAT Distro Generation must be a positive integer') |
| endif |
| efi_conf.set('SBAT_DISTRO_GENERATION', get_option('sbat-distro-generation')) |
| foreach sbatvar : [['sbat-distro', 'ID'], |
| ['sbat-distro-summary', 'NAME'], |
| ['sbat-distro-url', 'BUG_REPORT_URL']] |
| value = get_option(sbatvar[0]) |
| if (value == '' or value == 'auto') and not meson.is_cross_build() |
| cmd = 'if [ -e /etc/os-release ]; then . /etc/os-release; else . /usr/lib/os-release; fi; echo $@0@'.format(sbatvar[1]) |
| value = run_command(sh, '-c', cmd, check: true).stdout().strip() |
| endif |
| if value == '' |
| error('Required @0@ option not set and autodetection failed'.format(sbatvar[0])) |
| endif |
| efi_conf.set_quoted(sbatvar[0].underscorify().to_upper(), value) |
| endforeach |
| |
| pkgname = get_option('sbat-distro-pkgname') |
| if pkgname == '' |
| pkgname = meson.project_name() |
| endif |
| efi_conf.set_quoted('SBAT_DISTRO_PKGNAME', pkgname) |
| |
| pkgver = get_option('sbat-distro-version') |
| if pkgver == '' |
| efi_conf.set('SBAT_DISTRO_VERSION', 'GIT_VERSION') |
| # This is determined during build, not configuration, so we can't display it yet. |
| sbat_distro_version_display = '(git version)' |
| else |
| efi_conf.set_quoted('SBAT_DISTRO_VERSION', pkgver) |
| sbat_distro_version_display = pkgver |
| endif |
| endif |
| |
| efi_config_h = configure_file( |
| output : 'efi_config.h', |
| configuration : efi_conf) |
| |
| efi_cflags = [ |
| '-DGNU_EFI_USE_MS_ABI', |
| '-DSD_BOOT=1', |
| '-ffreestanding', |
| '-fshort-wchar', |
| '-fvisibility=hidden', |
| '-I', fundamental_path, |
| '-I', meson.current_source_dir(), |
| '-include', efi_config_h, |
| '-include', version_h, |
| '-I', efi_archdir, |
| '-isystem', efi_incdir, |
| '-std=gnu11', |
| '-Wall', |
| '-Wextra', |
| ] + cc.get_supported_arguments( |
| basic_disabled_warnings + |
| possible_common_cc_flags + [ |
| '-fno-stack-protector', |
| '-fno-strict-aliasing', |
| '-fpic', |
| '-fwide-exec-charset=UCS2', |
| ] |
| ) |
| |
| efi_cflags += cc.get_supported_arguments({ |
| 'ia32': ['-mno-sse', '-mno-mmx'], |
| 'x86_64': ['-mno-red-zone', '-mno-sse', '-mno-mmx'], |
| 'arm': ['-mgeneral-regs-only', '-mfpu=none'], |
| }.get(efi_arch[1], [])) |
| |
| # We are putting the efi_cc command line together ourselves, so make sure to pull any |
| # relevant compiler flags from meson/CFLAGS as povided by the user or distro. |
| |
| if get_option('werror') |
| efi_cflags += ['-Werror'] |
| endif |
| if get_option('debug') and get_option('mode') == 'developer' |
| efi_cflags += ['-ggdb', '-DEFI_DEBUG'] |
| endif |
| if get_option('optimization') in ['1', '2', '3', 's', 'g'] |
| efi_cflags += ['-O' + get_option('optimization')] |
| endif |
| 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 += cc.has_argument('-flto=auto') ? ['-flto=auto'] : ['-flto'] |
| endif |
| |
| foreach arg : get_option('c_args') |
| if arg in [ |
| '-DNDEBUG', |
| '-fno-lto', |
| '-O1', '-O2', '-O3', '-Og', '-Os', |
| '-Werror', |
| ] or arg.split('=')[0] in [ |
| '-ffile-prefix-map', |
| '-flto', |
| ] or (get_option('mode') == 'developer' and arg in [ |
| '-DEFI_DEBUG', |
| '-g', '-ggdb', |
| ]) |
| |
| message('Using "@0@" from c_args for EFI compiler'.format(arg)) |
| efi_cflags += arg |
| endif |
| endforeach |
| |
| efi_cflags += get_option('efi-cflags') |
| |
| efi_ldflags = [ |
| '-fuse-ld=' + 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', |
| '-z', 'noexecstack', |
| efi_crt0, |
| ] |
| |
| foreach arg : ['-Wl,--no-warn-execstack', |
| '-Wl,--no-warn-rwx-segments'] |
| # We need to check the correct linker for supported args. This is what |
| # cc.has_multi_link_arguments() is for, but it helpfully overrides our |
| # choice of linker by putting its own -fuse-ld= arg after ours. |
| if run_command('bash', '-c', |
| 'exec "$@" -x c -o/dev/null <(echo "int main(void){return 0;}")' + |
| ' -fuse-ld=' + efi_ld + ' -Wl,--fatal-warnings ' + arg, |
| 'bash', cc.cmd_array(), |
| check : false).returncode() == 0 |
| efi_ldflags += arg |
| endif |
| endforeach |
| |
| # If using objcopy, crt0 must not include the PE/COFF header |
| if run_command('grep', '-q', 'coff_header', efi_crt0, check: false).returncode() == 0 |
| coff_header_in_crt0 = true |
| else |
| coff_header_in_crt0 = false |
| endif |
| |
| if efi_arch[1] in ['arm', 'riscv64'] or (efi_arch[1] == 'aarch64' and (not objcopy_2_38.found() or coff_header_in_crt0)) |
| efi_ldflags += ['-shared'] |
| # ARM32 and 64bit RISC-V don't have an EFI capable objcopy. |
| # Older objcopy doesn't support Aarch64 either. |
| # Use 'binary' instead, and add required symbols manually. |
| efi_ldflags += ['-Wl,--defsym=EFI_SUBSYSTEM=0xa'] |
| efi_format = ['-O', 'binary'] |
| else |
| efi_ldflags += ['-pie'] |
| if efi_ld == 'bfd' |
| efi_ldflags += '-Wl,--no-dynamic-linker' |
| endif |
| efi_format = ['--target=efi-app-@0@'.format(efi_arch[1])] |
| endif |
| |
| if efi_arch[1] == 'arm' |
| # On arm, the compiler (correctly) warns about wchar_t size mismatch. This |
| # is because libgcc is not compiled with -fshort-wchar, but it does not |
| # have any occurrences of wchar_t in its sources or the documentation, so |
| # it is safe to assume that we can ignore this warning. |
| efi_ldflags += ['-Wl,--no-wchar-size-warning'] |
| endif |
| |
| if cc.get_id() == 'clang' and cc.version().split('.')[0].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 |
| |
| summary({ |
| 'EFI machine type' : efi_arch[0], |
| 'EFI LD' : efi_ld, |
| 'EFI lds' : efi_lds, |
| 'EFI crt0' : efi_crt0, |
| 'EFI include directory' : efi_archdir}, |
| section : 'Extensible Firmware Interface') |
| |
| if efi_conf.get('SBAT_DISTRO', '') != '' |
| summary({ |
| 'SBAT distro': efi_conf.get('SBAT_DISTRO'), |
| 'SBAT distro generation': efi_conf.get('SBAT_DISTRO_GENERATION'), |
| 'SBAT distro version': sbat_distro_version_display, |
| 'SBAT distro summary': efi_conf.get('SBAT_DISTRO_SUMMARY'), |
| 'SBAT distro URL': efi_conf.get('SBAT_DISTRO_URL')}, |
| section : 'Extensible Firmware Interface') |
| endif |
| |
| ############################################################ |
| |
| efi_headers = files( |
| 'bcd.h', |
| 'console.h', |
| 'cpio.h', |
| 'devicetree.h', |
| 'disk.h', |
| 'drivers.h', |
| 'efi-string.h', |
| 'graphics.h', |
| 'initrd.h', |
| 'linux.h', |
| 'log.h', |
| 'measure.h', |
| 'missing_efi.h', |
| 'part-discovery.h', |
| 'pe.h', |
| 'random-seed.h', |
| 'secure-boot.h', |
| 'shim.h', |
| 'splash.h', |
| 'ticks.h', |
| 'util.h', |
| ) |
| |
| common_sources = files( |
| 'console.c', |
| 'devicetree.c', |
| 'drivers.c', |
| 'disk.c', |
| 'efi-string.c', |
| 'graphics.c', |
| 'initrd.c', |
| 'log.c', |
| 'measure.c', |
| 'part-discovery.c', |
| 'pe.c', |
| 'random-seed.c', |
| 'secure-boot.c', |
| 'ticks.c', |
| 'util.c', |
| 'vmm.c', |
| ) |
| |
| systemd_boot_sources = files( |
| 'boot.c', |
| 'shim.c', |
| ) |
| |
| stub_sources = files( |
| 'cpio.c', |
| 'linux.c', |
| 'splash.c', |
| 'stub.c', |
| ) |
| |
| if efi_arch[1] in ['ia32', 'x86_64'] |
| stub_sources += files('linux_x86.c') |
| endif |
| |
| tests += [ |
| [files('test-efi-string.c', 'efi-string.c')], |
| ] |
| |
| # BCD parser only makes sense on arches that Windows supports. |
| if efi_arch[1] in ['ia32', 'x86_64', 'arm', 'aarch64'] |
| systemd_boot_sources += files('bcd.c') |
| tests += [ |
| [files('test-bcd.c', 'efi-string.c'), |
| [], |
| [libzstd], |
| [], |
| 'HAVE_ZSTD'], |
| ] |
| fuzzers += [ |
| [files('fuzz-bcd.c', 'bcd.c', 'efi-string.c')], |
| [files('fuzz-efi-string.c', 'efi-string.c')], |
| [files('fuzz-efi-printf.c', 'efi-string.c')], |
| ] |
| endif |
| |
| systemd_boot_objects = [] |
| stub_objects = [] |
| foreach file : fundamental_source_paths + common_sources + systemd_boot_sources + stub_sources |
| # FIXME: replace ''.format(file) with fs.name(file) when meson_version requirement is >= 0.59.0 |
| o_file = custom_target('@0@.o'.format(file).split('/')[-1], |
| input : file, |
| output : '@0@.o'.format(file).split('/')[-1], |
| command : [cc.cmd_array(), '-c', '@INPUT@', '-o', '@OUTPUT@', efi_cflags], |
| depend_files : efi_headers + fundamental_headers) |
| if (fundamental_source_paths + common_sources + systemd_boot_sources).contains(file) |
| systemd_boot_objects += o_file |
| endif |
| if (fundamental_source_paths + common_sources + stub_sources).contains(file) |
| stub_objects += o_file |
| endif |
| endforeach |
| |
| foreach tuple : [['systemd-boot@0@.@1@', systemd_boot_objects, false, 'systemd-boot'], |
| ['linux@0@.@1@.stub', stub_objects, true, 'systemd-stub']] |
| elf = custom_target( |
| tuple[0].format(efi_arch[0], 'elf'), |
| input : tuple[1], |
| output : tuple[0].format(efi_arch[0], 'elf'), |
| command : [cc.cmd_array(), |
| '-o', '@OUTPUT@', |
| efi_cflags, |
| efi_ldflags, |
| '@INPUT@', |
| '-lgnuefi', |
| '-lgcc'], |
| install : tuple[2], |
| install_tag: tuple[3], |
| install_dir : bootlibdir) |
| |
| efi = custom_target( |
| tuple[0].format(efi_arch[0], 'efi'), |
| input : elf, |
| output : tuple[0].format(efi_arch[0], 'efi'), |
| command : [objcopy, |
| '-j', '.bss*', |
| '-j', '.data', |
| '-j', '.dynamic', |
| '-j', '.dynsym', |
| '-j', '.osrel', |
| '-j', '.rel*', |
| '-j', '.sbat', |
| '-j', '.sdata', |
| '-j', '.sdmagic', |
| '-j', '.text', |
| '--strip-all', |
| '--section-alignment=512', |
| efi_format, |
| '@INPUT@', '@OUTPUT@'], |
| install : true, |
| install_tag: tuple[3], |
| install_dir : bootlibdir) |
| |
| alias_target(tuple[3], efi) |
| endforeach |