class AsanMapFileBuilder(base_builders.Builder): name: str = 'asan-mapfile' config_list: List[configs.Config] = configs.android_configs() def _build_config(self) -> None: arch = self._config.target_arch # We can not build asan_test using current CMake building system. Since # those files are not used to build AOSP, we just simply touch them so that # we can pass the build checks. asan_test_path = self.output_toolchain.path / 'test' / arch.llvm_arch / 'bin' asan_test_path.mkdir(parents=True, exist_ok=True) asan_test_bin_path = asan_test_path / 'asan_test' asan_test_bin_path.touch(exist_ok=True) lib_dir = self.output_toolchain.resource_dir self._build_sanitizer_map_file('asan', arch, lib_dir) self._build_sanitizer_map_file('ubsan_standalone', arch, lib_dir) if arch == hosts.Arch.AARCH64: self._build_sanitizer_map_file('hwasan', arch, lib_dir) @staticmethod def _build_sanitizer_map_file(san: str, arch: hosts.Arch, lib_dir: Path) -> None: lib_file = lib_dir / f'libclang_rt.{san}-{arch.llvm_arch}-android.so' map_file = lib_dir / f'libclang_rt.{san}-{arch.llvm_arch}-android.map.txt' mapfile.create_map_file(lib_file, map_file)
class LibOMPBuilder(base_builders.LLVMRuntimeBuilder): name: str = 'libomp' src_dir: Path = paths.LLVM_PATH / 'openmp' config_list: List[configs.Config] = ( configs.android_configs(platform=True, extra_config={'is_shared': False}) + configs.android_configs(platform=False, extra_config={'is_shared': False}) + configs.android_configs(platform=False, extra_config={'is_shared': True})) @property def is_shared(self) -> bool: return cast(Dict[str, bool], self._config.extra_config)['is_shared'] @property def output_dir(self) -> Path: old_path = super().output_dir suffix = '-shared' if self.is_shared else '-static' return old_path.parent / (old_path.name + suffix) @property def cmake_defines(self) -> Dict[str, str]: defines = super().cmake_defines defines['OPENMP_ENABLE_LIBOMPTARGET'] = 'FALSE' defines['OPENMP_ENABLE_OMPT_TOOLS'] = 'FALSE' defines['LIBOMP_ENABLE_SHARED'] = 'TRUE' if self.is_shared else 'FALSE' # Some compiler-rt math builtins depend on libm, so link against it. # TODO: Try to break the builtins->libm dependency (llvm.org/PR32279). defines['LIBOMP_LIBFLAGS'] = '-lm' # Minimum version for OpenMP's CMake is too low for the CMP0056 policy # to be ON by default. defines['CMAKE_POLICY_DEFAULT_CMP0056'] = 'NEW' return defines def install_config(self) -> None: # We need to install libomp manually. libname = 'libomp.' + ('so' if self.is_shared else 'a') src_lib = self.output_dir / 'runtime' / 'src' / libname dst_dir = self.install_dir dst_dir.mkdir(parents=True, exist_ok=True) shutil.copy2(src_lib, dst_dir / libname)
def config_list(self) -> List[configs.Config]: result = configs.android_configs(platform=False, extra_config={'is_exported': False}) # For arm32 and x86, build a special version of the builtins library # where the symbols are exported, not hidden. This version is needed # to continue exporting builtins from libc.so and libm.so. for arch in [configs.AndroidARMConfig(), configs.AndroidI386Config()]: arch.platform = False arch.extra_config = {'is_exported': True} result.append(arch) return result
class LldbServerBuilder(base_builders.LLVMRuntimeBuilder): name: str = 'lldb-server' src_dir: Path = paths.LLVM_PATH / 'llvm' config_list: List[configs.Config] = configs.android_configs(platform=False, static=True) ninja_targets: List[str] = ['lldb-server'] @property def cflags(self) -> List[str]: cflags: List[str] = super().cflags # The build system will add '-stdlib=libc++' automatically. Since we # have -nostdinc++ here, -stdlib is useless. Adds a flag to avoid the # warnings. cflags.append('-Wno-unused-command-line-argument') return cflags @property def ldflags(self) -> List[str]: # Currently, -rtlib=compiler-rt (even with -unwindlib=libunwind) does # not automatically link libunwind.a on Android. return super().ldflags + ['-lunwind'] @property def _llvm_target(self) -> str: return { hosts.Arch.ARM: 'ARM', hosts.Arch.AARCH64: 'AArch64', hosts.Arch.I386: 'X86', hosts.Arch.X86_64: 'X86', }[self._config.target_arch] @property def cmake_defines(self) -> Dict[str, str]: defines = super().cmake_defines # lldb depends on support libraries. defines['LLVM_ENABLE_PROJECTS'] = 'clang;lldb' defines['LLVM_TARGETS_TO_BUILD'] = self._llvm_target defines['LLVM_TABLEGEN'] = str(self.toolchain.build_path / 'bin' / 'llvm-tblgen') defines['CLANG_TABLEGEN'] = str(self.toolchain.build_path / 'bin' / 'clang-tblgen') defines['LLDB_TABLEGEN'] = str(self.toolchain.build_path / 'bin' / 'lldb-tblgen') triple = self._config.target_arch.llvm_triple defines['LLVM_HOST_TRIPLE'] = triple.replace('i686', 'i386') return defines def install_config(self) -> None: src_path = self.output_dir / 'bin' / 'lldb-server' install_dir = self.install_dir install_dir.mkdir(parents=True, exist_ok=True) shutil.copy2(src_path, install_dir)
class PlatformLibcxxAbiBuilder(base_builders.LLVMRuntimeBuilder): name = 'platform-libcxxabi' src_dir: Path = paths.LLVM_PATH / 'libcxxabi' config_list: List[configs.Config] = configs.android_configs( platform=True, suppress_libcxx_headers=True) @property def cmake_defines(self) -> Dict[str, str]: defines: Dict[str, str] = super().cmake_defines defines['LIBCXXABI_LIBCXX_INCLUDES'] = str(paths.LLVM_PATH / 'libcxx' / 'include') defines['LIBCXXABI_ENABLE_SHARED'] = 'OFF' return defines def _is_64bit(self) -> bool: return self._config.target_arch in (hosts.Arch.AARCH64, hosts.Arch.X86_64) def _build_config(self) -> None: if self._is_64bit(): # For arm64 and x86_64, build static cxxabi library from # toolchain/libcxxabi and use it when building runtimes. This # should affect all compiler-rt runtimes that use libcxxabi # (e.g. asan, hwasan, scudo, tsan, ubsan, xray). super()._build_config() else: self.install_config() def install_config(self) -> None: arch = self._config.target_arch lib_name = 'lib64' if arch == hosts.Arch.X86_64 else 'lib' install_dir = self._config.sysroot / 'usr' / lib_name if self._is_64bit(): src_path = self.output_dir / 'lib64' / 'libc++abi.a' shutil.copy2(src_path, install_dir / 'libc++abi.a') else: with (install_dir / 'libc++abi.so').open('w') as f: f.write('INPUT(-lc++)')
class SysrootsBuilder(base_builders.Builder): name: str = 'sysroots' config_list: List[configs.Config] = ( configs.android_configs(platform=True) + configs.android_configs(platform=False)) def _build_config(self) -> None: config: configs.AndroidConfig = cast(configs.AndroidConfig, self._config) arch = config.target_arch platform = config.platform sysroot = config.sysroot if sysroot.exists(): shutil.rmtree(sysroot) sysroot.mkdir(parents=True, exist_ok=True) # Copy the NDK prebuilt's sysroot, but for the platform variant, omit # the STL and android_support headers and libraries. src_sysroot = paths.NDK_BASE / 'toolchains' / 'llvm' / 'prebuilt' / 'linux-x86_64' / 'sysroot' # Copy over usr/include. shutil.copytree(src_sysroot / 'usr' / 'include', sysroot / 'usr' / 'include', symlinks=True) if platform: # Remove the STL headers. shutil.rmtree(sysroot / 'usr' / 'include' / 'c++') else: # Add the android_support headers from usr/local/include. shutil.copytree(src_sysroot / 'usr' / 'local' / 'include', sysroot / 'usr' / 'local' / 'include', symlinks=True) # Copy over usr/lib/$TRIPLE. src_lib = src_sysroot / 'usr' / 'lib' / arch.ndk_triple dest_lib = sysroot / 'usr' / 'lib' / arch.ndk_triple shutil.copytree(src_lib, dest_lib, symlinks=True) # Remove the NDK r20's old libcompiler_rt-extras and libunwind. (In the # future, libunwind.a will be located in the toolchain resource # directory along with libclang_rt.*.a, not in the sysroot directory.) # For the platform, also remove the NDK libc++. (dest_lib / 'libcompiler_rt-extras.a').unlink() if arch == hosts.Arch.ARM: (dest_lib / 'libunwind.a').unlink() if platform: (dest_lib / 'libc++abi.a').unlink() (dest_lib / 'libc++_static.a').unlink() (dest_lib / 'libc++_shared.so').unlink() # Each per-API-level directory has libc++.so, libc++.a, and libcompiler_rt-extras.a. for subdir in dest_lib.iterdir(): if subdir.is_symlink() or not subdir.is_dir(): continue if not re.match(r'\d+$', subdir.name): continue (subdir / 'libcompiler_rt-extras.a').unlink() if platform: (subdir / 'libc++.a').unlink() (subdir / 'libc++.so').unlink() # Verify that there aren't any extra copies somewhere else in the # directory hierarchy. verify_gone = ['libcompiler_rt-extras.a', 'libunwind.a'] if platform: verify_gone += [ 'libc++abi.a', 'libc++_static.a', 'libc++_shared.so', 'libc++.a', 'libc++.so', ] for (parent, _, files) in os.walk(sysroot): for f in files: if f in verify_gone: raise RuntimeError('sysroot file should have been ' + f'removed: {os.path.join(parent, f)}') if not platform and arch in [hosts.Arch.ARM, hosts.Arch.I386]: # HACK: The arm32 libunwind uses dl_unwind_find_exidx rather than # __gnu_Unwind_Find_exidx. However, libc.a only provides the latter # until NDK r22. Until this build system upgrades to NDK r22, # replace libc.a(exidx_static.o) with an upgraded copy. # # HACK: The x86 libc.a from NDK r20 needs __x86.get_pc_thunk.cx from # libgcc.a. This incorrect dependency will be fixed in NDK r22's # libc.a. The workaround here might result in a mislinked # lldb-server that crashes, but instead, lldb-server seems to be OK. # See https://bugs.llvm.org/show_bug.cgi?id=45594. if constants.NDK_VERSION >= 'r22': raise RuntimeError( 'libc.a patching should be removed with r22 prebuilt: ' f'NDK_VERSION={constants.NDK_VERSION}') patch_dir = paths.OUT_DIR / 'ndk_libc_patch' patch_dir.mkdir(parents=True, exist_ok=True) if arch == hosts.Arch.ARM: patch_src = (paths.ANDROID_DIR / 'bionic' / 'libc' / 'arch-arm' / 'bionic' / 'exidx_static.c') patch_name = 'exidx_static' else: patch_src = (paths.ANDROID_DIR / 'bionic' / 'libc' / 'arch-x86' / 'bionic' / '__x86.get_pc_thunk.S') patch_name = '__x86.get_pc_thunk' patch_obj = patch_dir / f'{patch_name}.o' libc_archive = (sysroot / 'usr' / 'lib' / arch.ndk_triple / '29' / 'libc.a') utils.check_call([ self.toolchain.cc, f'--sysroot={sysroot}', '-c', f'--target={arch.llvm_triple}', f'-o{patch_obj}', patch_src ]) utils.check_call([ self.toolchain.path / 'bin' / 'llvm-ar', 'rcs', libc_archive, patch_obj ]) if platform: # Create a stub library for the platform's libc++. platform_stubs = paths.OUT_DIR / 'platform_stubs' / arch.ndk_arch platform_stubs.mkdir(parents=True, exist_ok=True) libdir = sysroot / 'usr' / ('lib64' if arch == hosts.Arch.X86_64 else 'lib') libdir.mkdir(parents=True, exist_ok=True) with (platform_stubs / 'libc++.c').open('w') as f: f.write( textwrap.dedent("""\ void __cxa_atexit() {} void __cxa_demangle() {} void __cxa_finalize() {} void __dynamic_cast() {} void _ZTIN10__cxxabiv117__class_type_infoE() {} void _ZTIN10__cxxabiv120__si_class_type_infoE() {} void _ZTIN10__cxxabiv121__vmi_class_type_infoE() {} void _ZTISt9type_info() {} """)) utils.check_call([ self.toolchain.cc, f'--target={arch.llvm_triple}', '-fuse-ld=lld', '-nostdlib', '-shared', '-Wl,-soname,libc++.so', '-o{}'.format(libdir / 'libc++.so'), str(platform_stubs / 'libc++.c') ])
class LibUnwindBuilder(base_builders.LLVMRuntimeBuilder): name: str = 'libunwind' src_dir: Path = paths.LLVM_PATH / 'libunwind' # Build two copies of the builtins library: # - A copy targeting the NDK with hidden symbols. # - A copy targeting the platform with exported symbols. # Bionic's libc.so exports the unwinder, so it needs a copy with exported # symbols. Everything else uses the NDK copy. config_list: List[configs.Config] = ( configs.android_configs(platform=True) + configs.android_configs(platform=False)) @property def is_exported(self) -> bool: return self._config.platform @property def output_dir(self) -> Path: old_path = super().output_dir suffix = '-exported' if self.is_exported else '-hermetic' return old_path.parent / (old_path.name + suffix) @property def cflags(self) -> List[str]: return super().cflags + ['-D_LIBUNWIND_USE_DLADDR=0'] @property def ldflags(self) -> List[str]: # This flag is currently unnecessary but will become necessary if the # default -unwindlib changes to libunwind. libunwind.a doesn't exist # when libunwind is built, and libunwind can't use # CMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY because # LIBUNWIND_HAS_PTHREAD_LIB must be set to false. return super().ldflags + ['-unwindlib=none'] @property def cmake_defines(self) -> Dict[str, str]: defines = super().cmake_defines defines[ 'LIBUNWIND_HERMETIC_STATIC_LIBRARY'] = 'TRUE' if not self.is_exported else 'FALSE' defines['LIBUNWIND_ENABLE_SHARED'] = 'FALSE' # TODO: Enable the FrameHeaderCache, for the platform only (not the # NDK), after (a) upgrading libunwind to a version with this config # setting and (b) upgrading the prebuilt NDK to r21 (which adds # dlpi_adds/dlpi_subs). return defines def install_config(self) -> None: # We need to install libunwind manually. src_path = self.output_dir / 'lib64' / 'libunwind.a' arch = self._config.target_arch out_res_dir = self.output_toolchain.resource_dir / arch.value out_res_dir.mkdir(parents=True, exist_ok=True) if self.is_exported: # This special copy exports its symbols and is only intended for use # in Bionic's libc.so. shutil.copy2(src_path, out_res_dir / 'libunwind-exported.a') else: shutil.copy2(src_path, out_res_dir / 'libunwind.a') # Also install to self.toolchain.resource_dir, if it's different, for # use when building runtimes. if self.toolchain.resource_dir != self.output_toolchain.resource_dir: res_dir = self.toolchain.resource_dir / arch.value res_dir.mkdir(parents=True, exist_ok=True) shutil.copy2(src_path, res_dir / 'libunwind.a') # Make a copy for the NDK. ndk_dir = self.output_toolchain.path / 'runtimes_ndk_cxx' / arch.value ndk_dir.mkdir(parents=True, exist_ok=True) shutil.copy2(src_path, ndk_dir / 'libunwind.a')
class CompilerRTBuilder(base_builders.LLVMRuntimeBuilder): name: str = 'compiler-rt' src_dir: Path = paths.LLVM_PATH / 'compiler-rt' config_list: List[configs.Config] = ( configs.android_configs(platform=True) + configs.android_configs(platform=False)) @property def install_dir(self) -> Path: if self._config.platform: return self.output_toolchain.clang_lib_dir # Installs to a temporary dir and copies to runtimes_ndk_cxx manually. output_dir = self.output_dir return output_dir.parent / (output_dir.name + '-install') @property def cmake_defines(self) -> Dict[str, str]: defines = super().cmake_defines arch = self._config.target_arch defines['COMPILER_RT_BUILD_BUILTINS'] = 'OFF' defines['COMPILER_RT_USE_BUILTINS_LIBRARY'] = 'ON' # FIXME: Disable WError build until upstream fixed the compiler-rt # personality routine warnings caused by r309226. # defines['COMPILER_RT_ENABLE_WERROR'] = 'ON' defines['COMPILER_RT_TEST_COMPILER_CFLAGS'] = defines['CMAKE_C_FLAGS'] defines['COMPILER_RT_DEFAULT_TARGET_TRIPLE'] = arch.llvm_triple defines['COMPILER_RT_INCLUDE_TESTS'] = 'OFF' defines['SANITIZER_CXX_ABI'] = 'libcxxabi' # With CMAKE_SYSTEM_NAME='Android', compiler-rt will be installed to # lib/android instead of lib/linux. del defines['CMAKE_SYSTEM_NAME'] libs: List[str] = [] if arch == 'arm': libs += ['-latomic'] if self._config.api_level < 21: libs += ['-landroid_support'] # Currently, -rtlib=compiler-rt (even with -unwindlib=libunwind) does # not automatically link libunwind.a on Android. libs += ['-lunwind'] defines['SANITIZER_COMMON_LINK_LIBS'] = ' '.join(libs) if self._config.platform: defines['COMPILER_RT_HWASAN_WITH_INTERCEPTORS'] = 'OFF' return defines @property def cflags(self) -> List[str]: cflags = super().cflags cflags.append('-funwind-tables') return cflags def install_config(self) -> None: # Still run `ninja install`. super().install_config() # Install the fuzzer library to the old {arch}/libFuzzer.a path for # backwards compatibility. arch = self._config.target_arch sarch = 'i686' if arch == hosts.Arch.I386 else arch.value static_lib_filename = 'libclang_rt.fuzzer-' + sarch + '-android.a' lib_dir = self.install_dir / 'lib' / 'linux' arch_dir = lib_dir / arch.value arch_dir.mkdir(parents=True, exist_ok=True) shutil.copy2(lib_dir / static_lib_filename, arch_dir / 'libFuzzer.a') if not self._config.platform: dst_dir = self.output_toolchain.path / 'runtimes_ndk_cxx' shutil.copytree(lib_dir, dst_dir, dirs_exist_ok=True) def install(self) -> None: # Install libfuzzer headers once for all configs. header_src = self.src_dir / 'lib' / 'fuzzer' header_dst = self.output_toolchain.path / 'prebuilt_include' / 'llvm' / 'lib' / 'Fuzzer' header_dst.mkdir(parents=True, exist_ok=True) for f in header_src.iterdir(): if f.suffix in ('.h', '.def'): shutil.copy2(f, header_dst) symlink_path = self.output_toolchain.resource_dir / 'libclang_rt.hwasan_static-aarch64-android.a' symlink_path.unlink(missing_ok=True) os.symlink('libclang_rt.hwasan-aarch64-android.a', symlink_path)
class SysrootsBuilder(base_builders.Builder): name: str = 'sysroots' config_list: List[configs.Config] = ( configs.android_configs(platform=True) + configs.android_configs(platform=False) ) @property def toolchain(self) -> toolchains.Toolchain: return toolchains.get_runtime_toolchain() def _build_config(self) -> None: config: configs.AndroidConfig = cast(configs.AndroidConfig, self._config) arch = config.target_arch platform = config.platform sysroot = config.sysroot if sysroot.exists(): shutil.rmtree(sysroot) sysroot.mkdir(parents=True, exist_ok=True) base_header_path = paths.NDK_BASE / 'sysroot' / 'usr' / 'include' base_lib_path = paths.NDK_BASE / 'platforms' / f'android-{config.api_level}' dest_usr = sysroot / 'usr' # Copy over usr/include. dest_usr_include = dest_usr / 'include' shutil.copytree(base_header_path, dest_usr_include, symlinks=True) # Copy over usr/include/asm. asm_headers = base_header_path / arch.ndk_triple / 'asm' dest_usr_include_asm = dest_usr_include / 'asm' shutil.copytree(asm_headers, dest_usr_include_asm, symlinks=True) # Copy over usr/lib. arch_lib_path = base_lib_path / f'arch-{arch.ndk_arch}' / 'usr' / 'lib' dest_usr_lib = dest_usr / 'lib' shutil.copytree(arch_lib_path, dest_usr_lib, symlinks=True) # For only x86_64, we also need to copy over usr/lib64 if arch == hosts.Arch.X86_64: arch_lib64_path = base_lib_path / f'arch-{arch.ndk_arch}' / 'usr' / 'lib64' dest_usr_lib64 = dest_usr / 'lib64' shutil.copytree(arch_lib64_path, dest_usr_lib64, symlinks=True) if platform: # Create a stub library for the platform's libc++. platform_stubs = paths.OUT_DIR / 'platform_stubs' / arch.ndk_arch platform_stubs.mkdir(parents=True, exist_ok=True) libdir = dest_usr_lib64 if arch == hosts.Arch.X86_64 else dest_usr_lib with (platform_stubs / 'libc++.c').open('w') as f: f.write(textwrap.dedent("""\ void __cxa_atexit() {} void __cxa_demangle() {} void __cxa_finalize() {} void __dynamic_cast() {} void _ZTIN10__cxxabiv117__class_type_infoE() {} void _ZTIN10__cxxabiv120__si_class_type_infoE() {} void _ZTIN10__cxxabiv121__vmi_class_type_infoE() {} void _ZTISt9type_info() {} """)) utils.check_call([self.toolchain.cc, f'--target={arch.llvm_triple}', '-fuse-ld=lld', '-nostdlib', '-shared', '-Wl,-soname,libc++.so', '-o{}'.format(libdir / 'libc++.so'), str(platform_stubs / 'libc++.c')]) # For arm64 and x86_64, build static cxxabi library from # toolchain/libcxxabi and use it when building runtimes. This # should affect all compiler-rt runtimes that use libcxxabi # (e.g. asan, hwasan, scudo, tsan, ubsan, xray). if arch not in (hosts.Arch.AARCH64, hosts.Arch.X86_64): with (libdir / 'libc++abi.so').open('w') as f: f.write('INPUT(-lc++)') else: # We can build libcxxabi only after the sysroots are # created. Build it for the current arch and copy it to # <libdir>. out_dir = build_libcxxabi(self.toolchain, arch) out_path = out_dir / 'lib64' / 'libc++abi.a' shutil.copy2(out_path, libdir)