PR-URL: https://github.com/nodejs/node/pull/64040 Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: René <contact.9a5d6388@renegade334.me.uk> Reviewed-By: Paolo Insogna <paolo@cowtech.it>
278 lines
7.9 KiB
Python
278 lines
7.9 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import argparse
|
|
import os
|
|
import platform
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
|
|
LIBFFI_VERSION = '3.6.0'
|
|
LIBFFI_VERSION_NUMBER = '30600'
|
|
|
|
def normalize_arch(target_arch):
|
|
aliases = {
|
|
'x86': 'ia32',
|
|
'x86_64': 'x64',
|
|
'arm64': 'arm64',
|
|
'aarch64': 'arm64',
|
|
}
|
|
return aliases.get(target_arch, target_arch)
|
|
|
|
|
|
def get_target(os_name, target_arch):
|
|
target_arch = normalize_arch(target_arch)
|
|
|
|
if target_arch == 'arm':
|
|
return ('ARM_WIN32' if os_name == 'win' else 'ARM', 'arm')
|
|
|
|
if target_arch == 'arm64':
|
|
return ('ARM_WIN64' if os_name == 'win' else 'AARCH64', 'aarch64')
|
|
|
|
if target_arch == 'ia32':
|
|
if os_name in ('freebsd', 'openbsd'):
|
|
return ('X86_FREEBSD', 'x86')
|
|
if os_name in ('ios', 'mac'):
|
|
return ('X86_DARWIN', 'x86')
|
|
if os_name == 'win':
|
|
return ('X86_WIN32', 'x86')
|
|
return ('X86', 'x86')
|
|
|
|
if target_arch == 'x64':
|
|
return ('X86_WIN64' if os_name == 'win' else 'X86_64', 'x86')
|
|
|
|
if target_arch == 'riscv64':
|
|
return ('RISCV', 'riscv')
|
|
|
|
if target_arch == 'loong64':
|
|
return ('LOONGARCH64', 'loongarch')
|
|
|
|
if target_arch in ('mips', 'mipsel', 'mips64el'):
|
|
if os_name in ('freebsd', 'linux', 'openbsd'):
|
|
return ('MIPS', 'mips')
|
|
|
|
if target_arch == 'ppc64':
|
|
if os_name == 'aix':
|
|
return ('POWERPC_AIX', 'powerpc')
|
|
if os_name == 'mac':
|
|
return ('POWERPC_DARWIN', 'powerpc')
|
|
if os_name in ('freebsd', 'openbsd'):
|
|
return ('POWERPC_FREEBSD', 'powerpc')
|
|
if os_name == 'linux':
|
|
return ('POWERPC', 'powerpc')
|
|
|
|
raise ValueError(f'Unsupported libffi target {os_name}/{target_arch}.')
|
|
|
|
|
|
def has_long_double(os_name, target_arch):
|
|
target_arch = normalize_arch(target_arch)
|
|
|
|
if os_name == 'win':
|
|
return '0'
|
|
|
|
if os_name == 'mac' and target_arch == 'arm64':
|
|
return '0'
|
|
|
|
if target_arch == 'arm':
|
|
return '0'
|
|
|
|
return '1'
|
|
|
|
|
|
def uses_exec_trampoline_table(os_name, target_arch):
|
|
target_arch = normalize_arch(target_arch)
|
|
return os_name == 'mac' and target_arch in ('arm', 'arm64')
|
|
|
|
|
|
def render_ffi_header(base_dir, os_name, target_arch, target):
|
|
template = (base_dir / 'include' / 'ffi.h.in').read_text(encoding='utf-8')
|
|
replacements = {
|
|
'@FFI_EXEC_TRAMPOLINE_TABLE@':
|
|
'1' if uses_exec_trampoline_table(os_name, target_arch) else '0',
|
|
'@FFI_VERSION_NUMBER@': LIBFFI_VERSION_NUMBER,
|
|
'@FFI_VERSION_STRING@': LIBFFI_VERSION,
|
|
'@HAVE_LONG_DOUBLE@': has_long_double(os_name, target_arch),
|
|
'@TARGET@': target,
|
|
'@VERSION@': LIBFFI_VERSION,
|
|
}
|
|
|
|
for source, replacement in replacements.items():
|
|
template = template.replace(source, replacement)
|
|
|
|
return template
|
|
|
|
|
|
def render_fficonfig(os_name, target_arch):
|
|
target_arch = normalize_arch(target_arch)
|
|
lines = [
|
|
'/* Auto-generated by generate-headers.py. */',
|
|
'#ifndef LIBFFI_FFICONFIG_H_',
|
|
'#define LIBFFI_FFICONFIG_H_',
|
|
'',
|
|
'#define HAVE_ALLOCA 1',
|
|
'#define HAVE_MEMCPY 1',
|
|
'#define HAVE_MEMORY_H 1',
|
|
'#define HAVE_STDINT_H 1',
|
|
'#define HAVE_STRING_H 1',
|
|
'#define STDC_HEADERS 1',
|
|
]
|
|
|
|
if os_name != 'win':
|
|
lines.extend([
|
|
'#define HAVE_AS_CFI_PSEUDO_OP 1',
|
|
'#define HAVE_HIDDEN_VISIBILITY_ATTRIBUTE 1',
|
|
'#define HAVE_MKOSTEMP 1',
|
|
'#define HAVE_RO_EH_FRAME 1',
|
|
'#define EH_FRAME_FLAGS "a"',
|
|
])
|
|
|
|
if target_arch in ('ia32', 'x64') and os_name != 'win':
|
|
lines.append('#define HAVE_AS_X86_PCREL 1')
|
|
|
|
if uses_exec_trampoline_table(os_name, target_arch):
|
|
lines.append('#define FFI_EXEC_TRAMPOLINE_TABLE 1')
|
|
elif os_name in ('freebsd', 'mac', 'openbsd'):
|
|
lines.append('#define FFI_MMAP_EXEC_WRIT 1')
|
|
|
|
if os_name != 'win':
|
|
lines.extend([
|
|
'#define HAVE_DLFCN_H 1',
|
|
'#define HAVE_MMAP 1',
|
|
'#define HAVE_MMAP_ANON 1',
|
|
'#define HAVE_MMAP_FILE 1',
|
|
'#define HAVE_SYS_MMAN_H 1',
|
|
'#define HAVE_UNISTD_H 1',
|
|
])
|
|
|
|
if os_name in ('freebsd', 'ios', 'mac', 'openbsd'):
|
|
lines.extend([
|
|
'#define HAVE_MMAP_DEV_ZERO 1',
|
|
])
|
|
|
|
if os_name == 'mac' and target_arch == 'arm64':
|
|
lines.extend([
|
|
'#if defined(__arm64e__)',
|
|
'#define HAVE_ARM64E_PTRAUTH 1',
|
|
'#endif',
|
|
])
|
|
|
|
lines.extend([
|
|
'',
|
|
'#ifdef HAVE_HIDDEN_VISIBILITY_ATTRIBUTE',
|
|
'#ifdef LIBFFI_ASM',
|
|
'#ifdef __APPLE__',
|
|
'#define FFI_HIDDEN(name) .private_extern name',
|
|
'#else',
|
|
'#define FFI_HIDDEN(name) .hidden name',
|
|
'#endif',
|
|
'#else',
|
|
'#define FFI_HIDDEN __attribute__ ((visibility ("hidden")))',
|
|
'#endif',
|
|
'#else',
|
|
'#ifdef LIBFFI_ASM',
|
|
'#define FFI_HIDDEN(name)',
|
|
'#else',
|
|
'#define FFI_HIDDEN',
|
|
'#endif',
|
|
'#endif',
|
|
'',
|
|
'#endif /* LIBFFI_FFICONFIG_H_ */',
|
|
'',
|
|
])
|
|
|
|
return '\n'.join(lines)
|
|
|
|
|
|
def generate_headers(output_dir, target_arch, os_name):
|
|
base_dir = Path(__file__).resolve().parent
|
|
output_dir = Path(str(output_dir).strip().strip('"'))
|
|
output_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
target, target_dir = get_target(os_name, target_arch)
|
|
ffitarget_src = base_dir / 'src' / target_dir / 'ffitarget.h'
|
|
if not ffitarget_src.exists():
|
|
raise FileNotFoundError(f'Missing libffi target header: {ffitarget_src}')
|
|
|
|
(output_dir / 'ffi.h').write_text(
|
|
render_ffi_header(base_dir, os_name, target_arch, target),
|
|
encoding='utf-8')
|
|
(output_dir / 'fficonfig.h').write_text(
|
|
render_fficonfig(os_name, target_arch),
|
|
encoding='utf-8')
|
|
(output_dir / 'ffitarget.h').write_text(
|
|
ffitarget_src.read_text(encoding='utf-8'),
|
|
encoding='utf-8')
|
|
|
|
|
|
def detect_os_name():
|
|
if sys.platform.startswith('win'):
|
|
return 'win'
|
|
if sys.platform == 'darwin':
|
|
return 'mac'
|
|
if sys.platform.startswith('linux'):
|
|
return 'linux'
|
|
if sys.platform.startswith('freebsd'):
|
|
return 'freebsd'
|
|
raise ValueError(f'Unsupported host platform {sys.platform!r}')
|
|
|
|
|
|
def detect_target_arch():
|
|
candidates = [
|
|
os.environ.get('TARGET_ARCH'),
|
|
os.environ.get('npm_config_arch'),
|
|
os.environ.get('VSCMD_ARG_TGT_ARCH'),
|
|
os.environ.get('Platform'),
|
|
os.environ.get('PROCESSOR_ARCHITECTURE'),
|
|
platform.machine(),
|
|
]
|
|
|
|
aliases = {
|
|
'amd64': 'x64',
|
|
'x86_64': 'x64',
|
|
'x64': 'x64',
|
|
'win32': 'x64',
|
|
'i386': 'ia32',
|
|
'i686': 'ia32',
|
|
'ia32': 'ia32',
|
|
'x86': 'ia32',
|
|
'arm64': 'arm64',
|
|
'aarch64': 'arm64',
|
|
'arm': 'arm',
|
|
'riscv64': 'riscv64',
|
|
'loong64': 'loong64',
|
|
'ppc64': 'ppc64',
|
|
'mips': 'mips',
|
|
'mipsel': 'mipsel',
|
|
'mips64el': 'mips64el',
|
|
}
|
|
|
|
for candidate in candidates:
|
|
if not candidate:
|
|
continue
|
|
normalized = aliases.get(candidate.lower())
|
|
if normalized is not None:
|
|
return normalized
|
|
|
|
raise ValueError('Unable to determine target architecture for libffi headers')
|
|
|
|
|
|
def main(argv=None):
|
|
parser = argparse.ArgumentParser(description='Generate libffi headers')
|
|
parser.add_argument('--output-dir', required=True)
|
|
parser.add_argument('--target-arch')
|
|
parser.add_argument('--os')
|
|
args = parser.parse_args(argv)
|
|
|
|
try:
|
|
generate_headers(args.output_dir,
|
|
args.target_arch or detect_target_arch(),
|
|
args.os or detect_os_name())
|
|
except Exception as exc: # pylint: disable=broad-except
|
|
print(exc, file=sys.stderr)
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|