1# SPDX-License-Identifier: LGPL-2.1-or-later 2 3conf.set10('ENABLE_EFI', get_option('efi')) 4conf.set10('HAVE_GNU_EFI', false) 5 6efi_config_h_dir = meson.current_build_dir() 7 8if not get_option('efi') or get_option('gnu-efi') == 'false' 9 if get_option('gnu-efi') == 'true' 10 error('gnu-efi support requested, but general efi support is disabled') 11 endif 12 subdir_done() 13endif 14 15efi_arch = host_machine.cpu_family() 16if efi_arch == 'x86' and '-m64' in get_option('efi-cflags') 17 efi_arch = 'x86_64' 18elif efi_arch == 'x86_64' and '-m32' in get_option('efi-cflags') 19 efi_arch = 'x86' 20endif 21efi_arch = { 22 # host_cc_arch: [efi_arch (see Table 3-2 in UEFI spec), gnu_efi_inc_arch] 23 'x86': ['ia32', 'ia32'], 24 'x86_64': ['x64', 'x86_64'], 25 'arm': ['arm', 'arm'], 26 'aarch64': ['aa64', 'aarch64'], 27 'riscv64': ['riscv64', 'riscv64'], 28}.get(efi_arch, []) 29 30efi_incdir = get_option('efi-includedir') 31if efi_arch.length() > 0 and not cc.has_header( 32 '@0@/@1@/efibind.h'.format(efi_incdir, efi_arch[1]), 33 args: get_option('efi-cflags')) 34 35 efi_arch = [] 36endif 37 38if efi_arch.length() == 0 39 if get_option('gnu-efi') == 'true' 40 error('gnu-efi support requested, but headers not found or efi arch is unknown') 41 endif 42 warning('gnu-efi headers not found or efi arch is unknown, disabling gnu-efi support') 43 subdir_done() 44endif 45 46if not cc.has_header_symbol('efi.h', 'EFI_IMAGE_MACHINE_X64', 47 args: ['-nostdlib', '-ffreestanding', '-fshort-wchar'] + get_option('efi-cflags'), 48 include_directories: include_directories(efi_incdir, efi_incdir / efi_arch[1])) 49 50 if get_option('gnu-efi') == 'true' 51 error('gnu-efi support requested, but found headers are too old (3.0.5+ required)') 52 endif 53 warning('gnu-efi headers are too old (3.0.5+ required), disabling gnu-efi support') 54 subdir_done() 55endif 56 57objcopy = run_command(cc.cmd_array(), '-print-prog-name=objcopy', check: true).stdout().strip() 58 59efi_ld = get_option('efi-ld') 60if efi_ld == 'auto' 61 efi_ld = cc.get_linker_id().split('.')[1] 62 if efi_ld not in ['bfd', 'gold'] 63 warning('Not using @0@ as efi-ld, falling back to bfd'.format(efi_ld)) 64 efi_ld = 'bfd' 65 endif 66endif 67 68efi_multilib = run_command( 69 cc.cmd_array(), '-print-multi-os-directory', get_option('efi-cflags'), 70 check: false 71).stdout().strip() 72efi_multilib = run_command( 73 'realpath', '-e', '/usr/lib' / efi_multilib, 74 check: false 75).stdout().strip() 76 77efi_libdir = '' 78foreach dir : [get_option('efi-libdir'), 79 '/usr/lib/gnuefi' / efi_arch[0], 80 efi_multilib] 81 if dir != '' and fs.is_dir(dir) 82 efi_libdir = dir 83 break 84 endif 85endforeach 86if efi_libdir == '' 87 if get_option('gnu-efi') == 'true' 88 error('gnu-efi support requested, but efi-libdir was not found') 89 endif 90 warning('efi-libdir was not found, disabling gnu-efi support') 91 subdir_done() 92endif 93 94efi_lds = '' 95foreach location : [ # New locations first introduced with gnu-efi 3.0.11 96 [efi_libdir / 'efi.lds', 97 efi_libdir / 'crt0.o'], 98 # Older locations... 99 [efi_libdir / 'gnuefi' / 'elf_@0@_efi.lds'.format(efi_arch[1]), 100 efi_libdir / 'gnuefi' / 'crt0-efi-@0@.o'.format(efi_arch[1])], 101 [efi_libdir / 'elf_@0@_efi.lds'.format(efi_arch[1]), 102 efi_libdir / 'crt0-efi-@0@.o'.format(efi_arch[1])]] 103 if fs.is_file(location[0]) and fs.is_file(location[1]) 104 efi_lds = location[0] 105 efi_crt0 = location[1] 106 break 107 endif 108endforeach 109if efi_lds == '' 110 if get_option('gnu-efi') == 'true' 111 error('gnu-efi support requested, but cannot find efi.lds') 112 endif 113 warning('efi.lds was not found, disabling gnu-efi support') 114 subdir_done() 115endif 116 117conf.set10('HAVE_GNU_EFI', true) 118conf.set_quoted('EFI_MACHINE_TYPE_NAME', efi_arch[0]) 119 120efi_conf = configuration_data() 121efi_conf.set_quoted('EFI_MACHINE_TYPE_NAME', efi_arch[0]) 122efi_conf.set10('ENABLE_TPM', get_option('tpm')) 123efi_conf.set10('EFI_TPM_PCR_COMPAT', get_option('efi-tpm-pcr-compat')) 124 125foreach ctype : ['color-normal', 'color-entry', 'color-highlight', 'color-edit'] 126 c = get_option('efi-' + ctype).split(',') 127 efi_conf.set(ctype.underscorify().to_upper(), 'EFI_TEXT_ATTR(@0@, @1@)'.format( 128 'EFI_' + c[0].strip().underscorify().to_upper(), 129 'EFI_' + c[1].strip().underscorify().to_upper())) 130endforeach 131 132if meson.is_cross_build() and get_option('sbat-distro') == 'auto' 133 warning('Auto detection of SBAT information not supported when cross-building, disabling SBAT.') 134elif get_option('sbat-distro') != '' 135 efi_conf.set_quoted('SBAT_PROJECT', meson.project_name()) 136 efi_conf.set_quoted('PROJECT_VERSION', meson.project_version()) 137 efi_conf.set('PROJECT_URL', conf.get('PROJECT_URL')) 138 if get_option('sbat-distro-generation') < 1 139 error('SBAT Distro Generation must be a positive integer') 140 endif 141 efi_conf.set('SBAT_DISTRO_GENERATION', get_option('sbat-distro-generation')) 142 foreach sbatvar : [['sbat-distro', 'ID'], 143 ['sbat-distro-summary', 'NAME'], 144 ['sbat-distro-url', 'BUG_REPORT_URL']] 145 value = get_option(sbatvar[0]) 146 if (value == '' or value == 'auto') and not meson.is_cross_build() 147 cmd = 'if [ -e /etc/os-release ]; then . /etc/os-release; else . /usr/lib/os-release; fi; echo $@0@'.format(sbatvar[1]) 148 value = run_command(sh, '-c', cmd, check: true).stdout().strip() 149 endif 150 if value == '' 151 error('Required @0@ option not set and autodetection failed'.format(sbatvar[0])) 152 endif 153 efi_conf.set_quoted(sbatvar[0].underscorify().to_upper(), value) 154 endforeach 155 156 pkgname = get_option('sbat-distro-pkgname') 157 if pkgname == '' 158 pkgname = meson.project_name() 159 endif 160 efi_conf.set_quoted('SBAT_DISTRO_PKGNAME', pkgname) 161 162 pkgver = get_option('sbat-distro-version') 163 if pkgver == '' 164 efi_conf.set('SBAT_DISTRO_VERSION', 'GIT_VERSION') 165 # This is determined during build, not configuration, so we can't display it yet. 166 sbat_distro_version_display = '(git version)' 167 else 168 efi_conf.set_quoted('SBAT_DISTRO_VERSION', pkgver) 169 sbat_distro_version_display = pkgver 170 endif 171endif 172 173efi_config_h = configure_file( 174 output : 'efi_config.h', 175 configuration : efi_conf) 176 177efi_cflags = cc.get_supported_arguments( 178 basic_disabled_warnings + 179 possible_common_cc_flags + [ 180 '-fno-stack-protector', 181 '-fno-strict-aliasing', 182 '-fpic', 183 '-fwide-exec-charset=UCS2', 184 '-Wall', 185 '-Wextra', 186 '-Wsign-compare', 187 ] 188) + [ 189 '-nostdlib', 190 '-std=gnu11', 191 '-ffreestanding', 192 '-fshort-wchar', 193 '-fvisibility=hidden', 194 '-isystem', efi_incdir, 195 '-isystem', efi_incdir / efi_arch[1], 196 '-I', fundamental_path, 197 '-DSD_BOOT', 198 '-DGNU_EFI_USE_MS_ABI', 199 '-include', efi_config_h, 200 '-include', version_h, 201] 202 203efi_cflags += cc.get_supported_arguments({ 204 'ia32': ['-mno-sse', '-mno-mmx'], 205 'x86_64': ['-mno-red-zone', '-mno-sse', '-mno-mmx'], 206 'arm': ['-mgeneral-regs-only', '-mfpu=none'], 207}.get(efi_arch[1], [])) 208 209# We are putting the efi_cc command line together ourselves, so make sure to pull any 210# relevant compiler flags from meson/CFLAGS as povided by the user or distro. 211 212if get_option('werror') 213 efi_cflags += ['-Werror'] 214endif 215if get_option('debug') and get_option('mode') == 'developer' 216 efi_cflags += ['-ggdb', '-DEFI_DEBUG'] 217endif 218if get_option('optimization') != '0' 219 efi_cflags += ['-O' + get_option('optimization')] 220endif 221if get_option('b_ndebug') == 'true' or ( 222 get_option('b_ndebug') == 'if-release' and get_option('buildtype') in ['plain', 'release']) 223 efi_cflags += ['-DNDEBUG'] 224endif 225if get_option('b_lto') 226 efi_cflags += ['-flto'] 227endif 228 229foreach arg : get_option('c_args') 230 if arg in [ 231 '-DNDEBUG', 232 '-fno-lto', 233 '-O1', '-O2', '-O3', '-Og', '-Os', 234 '-Werror', 235 ] or arg.split('=')[0] in [ 236 '-ffile-prefix-map', 237 '-flto', 238 ] or (get_option('mode') == 'developer' and arg in [ 239 '-DEFI_DEBUG', 240 '-g', '-ggdb', 241 ]) 242 243 message('Using "@0@" from c_args for EFI compiler'.format(arg)) 244 efi_cflags += arg 245 endif 246endforeach 247 248efi_cflags += get_option('efi-cflags') 249 250efi_ldflags = [ 251 '-fuse-ld=' + efi_ld, 252 '-L', efi_libdir, 253 '-nostdlib', 254 '-T', efi_lds, 255 '-Wl,--build-id=sha1', 256 '-Wl,--fatal-warnings', 257 '-Wl,--no-undefined', 258 '-Wl,--warn-common', 259 '-Wl,-Bsymbolic', 260 '-z', 'nocombreloc', 261 efi_crt0, 262] 263if efi_arch[1] in ['aarch64', 'arm', 'riscv64'] 264 efi_ldflags += ['-shared'] 265 # Aarch64, ARM32 and 64bit RISC-V don't have an EFI capable objcopy. 266 # Use 'binary' instead, and add required symbols manually. 267 efi_ldflags += ['-Wl,--defsym=EFI_SUBSYSTEM=0xa'] 268 efi_format = ['-O', 'binary'] 269else 270 efi_ldflags += ['-pie'] 271 if efi_ld == 'bfd' 272 efi_ldflags += '-Wl,--no-dynamic-linker' 273 endif 274 efi_format = ['--target=efi-app-@0@'.format(efi_arch[1])] 275endif 276 277if efi_arch[1] == 'arm' 278 # On arm, the compiler (correctly) warns about wchar_t size mismatch. This 279 # is because libgcc is not compiled with -fshort-wchar, but it does not 280 # have any occurrences of wchar_t in its sources or the documentation, so 281 # it is safe to assume that we can ignore this warning. 282 efi_ldflags += ['-Wl,--no-wchar-size-warning'] 283endif 284 285if run_command('grep', '-q', '__CTOR_LIST__', efi_lds, check: false).returncode() == 0 286 # fedora has a patched gnu-efi that adds support for ELF constructors. 287 # If ld is called by gcc something about these symbols breaks, resulting 288 # in sd-boot freezing when gnu-efi runs the constructors. Force defining 289 # them seems to work around this. 290 efi_ldflags += [ 291 '-Wl,--defsym=_init_array=0', 292 '-Wl,--defsym=_init_array_end=0', 293 '-Wl,--defsym=_fini_array=0', 294 '-Wl,--defsym=_fini_array_end=0', 295 '-Wl,--defsym=__CTOR_LIST__=0', 296 '-Wl,--defsym=__CTOR_END__=0', 297 '-Wl,--defsym=__DTOR_LIST__=0', 298 '-Wl,--defsym=__DTOR_END__=0', 299 ] 300endif 301 302if cc.get_id() == 'clang' and cc.version().split('.')[0].to_int() <= 10 303 # clang <= 10 doesn't pass -T to the linker and then even complains about it being unused 304 efi_ldflags += ['-Wl,-T,' + efi_lds, '-Wno-unused-command-line-argument'] 305endif 306 307summary({ 308 'EFI machine type' : efi_arch[0], 309 'EFI LD' : efi_ld, 310 'EFI lds' : efi_lds, 311 'EFI crt0' : efi_crt0, 312 'EFI include directory' : efi_incdir}, 313 section : 'Extensible Firmware Interface') 314 315if efi_conf.get('SBAT_DISTRO', '') != '' 316 summary({ 317 'SBAT distro': efi_conf.get('SBAT_DISTRO'), 318 'SBAT distro generation': efi_conf.get('SBAT_DISTRO_GENERATION'), 319 'SBAT distro version': sbat_distro_version_display, 320 'SBAT distro summary': efi_conf.get('SBAT_DISTRO_SUMMARY'), 321 'SBAT distro URL': efi_conf.get('SBAT_DISTRO_URL')}, 322 section : 'Extensible Firmware Interface') 323endif 324 325############################################################ 326 327efi_headers = files( 328 'bcd.h', 329 'console.h', 330 'cpio.h', 331 'devicetree.h', 332 'disk.h', 333 'drivers.h', 334 'graphics.h', 335 'initrd.h', 336 'linux.h', 337 'measure.h', 338 'missing_efi.h', 339 'pe.h', 340 'random-seed.h', 341 'secure-boot.h', 342 'shim.h', 343 'splash.h', 344 'ticks.h', 345 'util.h', 346 'xbootldr.h', 347) 348 349common_sources = files( 350 'assert.c', 351 'devicetree.c', 352 'disk.c', 353 'graphics.c', 354 'measure.c', 355 'pe.c', 356 'secure-boot.c', 357 'ticks.c', 358 'util.c', 359) 360 361systemd_boot_sources = files( 362 'boot.c', 363 'console.c', 364 'drivers.c', 365 'random-seed.c', 366 'shim.c', 367 'xbootldr.c', 368) 369 370stub_sources = files( 371 'cpio.c', 372 'initrd.c', 373 'splash.c', 374 'stub.c', 375) 376 377if efi_arch[1] in ['ia32', 'x86_64'] 378 stub_sources += files('linux_x86.c') 379else 380 stub_sources += files('linux.c') 381endif 382 383# BCD parser only makes sense on arches that Windows supports. 384if efi_arch[1] in ['ia32', 'x86_64', 'arm', 'aarch64'] 385 systemd_boot_sources += files('bcd.c') 386 tests += [ 387 [files('test-bcd.c'), 388 [], 389 [libzstd], 390 [], 391 'HAVE_ZSTD'], 392 ] 393 fuzzers += [ 394 [files('fuzz-bcd.c')], 395 ] 396endif 397 398systemd_boot_objects = [] 399stub_objects = [] 400foreach file : fundamental_source_paths + common_sources + systemd_boot_sources + stub_sources 401 # FIXME: replace ''.format(file) with fs.name(file) when meson_version requirement is >= 0.59.0 402 o_file = custom_target('@0@.o'.format(file).split('/')[-1], 403 input : file, 404 output : '@0@.o'.format(file).split('/')[-1], 405 command : [cc.cmd_array(), '-c', '@INPUT@', '-o', '@OUTPUT@', efi_cflags], 406 depend_files : efi_headers + fundamental_headers) 407 if (fundamental_source_paths + common_sources + systemd_boot_sources).contains(file) 408 systemd_boot_objects += o_file 409 endif 410 if (fundamental_source_paths + common_sources + stub_sources).contains(file) 411 stub_objects += o_file 412 endif 413endforeach 414 415foreach tuple : [['systemd-boot@0@.@1@', systemd_boot_objects, false, 'systemd-boot'], 416 ['linux@0@.@1@.stub', stub_objects, true, 'systemd-stub']] 417 elf = custom_target( 418 tuple[0].format(efi_arch[0], 'elf'), 419 input : tuple[1], 420 output : tuple[0].format(efi_arch[0], 'elf'), 421 command : [cc.cmd_array(), 422 '-o', '@OUTPUT@', 423 efi_cflags, 424 efi_ldflags, 425 '@INPUT@', 426 '-lefi', 427 '-lgnuefi', 428 '-lgcc'], 429 install : tuple[2], 430 install_tag: tuple[3], 431 install_dir : bootlibdir) 432 433 efi = custom_target( 434 tuple[0].format(efi_arch[0], 'efi'), 435 input : elf, 436 output : tuple[0].format(efi_arch[0], 'efi'), 437 command : [objcopy, 438 '-j', '.bss*', 439 '-j', '.data', 440 '-j', '.dynamic', 441 '-j', '.dynsym', 442 '-j', '.osrel', 443 '-j', '.rel*', 444 '-j', '.sbat', 445 '-j', '.sdata', 446 '-j', '.sdmagic', 447 '-j', '.text', 448 '--section-alignment=512', 449 efi_format, 450 '@INPUT@', '@OUTPUT@'], 451 install : true, 452 install_tag: tuple[3], 453 install_dir : bootlibdir) 454 455 alias_target(tuple[3], efi) 456endforeach 457